@@ -8,6 +8,7 @@ use itertools::Itertools;
88
99use witnet_config:: defaults:: {
1010 PSEUDO_CONSENSUS_CONSTANTS_POS_MIN_STAKE_NANOWITS ,
11+ PSEUDO_CONSENSUS_CONSTANTS_POS_UNSTAKING_DELAY_SECONDS ,
1112 PSEUDO_CONSENSUS_CONSTANTS_WIP0022_REWARD_COLLATERAL_RATIO ,
1213} ;
1314use witnet_crypto:: {
@@ -27,7 +28,10 @@ use witnet_data_structures::{
2728 radon_error:: RadonError ,
2829 radon_report:: { RadonReport , ReportContext , TypeLike } ,
2930 register_protocol_version,
30- staking:: stakes:: StakesTracker ,
31+ staking:: {
32+ helpers:: StakeKey ,
33+ stakes:: { process_stake_transactions, StakesTracker } ,
34+ } ,
3135 transaction:: * ,
3236 transaction_factory:: transaction_outputs_sum,
3337 utxo_pool:: { UnspentOutputsPool , UtxoDiff } ,
@@ -54,6 +58,7 @@ static ONE_WIT: u64 = 1_000_000_000;
5458const MAX_VT_WEIGHT : u32 = 20_000 ;
5559const MAX_DR_WEIGHT : u32 = 80_000 ;
5660const MIN_STAKE_NANOWITS : u64 = PSEUDO_CONSENSUS_CONSTANTS_POS_MIN_STAKE_NANOWITS ;
61+ const UNSTAKING_DELAY_SECONDS : u64 = PSEUDO_CONSENSUS_CONSTANTS_POS_UNSTAKING_DELAY_SECONDS ;
5762
5863const REQUIRED_REWARD_COLLATERAL_RATIO : u64 =
5964 PSEUDO_CONSENSUS_CONSTANTS_WIP0022_REWARD_COLLATERAL_RATIO ;
@@ -8815,6 +8820,42 @@ fn tally_error_encode_reveal_wip() {
88158820 . map ( |_| ( ) ) ;
88168821 x. unwrap ( ) ;
88178822}
8823+
8824+ fn setup_stakes_tracker (
8825+ stake_value : u64 ,
8826+ key_1 : [ u8 ; 32 ] ,
8827+ key_2 : [ u8 ; 32 ] ,
8828+ ) -> ( PublicKeyHash , PublicKeyHash , StakesTracker ) {
8829+ register_protocol_version ( ProtocolVersion :: V2_0 , 20000 , 10 ) ;
8830+
8831+ let validator_sk =
8832+ Secp256k1_SecretKey :: from_slice ( & key_1) . expect ( "32 bytes, within curve order" ) ;
8833+ let withdrawer_sk =
8834+ Secp256k1_SecretKey :: from_slice ( & key_2) . expect ( "32 bytes, within curve order" ) ;
8835+ let validator_pkh =
8836+ PublicKey :: from ( Secp256k1_PublicKey :: from_secret_key_global ( & validator_sk) ) . pkh ( ) ;
8837+ let withdrawer_pkh =
8838+ PublicKey :: from ( Secp256k1_PublicKey :: from_secret_key_global ( & withdrawer_sk) ) . pkh ( ) ;
8839+
8840+ let stake_output = StakeOutput {
8841+ value : stake_value,
8842+ key : StakeKey :: from ( ( validator_pkh, withdrawer_pkh) ) ,
8843+ ..Default :: default ( )
8844+ } ;
8845+ let vti = Input :: new (
8846+ "2222222222222222222222222222222222222222222222222222222222222222:0"
8847+ . parse ( )
8848+ . unwrap ( ) ,
8849+ ) ;
8850+ let stake_tx_body = StakeTransactionBody :: new ( vec ! [ vti] , stake_output, None ) ;
8851+ let stake_tx = StakeTransaction :: new ( stake_tx_body, vec ! [ ] ) ;
8852+
8853+ let mut stakes = StakesTracker :: default ( ) ;
8854+ let _ = process_stake_transactions ( & mut stakes, vec ! [ stake_tx] . iter ( ) , 0 ) ;
8855+
8856+ ( validator_pkh, withdrawer_pkh, stakes)
8857+ }
8858+
88188859#[ test]
88198860fn st_not_allowed ( ) {
88208861 let utxo_set = UnspentOutputsPool :: default ( ) ;
@@ -8959,6 +9000,237 @@ fn st_below_min_stake() {
89599000 ) ;
89609001}
89619002
9003+ #[ test]
9004+ fn unstake_success ( ) {
9005+ // Setup stakes tracker with a (validator, validator) pair
9006+ let ( validator_pkh, _, stakes) =
9007+ setup_stakes_tracker ( MIN_STAKE_NANOWITS + 1000 , PRIV_KEY_1 , PRIV_KEY_1 ) ;
9008+
9009+ let vto = ValueTransferOutput {
9010+ pkh : validator_pkh,
9011+ value : 1000 ,
9012+ time_lock : UNSTAKING_DELAY_SECONDS ,
9013+ } ;
9014+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 1 ) ;
9015+ // Sign with the validator private key instead of the withdrawer private key
9016+ let signature = sign_tx ( PRIV_KEY_1 , & unstake_tx_body, None ) ;
9017+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9018+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9019+ assert ! ( x. is_ok( ) ) ;
9020+
9021+ // Setup stakes tracker with a (validator, withdrawer) pair
9022+ let ( validator_pkh, withdrawer_pkh, stakes) =
9023+ setup_stakes_tracker ( MIN_STAKE_NANOWITS + 1000 , PRIV_KEY_1 , PRIV_KEY_2 ) ;
9024+
9025+ // Try unstaking up to the MIN_STAKE_NANOWITS balance
9026+ let vto = ValueTransferOutput {
9027+ pkh : withdrawer_pkh,
9028+ value : 1000 ,
9029+ time_lock : UNSTAKING_DELAY_SECONDS ,
9030+ } ;
9031+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 1 ) ;
9032+ // Sign with the validator private key instead of the withdrawer private key
9033+ let signature = sign_tx ( PRIV_KEY_2 , & unstake_tx_body, None ) ;
9034+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9035+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9036+ assert ! ( x. is_ok( ) ) ;
9037+
9038+ // Note that we did not process above unstake transaction, so the stakes structure did not change
9039+ // Try unstaking the full balance
9040+ let vto = ValueTransferOutput {
9041+ pkh : withdrawer_pkh,
9042+ value : MIN_STAKE_NANOWITS + 1000 ,
9043+ time_lock : UNSTAKING_DELAY_SECONDS ,
9044+ } ;
9045+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 1 ) ;
9046+ // Sign with the validator private key instead of the withdrawer private key
9047+ let signature = sign_tx ( PRIV_KEY_2 , & unstake_tx_body, None ) ;
9048+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9049+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9050+ assert ! ( x. is_ok( ) ) ;
9051+ }
9052+
9053+ #[ test]
9054+ fn unstake_not_allowed ( ) {
9055+ register_protocol_version ( ProtocolVersion :: V2_0 , 20000 , 10 ) ;
9056+
9057+ let stakes = StakesTracker :: default ( ) ;
9058+ let vto = ValueTransferOutput {
9059+ pkh : PublicKeyHash :: default ( ) ,
9060+ value : 1 ,
9061+ time_lock : 0 ,
9062+ } ;
9063+ let unstake_tx_body = UnstakeTransactionBody :: new ( PublicKeyHash :: default ( ) , vto, 0 , 1 ) ;
9064+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, KeyedSignature :: default ( ) ) ;
9065+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: default ( ) , & stakes) ;
9066+ assert_eq ! (
9067+ x. unwrap_err( ) . downcast:: <TransactionError >( ) . unwrap( ) ,
9068+ TransactionError :: NoUnstakeTransactionsAllowed { }
9069+ ) ;
9070+ }
9071+
9072+ #[ test]
9073+ fn unstake_more_than_staked ( ) {
9074+ let ( validator_pkh, withdrawer_pkh, stakes) =
9075+ setup_stakes_tracker ( MIN_STAKE_NANOWITS , PRIV_KEY_1 , PRIV_KEY_2 ) ;
9076+
9077+ // Unstaking 1 nanowit more than what was staked (using a zero nanowit fee)
9078+ let vto = ValueTransferOutput {
9079+ pkh : withdrawer_pkh,
9080+ value : MIN_STAKE_NANOWITS + 1 ,
9081+ time_lock : UNSTAKING_DELAY_SECONDS ,
9082+ } ;
9083+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 1 ) ;
9084+ let signature = sign_tx ( PRIV_KEY_2 , & unstake_tx_body, None ) ;
9085+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9086+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9087+ assert_eq ! (
9088+ x. unwrap_err( ) . downcast:: <TransactionError >( ) . unwrap( ) ,
9089+ TransactionError :: UnstakingMoreThanStaked {
9090+ unstake: MIN_STAKE_NANOWITS + 1 ,
9091+ stake: MIN_STAKE_NANOWITS
9092+ }
9093+ ) ;
9094+
9095+ // Unstaking what was staked, but adding a 1 nanowit fee resulting in unstaking too much
9096+ let vto = ValueTransferOutput {
9097+ pkh : withdrawer_pkh,
9098+ value : MIN_STAKE_NANOWITS ,
9099+ time_lock : UNSTAKING_DELAY_SECONDS ,
9100+ } ;
9101+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 1 , 1 ) ;
9102+ let signature = sign_tx ( PRIV_KEY_2 , & unstake_tx_body, None ) ;
9103+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9104+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9105+ assert_eq ! (
9106+ x. unwrap_err( ) . downcast:: <TransactionError >( ) . unwrap( ) ,
9107+ TransactionError :: UnstakingMoreThanStaked {
9108+ unstake: MIN_STAKE_NANOWITS + 1 ,
9109+ stake: MIN_STAKE_NANOWITS
9110+ }
9111+ ) ;
9112+ }
9113+
9114+ #[ test]
9115+ fn unstake_invalid_nonce ( ) {
9116+ let ( validator_pkh, withdrawer_pkh, stakes) =
9117+ setup_stakes_tracker ( MIN_STAKE_NANOWITS , PRIV_KEY_1 , PRIV_KEY_2 ) ;
9118+
9119+ let vto = ValueTransferOutput {
9120+ pkh : withdrawer_pkh,
9121+ value : MIN_STAKE_NANOWITS ,
9122+ time_lock : UNSTAKING_DELAY_SECONDS ,
9123+ } ;
9124+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 0 ) ;
9125+ let signature = sign_tx ( PRIV_KEY_2 , & unstake_tx_body, None ) ;
9126+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9127+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9128+ assert_eq ! (
9129+ x. unwrap_err( ) . downcast:: <TransactionError >( ) . unwrap( ) ,
9130+ TransactionError :: UnstakeInvalidNonce {
9131+ used: 0 ,
9132+ current: 1
9133+ }
9134+ ) ;
9135+ }
9136+
9137+ #[ test]
9138+ fn unstake_wrong_withdrawer ( ) {
9139+ let ( validator_pkh, withdrawer_pkh, stakes) =
9140+ setup_stakes_tracker ( MIN_STAKE_NANOWITS , PRIV_KEY_1 , PRIV_KEY_2 ) ;
9141+
9142+ let vto = ValueTransferOutput {
9143+ pkh : withdrawer_pkh,
9144+ value : MIN_STAKE_NANOWITS ,
9145+ time_lock : UNSTAKING_DELAY_SECONDS ,
9146+ } ;
9147+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 1 ) ;
9148+ // Sign with the validator private key instead of the withdrawer private key
9149+ let signature = sign_tx ( PRIV_KEY_1 , & unstake_tx_body, None ) ;
9150+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9151+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9152+ assert_eq ! (
9153+ x. unwrap_err( ) . downcast:: <TransactionError >( ) . unwrap( ) ,
9154+ TransactionError :: NoStakeFound {
9155+ validator: validator_pkh,
9156+ withdrawer: validator_pkh,
9157+ }
9158+ ) ;
9159+ }
9160+
9161+ #[ test]
9162+ fn unstake_below_stake_minimum ( ) {
9163+ let ( validator_pkh, withdrawer_pkh, stakes) =
9164+ setup_stakes_tracker ( MIN_STAKE_NANOWITS + 1000 , PRIV_KEY_1 , PRIV_KEY_2 ) ;
9165+
9166+ let vto = ValueTransferOutput {
9167+ pkh : withdrawer_pkh,
9168+ value : 1001 ,
9169+ time_lock : UNSTAKING_DELAY_SECONDS ,
9170+ } ;
9171+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 1 ) ;
9172+ let signature = sign_tx ( PRIV_KEY_2 , & unstake_tx_body, None ) ;
9173+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9174+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9175+ assert_eq ! (
9176+ x. unwrap_err( ) . downcast:: <TransactionError >( ) . unwrap( ) ,
9177+ TransactionError :: StakeBelowMinimum {
9178+ min_stake: MIN_STAKE_NANOWITS ,
9179+ stake: MIN_STAKE_NANOWITS + 1000 ,
9180+ }
9181+ ) ;
9182+ }
9183+
9184+ #[ test]
9185+ fn unstake_timelock ( ) {
9186+ let ( validator_pkh, withdrawer_pkh, stakes) =
9187+ setup_stakes_tracker ( MIN_STAKE_NANOWITS , PRIV_KEY_1 , PRIV_KEY_2 ) ;
9188+
9189+ let vto = ValueTransferOutput {
9190+ pkh : withdrawer_pkh,
9191+ value : MIN_STAKE_NANOWITS ,
9192+ time_lock : 0 ,
9193+ } ;
9194+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 1 ) ;
9195+ let signature = sign_tx ( PRIV_KEY_2 , & unstake_tx_body, None ) ;
9196+ let unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9197+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9198+ assert_eq ! (
9199+ x. unwrap_err( ) . downcast:: <TransactionError >( ) . unwrap( ) ,
9200+ TransactionError :: InvalidUnstakeTimelock {
9201+ time_lock: unstake_tx. body. withdrawal. time_lock,
9202+ unstaking_delay_seconds: UNSTAKING_DELAY_SECONDS ,
9203+ }
9204+ ) ;
9205+ }
9206+
9207+ #[ test]
9208+ fn unstake_signature ( ) {
9209+ let ( validator_pkh, withdrawer_pkh, stakes) =
9210+ setup_stakes_tracker ( MIN_STAKE_NANOWITS + 1000 , PRIV_KEY_1 , PRIV_KEY_2 ) ;
9211+
9212+ let vto = ValueTransferOutput {
9213+ pkh : withdrawer_pkh,
9214+ value : 1000 ,
9215+ time_lock : UNSTAKING_DELAY_SECONDS ,
9216+ } ;
9217+ let unstake_tx_body = UnstakeTransactionBody :: new ( validator_pkh, vto, 0 , 1 ) ;
9218+ let signature = sign_tx ( PRIV_KEY_2 , & unstake_tx_body, None ) ;
9219+ let mut unstake_tx = UnstakeTransaction :: new ( unstake_tx_body, signature) ;
9220+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9221+ assert ! ( x. is_ok( ) ) ;
9222+
9223+ unstake_tx. body . withdrawal . value = 999 ;
9224+ let x = validate_unstake_transaction ( & unstake_tx, Epoch :: from ( 20001 as u32 ) , & stakes) ;
9225+ assert_eq ! (
9226+ x. unwrap_err( ) . downcast:: <TransactionError >( ) . unwrap( ) ,
9227+ TransactionError :: VerifyTransactionSignatureFail {
9228+ hash: unstake_tx. hash( ) ,
9229+ msg: "signature failed verification" . to_string( ) ,
9230+ }
9231+ ) ;
9232+ }
9233+
89629234static LAST_VRF_INPUT : & str = "4da71b67e7e50ae4ad06a71e505244f8b490da55fc58c50386c908f7146d2239" ;
89639235
89649236#[ test]
0 commit comments