Skip to content

Commit dcf01aa

Browse files
committed
feat(validations): add unstake tests
1 parent 2dff08b commit dcf01aa

File tree

1 file changed

+273
-1
lines changed

1 file changed

+273
-1
lines changed

validations/src/tests/mod.rs

Lines changed: 273 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use itertools::Itertools;
88

99
use 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
};
1314
use 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;
5458
const MAX_VT_WEIGHT: u32 = 20_000;
5559
const MAX_DR_WEIGHT: u32 = 80_000;
5660
const 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

5863
const 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]
88198860
fn 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+
89629234
static LAST_VRF_INPUT: &str = "4da71b67e7e50ae4ad06a71e505244f8b490da55fc58c50386c908f7146d2239";
89639235

89649236
#[test]

0 commit comments

Comments
 (0)