Skip to content

Commit d61cb0b

Browse files
committed
feat(node): reduce duplicated stake and unstake validation code by relying on the validation module
1 parent f58517e commit d61cb0b

File tree

2 files changed

+40
-99
lines changed

2 files changed

+40
-99
lines changed

data_structures/src/chain/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3190,7 +3190,7 @@ impl TransactionsPool {
31903190
}
31913191

31923192
/// Remove stake transactions that would result in overstaking on a validator
3193-
pub fn remove_overstake_transactions(&mut self, transactions: Vec<Hash>) {
3193+
pub fn remove_invalid_stake_transactions(&mut self, transactions: Vec<Hash>) {
31943194
for st_tx_hash in transactions.iter() {
31953195
if let Some(st_tx) = self
31963196
.st_transactions

node/src/actors/chain_manager/mining.rs

+39-98
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ use witnet_data_structures::{
3333
radon_error::RadonError,
3434
radon_report::{RadonReport, ReportContext, TypeLike},
3535
staking::{
36-
helpers::StakeKey,
3736
stake::totalize_stakes,
3837
stakes::{QueryStakesKey, StakesTracker},
3938
},
@@ -63,7 +62,8 @@ use witnet_validations::{
6362
validations::{
6463
block_reward, calculate_liars_and_errors_count_from_tally, dr_transaction_fee,
6564
merkle_tree_root, run_tally, st_transaction_fee, tally_bytes_on_encode_error,
66-
update_utxo_diff, vt_transaction_fee,
65+
update_utxo_diff, validate_stake_transaction, validate_unstake_transaction,
66+
vt_transaction_fee,
6767
},
6868
};
6969

@@ -1180,7 +1180,7 @@ pub fn build_block(
11801180

11811181
if protocol_version > V1_7 {
11821182
let max_st_weight = consensus_constants_wit2.get_maximum_stake_block_weight(epoch);
1183-
let mut overstake_transactions = Vec::<Hash>::new();
1183+
let mut invalid_stake_transactions = Vec::<Hash>::new();
11841184
let mut included_validators = HashSet::<PublicKeyHash>::new();
11851185
for st_tx in transactions_pool.st_iter() {
11861186
let validator_pkh = st_tx.body.output.authorization.public_key.pkh();
@@ -1192,37 +1192,28 @@ pub fn build_block(
11921192
continue;
11931193
}
11941194

1195-
// If a set of staking transactions is sent simultaneously to the transactions pool using a staking amount smaller
1196-
// than 'max_stake' they can all be accepted since they do not introduce overstaking yet. However, accepting all of
1197-
// them in subsequent blocks could violate the 'max_stake' rule. Thus we still need to check that we do not include
1198-
// all these staking transactions in a block so we do not produce an invalid block.
1199-
let stakes_key = QueryStakesKey::Key(StakeKey {
1200-
validator: st_tx.body.output.key.validator,
1201-
withdrawer: st_tx.body.output.key.withdrawer,
1202-
});
1195+
// Revalidate stake transaction since concurrent stake transactions could have invalidated the transaction
1196+
// we want to include here which would result in producing an invalid block.
1197+
let min_stake = consensus_constants_wit2.get_validator_min_stake_nanowits(epoch);
12031198
let max_stake = consensus_constants_wit2.get_validator_max_stake_nanowits(epoch);
1204-
match stakes.query_stakes(stakes_key) {
1205-
Ok(stake_entry) => {
1206-
// TODO: modify this to enable delegated staking with multiple withdrawer addresses on a single validator
1207-
let staked_amount: u64 = stake_entry
1208-
.first()
1209-
.map(|stake| stake.value.coins)
1210-
.unwrap()
1211-
.into();
1212-
if st_tx.body.output.value + staked_amount > max_stake {
1213-
overstake_transactions.push(st_tx.hash());
1214-
continue;
1215-
}
1216-
}
1217-
Err(_) => {
1218-
// This should never happen since a staking transaction to a non-existing (validator, withdrawer) pair
1219-
// with a value higher than 'max_stake' should not have been accepted in the transactions pool.
1220-
if st_tx.body.output.value > max_stake {
1221-
overstake_transactions.push(st_tx.hash());
1222-
continue;
1223-
}
1224-
}
1225-
};
1199+
if let Err(e) = validate_stake_transaction(
1200+
st_tx,
1201+
&utxo_diff,
1202+
epoch,
1203+
epoch_constants,
1204+
&mut vec![],
1205+
stakes,
1206+
min_stake,
1207+
max_stake,
1208+
) {
1209+
log::warn!(
1210+
"Cannot build block with stake transaction {}: {}",
1211+
st_tx.hash(),
1212+
e
1213+
);
1214+
invalid_stake_transactions.push(st_tx.hash());
1215+
continue;
1216+
}
12261217

12271218
let transaction_weight = st_tx.weight();
12281219
let transaction_fee =
@@ -1262,7 +1253,7 @@ pub fn build_block(
12621253
included_validators.insert(validator_pkh);
12631254
}
12641255

1265-
transactions_pool.remove_overstake_transactions(overstake_transactions);
1256+
transactions_pool.remove_invalid_stake_transactions(invalid_stake_transactions);
12661257
} else {
12671258
transactions_pool.clear_stake_transactions();
12681259
}
@@ -1281,71 +1272,21 @@ pub fn build_block(
12811272
continue;
12821273
}
12831274

1284-
// If a set of unstaking transactions is sent simultaneously to the transactions pool using an amount which leaves
1285-
// more than 'min_stake' staked they can all be accepted since they do not introduce understaking yet. However,
1286-
// accepting all of them in subsequent blocks could violate the 'min_stake' rule. Thus we still need to check that
1287-
// we do not include all these unstaking transactions in a block so we do not produce an invalid block.
1288-
let stakes_key = QueryStakesKey::Key(StakeKey {
1289-
validator: ut_tx.body.operator,
1290-
withdrawer: ut_tx.body.withdrawal.pkh,
1291-
});
1275+
// Revalidate unstake transaction since concurrent unstake transactions could have invalidated the transaction
1276+
// we want to include here which would result in producing an invalid block.
12921277
let min_stake = consensus_constants_wit2.get_validator_min_stake_nanowits(epoch);
1293-
match stakes.query_stakes(stakes_key) {
1294-
Ok(stake_entry) => {
1295-
// TODO: modify this to enable delegated staking with multiple withdrawer addresses on a single validator
1296-
let staked_amount: u64 = stake_entry
1297-
.first()
1298-
.map(|stake| stake.value.coins)
1299-
.unwrap()
1300-
.into();
1301-
if staked_amount - ut_tx.body.withdrawal.value - ut_tx.body.fee < min_stake {
1302-
log::info!(
1303-
"Unstaking with {} would result in understaking",
1304-
ut_tx.hash()
1305-
);
1306-
invalid_unstake_transactions.push(ut_tx.hash());
1307-
continue;
1308-
}
1309-
}
1310-
Err(_) => {
1311-
// Not finding a stake entry is possible if there are two concurrent unstake transactions where at least
1312-
// one of them unstakes all balance before the second one is included in a block. In that case, remove
1313-
// the latter one from our transaction pool.
1314-
log::info!("Cannot process unstake transaction {} since the full balance was already unstaked",
1315-
ut_tx.hash(),
1316-
);
1317-
invalid_unstake_transactions.push(ut_tx.hash());
1318-
continue;
1319-
}
1320-
};
1321-
1322-
// Double check the nonce before building a block. A nonce that was valid when the transaction was received
1323-
// and inserted into the transaction pool once validated, may not be anymore when we build the actual block
1324-
// due to concurrent transactions.
1325-
let key = StakeKey {
1326-
validator: ut_tx.body.operator,
1327-
withdrawer: ut_tx.body.withdrawal.pkh,
1328-
};
1329-
match stakes.query_nonce(key) {
1330-
Ok(nonce) => {
1331-
if ut_tx.body.nonce != nonce {
1332-
log::info!("Cannot process unstake transaction {} since the nonce does not match: {} != {}",
1333-
ut_tx.hash(),
1334-
ut_tx.body.nonce,
1335-
nonce,
1336-
);
1337-
invalid_unstake_transactions.push(ut_tx.hash());
1338-
continue;
1339-
}
1340-
}
1341-
Err(_) => {
1342-
log::info!("Cannot process unstake transaction {} since the associated stake entry does not exist anymore",
1343-
ut_tx.hash()
1344-
);
1345-
invalid_unstake_transactions.push(ut_tx.hash());
1346-
continue;
1347-
}
1348-
};
1278+
let unstake_delay = consensus_constants_wit2.get_unstaking_delay_seconds(epoch);
1279+
if let Err(e) =
1280+
validate_unstake_transaction(ut_tx, epoch, stakes, min_stake, unstake_delay)
1281+
{
1282+
log::warn!(
1283+
"Cannot build block with unstake transaction {}: {}",
1284+
ut_tx.hash(),
1285+
e
1286+
);
1287+
invalid_unstake_transactions.push(ut_tx.hash());
1288+
continue;
1289+
}
13491290

13501291
let transaction_weight = ut_tx.weight();
13511292
let new_ut_weight = ut_weight.saturating_add(transaction_weight);

0 commit comments

Comments
 (0)