diff --git a/execution_engine_testing/tests/src/test/system_contracts/auction/bids.rs b/execution_engine_testing/tests/src/test/system_contracts/auction/bids.rs index 8597725f3f..35137984cc 100644 --- a/execution_engine_testing/tests/src/test/system_contracts/auction/bids.rs +++ b/execution_engine_testing/tests/src/test/system_contracts/auction/bids.rs @@ -4478,7 +4478,7 @@ fn should_transfer_validator_bid() { ARG_AMOUNT => U512::from(SYSTEM_TRANSFER_AMOUNT) }, ) - .build(); + .build(); let validator_1_fund_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, @@ -4488,7 +4488,7 @@ fn should_transfer_validator_bid() { ARG_AMOUNT => U512::from(TRANSFER_AMOUNT) }, ) - .build(); + .build(); let validator_2_fund_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, @@ -4498,7 +4498,7 @@ fn should_transfer_validator_bid() { ARG_AMOUNT => U512::from(TRANSFER_AMOUNT) }, ) - .build(); + .build(); let delegator_1_fund_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, @@ -4508,7 +4508,7 @@ fn should_transfer_validator_bid() { ARG_AMOUNT => U512::from(TRANSFER_AMOUNT) }, ) - .build(); + .build(); let delegator_2_fund_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, @@ -4518,7 +4518,7 @@ fn should_transfer_validator_bid() { ARG_AMOUNT => U512::from(TRANSFER_AMOUNT) }, ) - .build(); + .build(); let validator_1_add_bid_request = ExecuteRequestBuilder::standard( *NON_FOUNDER_VALIDATOR_1_ADDR, @@ -4529,7 +4529,7 @@ fn should_transfer_validator_bid() { ARG_DELEGATION_RATE => ADD_BID_DELEGATION_RATE_1, }, ) - .build(); + .build(); let delegator_1_validator_1_delegate_request = ExecuteRequestBuilder::standard( *BID_ACCOUNT_1_ADDR, @@ -4540,7 +4540,7 @@ fn should_transfer_validator_bid() { ARG_DELEGATOR => BID_ACCOUNT_1_PK.clone(), }, ) - .build(); + .build(); let delegator_2_validator_1_delegate_request = ExecuteRequestBuilder::standard( *BID_ACCOUNT_2_ADDR, @@ -4551,7 +4551,7 @@ fn should_transfer_validator_bid() { ARG_DELEGATOR => BID_ACCOUNT_2_PK.clone(), }, ) - .build(); + .build(); let post_genesis_requests = vec![ system_fund_request, @@ -4576,7 +4576,9 @@ fn should_transfer_validator_bid() { let bids = builder.get_bids(); assert_eq!(bids.len(), 3); - assert!(bids.validator_bid(&NON_FOUNDER_VALIDATOR_2_PK.clone()).is_none()); + assert!(bids + .validator_bid(&NON_FOUNDER_VALIDATOR_2_PK.clone()) + .is_none()); let validator_1_transfer_request = ExecuteRequestBuilder::standard( *NON_FOUNDER_VALIDATOR_1_ADDR, @@ -4586,21 +4588,29 @@ fn should_transfer_validator_bid() { ARG_NEW_VALIDATOR => NON_FOUNDER_VALIDATOR_2_PK.clone() }, ) - .build(); + .build(); - builder.exec(validator_1_transfer_request).commit().expect_success(); + builder + .exec(validator_1_transfer_request) + .commit() + .expect_success(); let bids = builder.get_bids(); assert_eq!(bids.len(), 3); - let validator_bid = bids.validator_bid(&NON_FOUNDER_VALIDATOR_2_PK.clone()).unwrap(); + let validator_bid = bids + .validator_bid(&NON_FOUNDER_VALIDATOR_2_PK.clone()) + .unwrap(); assert_eq!( builder.get_purse_balance(*validator_bid.bonding_purse()), U512::from(ADD_BID_AMOUNT_1) ); - assert!(bids.validator_bid(&NON_FOUNDER_VALIDATOR_1_PK.clone()).is_none()); + assert!(bids + .validator_bid(&NON_FOUNDER_VALIDATOR_1_PK.clone()) + .is_none()); assert!(bids - .delegators_by_validator_public_key(&NON_FOUNDER_VALIDATOR_1_PK).is_none()); + .delegators_by_validator_public_key(&NON_FOUNDER_VALIDATOR_1_PK) + .is_none()); let delegators = bids .delegators_by_validator_public_key(&NON_FOUNDER_VALIDATOR_2_PK) .expect("should have delegators"); diff --git a/smart_contracts/contracts/client/transfer_validator/src/main.rs b/smart_contracts/contracts/client/transfer_validator/src/main.rs index 0382910525..eb8d079b62 100644 --- a/smart_contracts/contracts/client/transfer_validator/src/main.rs +++ b/smart_contracts/contracts/client/transfer_validator/src/main.rs @@ -4,24 +4,26 @@ extern crate alloc; use casper_contract::contract_api::{runtime, system}; -use casper_types::{runtime_args, system::auction, PublicKey, U512}; - -const ARG_VALIDATOR: &str = "validator"; -const ARG_NEW_VALIDATOR: &str = "new_validator"; +use casper_types::{ + runtime_args, + system::auction::{ARG_NEW_VALIDATOR, ARG_VALIDATOR, METHOD_TRANSFER_VALIDATOR}, + PublicKey, +}; fn transfer_validator(validator: PublicKey, new_validator: PublicKey) { let contract_hash = system::get_auction(); let args = runtime_args! { - auction::ARG_VALIDATOR => validator, - auction::ARG_NEW_VALIDATOR => new_validator + ARG_VALIDATOR => validator, + ARG_NEW_VALIDATOR => new_validator }; - runtime::call_contract::<()>(contract_hash, auction::METHOD_TRANSFER_VALIDATOR, args); + runtime::call_contract::<()>(contract_hash, METHOD_TRANSFER_VALIDATOR, args); } // Transfer validator. // -// Accepts current validator's public key and new validator's public key -// where the existing `ValidatorBid` and all related delegators should be transferred. +// Accepts current validator's public key and new validator's public key. +// Updates existing validator bid and all related delegator bids with +// the new validator's public key. #[no_mangle] pub extern "C" fn call() { let validator = runtime::get_named_arg(ARG_VALIDATOR); diff --git a/storage/src/system/auction.rs b/storage/src/system/auction.rs index da304a1b7d..383f350237 100644 --- a/storage/src/system/auction.rs +++ b/storage/src/system/auction.rs @@ -700,8 +700,10 @@ pub trait Auction: } } - /// Transfers a `ValidatorBid` and all related delegators from one validator public key to another. + /// Updates a `ValidatorBid` and all related delegator bids to use a new validator public key. /// + /// This in effect "transfers" a validator along with its stake and all delegators + /// from one public key to another. /// This method can only be called by the existing validator. /// /// The arguments are the existing validator's key and the new validator's key. @@ -719,16 +721,47 @@ pub trait Auction: } // verify that a bid for existing validator exists - let validator_addr = BidAddr::from(validator_public_key.clone()); - let validator_bid = read_validator_bid(self, &validator_addr.into())?; + let validator_bid_addr = BidAddr::from(validator_public_key.clone()); + let mut validator_bid = read_validator_bid(self, &validator_bid_addr.into())?; // verify that a bid for new validator does not exist yet - let new_validator_addr = BidAddr::from(new_validator_public_key.clone()); - if self.read_bid(&new_validator_addr.into())?.is_some() { + let new_validator_bid_addr = BidAddr::from(new_validator_public_key.clone()); + if self.read_bid(&new_validator_bid_addr.into())?.is_some() { return Err(Error::TransferValidatorBid); } - todo!(); + debug!("transferring validator bid from {validator_bid_addr} to {new_validator_bid_addr}"); + + validator_bid.with_validator_public_key(new_validator_public_key.clone()); + self.write_bid( + new_validator_bid_addr.into(), + BidKind::Validator(validator_bid), + )?; + + debug!("pruning validator bid {}", validator_bid_addr); + self.prune_bid(validator_bid_addr); + + debug!("transferring delegator bids from validator {validator_bid_addr} to {new_validator_bid_addr}"); + let delegators = read_delegator_bids(self, &validator_public_key)?; + for mut delegator in delegators { + let delegator_public_key = delegator.delegator_public_key().clone(); + let delegator_bid_addr = + BidAddr::new_from_public_keys(&validator_public_key, Some(&delegator_public_key)); + + delegator.with_validator_public_key(new_validator_public_key.clone()); + let new_delegator_bid_addr = BidAddr::new_from_public_keys( + &new_validator_public_key, + Some(&delegator_public_key), + ); + + self.write_bid( + new_delegator_bid_addr.into(), + BidKind::Delegator(Box::from(delegator)), + )?; + + debug!("pruning delegator bid {}", delegator_bid_addr); + self.prune_bid(delegator_bid_addr); + } Ok(()) } diff --git a/types/src/system/auction/delegator.rs b/types/src/system/auction/delegator.rs index ff672353ba..39652beebe 100644 --- a/types/src/system/auction/delegator.rs +++ b/types/src/system/auction/delegator.rs @@ -187,6 +187,12 @@ impl Delegator { vesting_schedule, } } + + /// Sets validator public key + pub fn with_validator_public_key(&mut self, validator_public_key: PublicKey) -> &mut Self { + self.validator_public_key = validator_public_key; + self + } } impl CLTyped for Delegator { diff --git a/types/src/system/auction/validator_bid.rs b/types/src/system/auction/validator_bid.rs index a90b725b6e..57a096c6bd 100644 --- a/types/src/system/auction/validator_bid.rs +++ b/types/src/system/auction/validator_bid.rs @@ -225,6 +225,12 @@ impl ValidatorBid { self.inactive = true; true } + + /// Sets validator public key + pub fn with_validator_public_key(&mut self, validator_public_key: PublicKey) -> &mut Self { + self.validator_public_key = validator_public_key; + self + } } impl CLTyped for ValidatorBid {