Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redelegate without unbonding #2440

Merged
merged 23 commits into from
Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
deca075
2345: Add `new_validator_public_key` field to unbonding purse
darthsiroftardis Nov 16, 2021
8204d62
2345:Write test for upgrade of unbonding purses
darthsiroftardis Nov 19, 2021
ca9640c
Add type WithdrawPurse
darthsiroftardis Dec 1, 2021
55446e4
2345: Add test for redelegation to inactive bids
darthsiroftardis Dec 3, 2021
7e0ca49
Merge remote-tracking branch 'upstream/dev' into Redelegate
darthsiroftardis Dec 3, 2021
48b8f3c
2345: Add changes to for tests
darthsiroftardis Dec 3, 2021
e4aeeea
2345: Update Assembly script undelegate contract
darthsiroftardis Dec 4, 2021
ad0ea8a
2345: PR prep
darthsiroftardis Dec 6, 2021
bc96a57
Merge remote-tracking branch 'upstream/dev' into Redelegate
darthsiroftardis Dec 6, 2021
b0024fb
2345: Fix validation test failure
darthsiroftardis Dec 6, 2021
7a179c7
2345: Fix global state for redelegation test (1/2)
darthsiroftardis Dec 6, 2021
0f787f8
2345: Fix global state for redelegation test (2/2)
darthsiroftardis Dec 6, 2021
405f100
2345: Amend nctl-auction-undelegate
darthsiroftardis Dec 7, 2021
07d3f19
2345: Change do_delegate_withdraw.sh to use null
darthsiroftardis Dec 7, 2021
b12b941
2345: Address PR feedback pt.1
darthsiroftardis Dec 13, 2021
2cdcc21
2345: Address PR feedback pt. 2
darthsiroftardis Dec 15, 2021
ce0f523
Merge remote-tracking branch 'upstream/dev' into Redelegate
darthsiroftardis Dec 23, 2021
5009e96
2313: Add docstring for `Step` event.
darthsiroftardis Jan 5, 2022
7feb419
2313: Fix test for SSE schema
darthsiroftardis Jan 7, 2022
052cca9
Merge remote-tracking branch 'upstream/dev' into Redelegate
darthsiroftardis Jan 10, 2022
0d11edb
2313: Fix test for bond and clippy warning
darthsiroftardis Jan 10, 2022
09c57bf
Address PR feedback pt. 3
darthsiroftardis Jan 12, 2022
8bcd0a7
Address PR feedback pt. 4
darthsiroftardis Jan 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion execution_engine/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ All notable changes to this project will be documented in this file. The format
## [Unreleased]

### Changed

* Undelegate now takes an optional `new_validator` argument which will re-delegate to a validator without unbonding.
* (Perf) Changed contract runtime to allow caching GlobalState changes during execution of a single block.


Expand Down
15 changes: 15 additions & 0 deletions execution_engine/src/core/engine_state/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,21 @@ pub enum Error {
/// An attempt to push to the runtime stack while already at the maximum height.
#[error("Runtime stack overflow")]
RuntimeStackOverflow,
/// Failed to get the set of Key::Withdraw from global state.
#[error("Failed to get withdraw keys")]
FailedToGetWithdrawKeys,
/// Failed to get the purses stored under Key::Withdraw
#[error("Failed to get stored values under withdraws")]
FailedToGetStoredWithdraws,
/// Failed to convert the StoredValue into WithdrawPurse.
#[error("Failed to convert the stored value to a withdraw purse")]
FailedToGetWithdrawPurses,
/// Failed to retrieve the unbonding delay from the auction state.
#[error("Failed to retrieve the unbonding delay from the auction state")]
FailedToRetrieveUnbondingDelay,
/// Failed to retrieve the current EraId from the auction state.
#[error("Failed to retrieve the era_id from the auction state")]
FailedToRetrieveEraId,
}

