@@ -63,7 +63,8 @@ use witnet_validations::{
63
63
validations:: {
64
64
block_reward, calculate_liars_and_errors_count_from_tally, dr_transaction_fee,
65
65
merkle_tree_root, run_tally, st_transaction_fee, tally_bytes_on_encode_error,
66
- update_utxo_diff, vt_transaction_fee,
66
+ update_utxo_diff, validate_stake_transaction, validate_unstake_transaction,
67
+ vt_transaction_fee,
67
68
} ,
68
69
} ;
69
70
@@ -1180,7 +1181,7 @@ pub fn build_block(
1180
1181
1181
1182
if protocol_version > V1_7 {
1182
1183
let max_st_weight = consensus_constants_wit2. get_maximum_stake_block_weight ( epoch) ;
1183
- let mut overstake_transactions = Vec :: < Hash > :: new ( ) ;
1184
+ let mut invalid_stake_transactions = Vec :: < Hash > :: new ( ) ;
1184
1185
let mut included_validators = HashSet :: < PublicKeyHash > :: new ( ) ;
1185
1186
for st_tx in transactions_pool. st_iter ( ) {
1186
1187
let validator_pkh = st_tx. body . output . authorization . public_key . pkh ( ) ;
@@ -1192,37 +1193,27 @@ pub fn build_block(
1192
1193
continue ;
1193
1194
}
1194
1195
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
- } ) ;
1196
+ // Revalidate stake transaction since concurrent stake transactions could have invalidated the transaction
1197
+ // we want to include here which would result in producing an invalid block.
1198
+ let min_stake = consensus_constants_wit2. get_validator_min_stake_nanowits ( epoch) ;
1203
1199
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
- } ;
1200
+ if let Err ( e) = validate_stake_transaction (
1201
+ st_tx,
1202
+ utxo_diff,
1203
+ epoch,
1204
+ epoch_constants,
1205
+ & vec ! [ ] ,
1206
+ stakes,
1207
+ min_stake,
1208
+ max_stake,
1209
+ ) {
1210
+ log:: warn!(
1211
+ "Cannot build block with stake transaction {}: {}" ,
1212
+ ut_tx. hash( ) ,
1213
+ e
1214
+ ) ;
1215
+ invalid_stake_transactions. push ( st_tx. hash ( ) ) ;
1216
+ }
1226
1217
1227
1218
let transaction_weight = st_tx. weight ( ) ;
1228
1219
let transaction_fee =
@@ -1262,7 +1253,7 @@ pub fn build_block(
1262
1253
included_validators. insert ( validator_pkh) ;
1263
1254
}
1264
1255
1265
- transactions_pool. remove_overstake_transactions ( overstake_transactions ) ;
1256
+ transactions_pool. remove_invalid_stake_transactions ( invalid_stake_transactions ) ;
1266
1257
} else {
1267
1258
transactions_pool. clear_stake_transactions ( ) ;
1268
1259
}
@@ -1281,71 +1272,20 @@ pub fn build_block(
1281
1272
continue ;
1282
1273
}
1283
1274
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.
1292
1277
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
+ }
1349
1289
1350
1290
let transaction_weight = ut_tx. weight ( ) ;
1351
1291
let new_ut_weight = ut_weight. saturating_add ( transaction_weight) ;
0 commit comments