impl Error {
Expand Down
82 changes: 78 additions & 4 deletions execution_engine/src/core/engine_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ use casper_types::{
contracts::NamedKeys,
system::{
auction::{
EraValidators, ARG_ERA_END_TIMESTAMP_MILLIS, ARG_EVICTED_VALIDATORS,
ARG_REWARD_FACTORS, ARG_VALIDATOR_PUBLIC_KEYS, AUCTION_DELAY_KEY,
EraValidators, UnbondingPurse, ARG_ERA_END_TIMESTAMP_MILLIS, ARG_EVICTED_VALIDATORS,
ARG_REWARD_FACTORS, ARG_VALIDATOR_PUBLIC_KEYS, AUCTION_DELAY_KEY, ERA_ID_KEY,
LOCKED_FUNDS_PERIOD_KEY, UNBONDING_DELAY_KEY, VALIDATOR_SLOTS_KEY,
},
handle_payment,
mint::{self, ROUND_SEIGNIORAGE_RATE_KEY},
CallStackElement, AUCTION, HANDLE_PAYMENT, MINT, STANDARD_PAYMENT,
},
AccessRights, ApiError, BlockTime, CLValue, Contract, ContractHash, DeployHash, DeployInfo,
Gas, Key, KeyTag, Motes, Phase, ProtocolVersion, PublicKey, RuntimeArgs, StoredValue, URef,
U512,
EraId, Gas, Key, KeyTag, Motes, Phase, ProtocolVersion, PublicKey, RuntimeArgs, StoredValue,
URef, U512,
};

pub use self::{
Expand Down Expand Up @@ -429,6 +429,80 @@ where
tracking_copy.borrow_mut().write(*key, value.clone());
}

// This is a one time data transformation which will be removed
// in a following upgrade.
// TODO: CRef={https://github.com/casper-network/casper-node/issues/2479}
{
darthsiroftardis marked this conversation as resolved.
Show resolved Hide resolved
let withdraw_keys = tracking_copy
.borrow_mut()
.get_keys(correlation_id, &KeyTag::Withdraw)
.map_err(|_| Error::FailedToGetWithdrawKeys)?;

let (unbonding_delay, current_era_id) = {
let auction_contract = tracking_copy
.borrow_mut()
.get_contract(correlation_id, *auction_hash)?;

let unbonding_delay_key = auction_contract.named_keys()[UNBONDING_DELAY_KEY];
let delay = tracking_copy
.borrow_mut()
.read(correlation_id, &unbonding_delay_key)
.map_err(|error| error.into())?
.ok_or(Error::FailedToRetrieveUnbondingDelay)?
.as_cl_value()
.ok_or_else(|| Error::Bytesrepr("unbonding_delay".to_string()))?
.clone()
.into_t::<u64>()
.map_err(execution::Error::from)?;

let era_id_key = auction_contract.named_keys()[ERA_ID_KEY];

let era_id = tracking_copy
.borrow_mut()
.read(correlation_id, &era_id_key)
.map_err(|error| error.into())?
.ok_or(Error::FailedToRetrieveEraId)?
.as_cl_value()
.ok_or_else(|| Error::Bytesrepr("era_id".to_string()))?
.clone()
.into_t::<EraId>()
.map_err(execution::Error::from)?;

(delay, era_id)
};

for key in withdraw_keys {
// Transform only those withdraw purses that are still to be
// processed in the unbonding queue.
let withdraw_purses = tracking_copy
.borrow_mut()
.read(correlation_id, &key)
.map_err(|_| Error::FailedToGetWithdrawKeys)?
.ok_or(Error::FailedToGetStoredWithdraws)?
.as_withdraw()
.ok_or(Error::FailedToGetWithdrawPurses)?
.to_owned();

let unbonding_purses: Vec<UnbondingPurse> = withdraw_purses
.into_iter()
.filter_map(|purse| {
if purse.era_of_creation() + unbonding_delay >= current_era_id {
return Some(UnbondingPurse::from(purse));
}
None
})
.collect();

let unbonding_key = key
.withdraw_to_unbond()
.ok_or_else(|| Error::Bytesrepr("unbond".to_string()))?;

tracking_copy
.borrow_mut()
.write(unbonding_key, StoredValue::Unbonding(unbonding_purses))
}
}

let execution_effect = tracking_copy.borrow().effect();

// commit
Expand Down
76 changes: 68 additions & 8 deletions execution_engine/src/core/runtime/auction_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use casper_types::{
crypto,
system::{
auction::{
AccountProvider, Auction, Bid, EraInfo, Error, MintProvider, RuntimeProvider,
StorageProvider, UnbondingPurse,
AccountProvider, Auction, Bid, Delegator, EraInfo, Error, MintProvider,
RuntimeProvider, StorageProvider, UnbondingPurse,
},
mint, MINT,
},
Expand Down Expand Up @@ -77,9 +77,9 @@ where
.map_err(|exec_error| <Option<Error>>::from(exec_error).unwrap_or(Error::Storage))
}

fn read_withdraw(&mut self, account_hash: &AccountHash) -> Result<Vec<UnbondingPurse>, Error> {
match self.context.read_gs(&Key::Withdraw(*account_hash)) {
Ok(Some(StoredValue::Withdraw(unbonding_purses))) => Ok(unbonding_purses),
fn read_unbond(&mut self, account_hash: &AccountHash) -> Result<Vec<UnbondingPurse>, Error> {
match self.context.read_gs(&Key::Unbond(*account_hash)) {
Ok(Some(StoredValue::Unbonding(unbonding_purses))) => Ok(unbonding_purses),
Ok(Some(_)) => Err(Error::Storage),
Ok(None) => Ok(Vec::new()),
Err(execution::Error::BytesRepr(_)) => Err(Error::Serialization),
Expand All @@ -90,15 +90,15 @@ where
}
}

fn write_withdraw(
fn write_unbond(
&mut self,
account_hash: AccountHash,
unbonding_purses: Vec<UnbondingPurse>,
) -> Result<(), Error> {
self.context
.metered_write_gs_unsafe(
Key::Withdraw(account_hash),
StoredValue::Withdraw(unbonding_purses),
Key::Unbond(account_hash),
StoredValue::Unbonding(unbonding_purses),
)
.map_err(|exec_error| <Option<Error>>::from(exec_error).unwrap_or(Error::Storage))
}
Expand Down Expand Up @@ -315,6 +315,66 @@ where
self.mint_reduce_total_supply(mint_contract, amount)
.map_err(|exec_error| <Option<Error>>::from(exec_error).unwrap_or(Error::MintReward))
}

fn handle_delegation(
&mut self,
delegator_public_key: PublicKey,
validator_public_key: PublicKey,
source: URef,
amount: U512,
) -> Result<U512, Error> {
let validator_account_hash = AccountHash::from(&validator_public_key);

let mut bid = match self.read_bid(&validator_account_hash)? {
Some(bid) => bid,
None => {
// Return early if target validator is not in `bids`
return Err(Error::ValidatorNotFound);
}
};

let delegators = bid.delegators_mut();

let new_delegation_amount = match delegators.get_mut(&delegator_public_key) {
Some(delegator) => {
self.mint_transfer_direct(
Some(PublicKey::System.to_account_hash()),
source,
*delegator.bonding_purse(),
amount,
None,
)
.map_err(|_| Error::TransferToDelegatorPurse)?
.map_err(|_| Error::TransferToDelegatorPurse)?;
delegator.increase_stake(amount)?;
*delegator.staked_amount()
}
None => {
let bonding_purse = self.create_purse().map_err(|_| Error::CreatePurseFailed)?;
self.mint_transfer_direct(
Some(PublicKey::System.to_account_hash()),
source,
bonding_purse,
amount,
None,
)
.map_err(|_| Error::TransferToDelegatorPurse)?
.map_err(|_| Error::TransferToDelegatorPurse)?;
let delegator = Delegator::unlocked(
delegator_public_key.clone(),
amount,
bonding_purse,
validator_public_key,
);
delegators.insert(delegator_public_key.clone(), delegator);
amount
}
};

self.write_bid(validator_account_hash, bid)?;

Ok(new_delegation_amount)
}
}

impl<'a, R> AccountProvider for Runtime<'a, R>
Expand Down
9 changes: 8 additions & 1 deletion execution_engine/src/core/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ pub fn key_to_tuple(key: Key) -> Option<([u8; 32], AccessRights)> {
Key::Balance(_) => None,
Key::Bid(_) => None,
Key::Withdraw(_) => None,
Key::Unbond(_) => None,
Key::Dictionary(_) => None,
Key::SystemContractRegistry => None,
}
Expand Down Expand Up @@ -1262,6 +1263,10 @@ where
/// Checks if immediate caller is of session type of the same account as the provided account
/// hash.
fn is_allowed_session_caller(&self, provided_account_hash: &AccountHash) -> bool {
if self.context.get_caller() == PublicKey::System.to_account_hash() {
return true;
}

if let Some(CallStackElement::Session { account_hash }) = self.get_immediate_caller() {
return account_hash == provided_account_hash;
}
Expand Down Expand Up @@ -1807,9 +1812,11 @@ where
let delegator = Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR)?;
let validator = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
let amount = Self::get_named_argument(runtime_args, auction::ARG_AMOUNT)?;
let new_validator =
Self::get_named_argument(runtime_args, auction::ARG_NEW_VALIDATOR)?;

let result = runtime
.undelegate(delegator, validator, amount)
.undelegate(delegator, validator, amount, new_validator)
.map_err(Self::reverter)?;

CLValue::from_t(result).map_err(Self::reverter)
Expand Down
8 changes: 8 additions & 0 deletions execution_engine/src/core/runtime_context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ where
self.named_keys.remove(name);
Ok(())
}
Key::Unbond(_) => {
self.named_keys.remove(name);
Ok(())
}
Key::Dictionary(_) => {
self.named_keys.remove(name);
Ok(())
Expand Down Expand Up @@ -637,6 +641,7 @@ where
StoredValue::EraInfo(_) => Ok(()),
StoredValue::Bid(_) => Ok(()),
StoredValue::Withdraw(_) => Ok(()),
StoredValue::Unbonding(_) => Ok(()),
}
}

Expand Down Expand Up @@ -721,6 +726,7 @@ where
Key::Balance(_) => false,
Key::Bid(_) => true,
Key::Withdraw(_) => true,
Key::Unbond(_) => true,
Key::Dictionary(_) => {
// Dictionary is a special case that will not be readable by default, but the access
// bits are verified from within API call.
Expand All @@ -741,6 +747,7 @@ where
Key::Balance(_) => false,
Key::Bid(_) => false,
Key::Withdraw(_) => false,
Key::Unbond(_) => false,
Key::Dictionary(_) => {
// Dictionary is a special case that will not be readable by default, but the access
// bits are verified from within API call.
Expand All @@ -761,6 +768,7 @@ where
Key::Balance(_) => false,
Key::Bid(_) => false,
Key::Withdraw(_) => false,
Key::Unbond(_) => false,
Key::Dictionary(_) => {
// Dictionary is a special case that will not be readable by default, but the access
// bits are verified from within API call.
Expand Down
3 changes: 2 additions & 1 deletion execution_engine/src/core/tracking_copy/byte_size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ impl ByteSize for StoredValue {
StoredValue::Transfer(transfer) => transfer.serialized_length(),
StoredValue::EraInfo(era_info) => era_info.serialized_length(),
StoredValue::Bid(bid) => bid.serialized_length(),
StoredValue::Withdraw(unbonding_purses) => unbonding_purses.serialized_length(),
StoredValue::Withdraw(withdraw_purses) => withdraw_purses.serialized_length(),
StoredValue::Unbonding(unbonding_purses) => unbonding_purses.serialized_length(),
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions execution_engine/src/core/tracking_copy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,9 @@ impl<R: StateReader<Key, StoredValue>> TrackingCopy<R> {
return Ok(query.into_not_found_result("Bid value found."));
}
StoredValue::Withdraw(_) => {
return Ok(query.into_not_found_result("WithdrawPurses value found."));
}
StoredValue::Unbonding(_) => {
return Ok(query.into_not_found_result("UnbondingPurses value found."));
}
}
Expand Down
10 changes: 9 additions & 1 deletion execution_engine/src/shared/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ impl Transform {
let found = "Withdraw".to_string();
Err(StoredValueTypeMismatch::new(expected, found).into())
}
StoredValue::Unbonding(_) => {
let expected = "Contract or Account".to_string();
let found = "Unbonding".to_string();
Err(StoredValueTypeMismatch::new(expected, found).into())
}
},
Transform::Failure(error) => Err(error),
}
Expand Down Expand Up @@ -360,9 +365,12 @@ impl From<&Transform> for casper_types::Transform {
Transform::Write(StoredValue::Bid(bid)) => {
casper_types::Transform::WriteBid(bid.clone())
}
Transform::Write(StoredValue::Withdraw(unbonding_purses)) => {
Transform::Write(StoredValue::Unbonding(unbonding_purses)) => {
casper_types::Transform::WriteWithdraw(unbonding_purses.clone())
}
Transform::Write(StoredValue::Withdraw(_)) => casper_types::Transform::Failure(
"withdraw purses should not be be written to global state".to_string(),
),
Transform::AddInt32(value) => casper_types::Transform::AddInt32(*value),
Transform::AddUInt64(value) => casper_types::Transform::AddUInt64(*value),
Transform::AddUInt128(value) => casper_types::Transform::AddUInt128(*value),
Expand Down
Loading