Skip to content

Commit 2bfc7b0

Browse files
refactor!: Wrap fallible guesser fee helper functions in Result
Function `Block::total_guesser_reward`, along with other helper methods related to guesser fees, are undefined for un-validated blocks, so the result should be wrapped in a `Result` in order to avoid passing inconsistent values up the call graph. This change cascades into many function return type modifications and many `.unwrap`s and `.expect`s downstream. The inconsistency was exposed by test `rpc_server::rpc_server_tests::public_announcements_in_block_test` which is now fixed instead of ignored. Thanks to @skaunov for PR #543 and the effort leading to this exposition. Also: - Change logic for computing block proposal favorability: use `prev_block_digest` instead of block height. However, the old method (now suffixed with `_legacy`) continues to exist to enable unmodified processing of `PeerMessage` `BlockProposalNotification`, which comes with a block height and not a digest. (We cannot change `PeerMessage`, unfortunately.) - Rewrite handler for `PeerMessage` `BlockProposal`. Simplify and reduce indentation. Co-authored-by: Alan Szepieniec <[email protected]> Co-authored-by: Thorkil Schmidiger <[email protected]>
1 parent c841cf5 commit 2bfc7b0

File tree

21 files changed

+525
-263
lines changed

21 files changed

+525
-263
lines changed

benches/wallet_state.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ mod maintain_membership_proofs {
133133
rt.block_on(async {
134134
wallet_state
135135
.update_wallet_state_with_new_block(
136-
&genesis.mutator_set_accumulator_after(),
136+
&genesis.mutator_set_accumulator_after().unwrap(),
137137
&block1,
138138
)
139139
.await
@@ -156,7 +156,7 @@ mod maintain_membership_proofs {
156156
rt.block_on(async {
157157
wallet_state
158158
.update_wallet_state_with_new_block(
159-
&block1.mutator_set_accumulator_after(),
159+
&block1.mutator_set_accumulator_after().unwrap(),
160160
&block2,
161161
)
162162
.await

src/api/regtest/regtest_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl RegTestPrivate {
122122
SIZE_20MB_IN_BYTES,
123123
Some(MAX_NUM_TXS_TO_MERGE),
124124
true, //only_merge_single_proofs
125-
tip_block.mutator_set_accumulator_after().hash(),
125+
tip_block.mutator_set_accumulator_after().unwrap().hash(),
126126
);
127127

128128
drop(gs);

src/api/tx_initiation/builder/transaction_details_builder.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,9 @@ impl TransactionDetailsBuilder {
257257
fee,
258258
coinbase,
259259
timestamp,
260-
tip_block.mutator_set_accumulator_after(),
260+
tip_block
261+
.mutator_set_accumulator_after()
262+
.expect("Block from state must be valid"),
261263
state_lock.cli().network,
262264
))
263265
}

src/bench_helpers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub async fn next_block_incoming_utxos(
111111
.sum::<NativeCurrencyAmount>()
112112
+ fee;
113113

114-
let msa = parent.mutator_set_accumulator_after();
114+
let msa = parent.mutator_set_accumulator_after().unwrap();
115115
let wallet_status = sender.get_wallet_status(parent.hash(), &msa).await;
116116
let available_balance = wallet_status.synced_unspent_available_amount(timestamp);
117117
let change_amt = available_balance.checked_sub(&intermediate_spend).unwrap();

src/main_loop.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -893,8 +893,10 @@ impl MainLoopHandler {
893893
info!("Received new favorable block proposal for mining operation.");
894894
let mut global_state_mut = self.global_state_lock.lock_guard_mut().await;
895895
let verdict = global_state_mut.favor_incoming_block_proposal(
896-
block.header().height,
897-
block.total_guesser_reward(),
896+
block.header().prev_block_digest,
897+
block
898+
.total_guesser_reward()
899+
.expect("block received by main loop should be valid"),
898900
);
899901
if let Err(reject_reason) = verdict {
900902
warn!("main loop got unfavorable block proposal. Reason: {reject_reason}");

src/main_loop/proof_upgrader.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,8 @@ impl UpgradeJob {
380380
let tip_mutator_set = global_state
381381
.chain
382382
.light_state()
383-
.mutator_set_accumulator_after();
383+
.mutator_set_accumulator_after()
384+
.expect("Block from state must be valid");
384385

385386
let transaction_is_up_to_date =
386387
upgraded.kernel.mutator_set_hash == tip_mutator_set.hash();
@@ -740,7 +741,8 @@ pub(super) fn get_upgrade_task_from_mempool(
740741
let tip_mutator_set = global_state
741742
.chain
742743
.light_state()
743-
.mutator_set_accumulator_after();
744+
.mutator_set_accumulator_after()
745+
.expect("Block from state must be valid");
744746
let gobbling_fraction = global_state.gobbling_fraction();
745747
let min_gobbling_fee = global_state.min_gobbling_fee();
746748
let num_proofs_threshold = global_state.max_num_proofs();
@@ -1123,7 +1125,8 @@ mod tests {
11231125
.await
11241126
.chain
11251127
.light_state()
1126-
.mutator_set_accumulator_after();
1128+
.mutator_set_accumulator_after()
1129+
.unwrap();
11271130
assert!(mempool_tx.is_confirmable_relative_to(&mutator_set_accumulator_after));
11281131
}
11291132
}

src/mine_loop.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -527,13 +527,17 @@ pub(crate) fn composer_outputs(
527527

528528
/// Compute `TransactionDetails` and a list of `TxOutput`s for a coinbase
529529
/// transaction.
530+
///
531+
/// # Panics
532+
///
533+
/// - If `latest_block` is invalid.
530534
pub(super) fn prepare_coinbase_transaction_stateless(
531535
latest_block: &Block,
532536
composer_parameters: ComposerParameters,
533537
timestamp: Timestamp,
534538
network: Network,
535539
) -> (TxOutputList, TransactionDetails) {
536-
let mutator_set_accumulator = latest_block.mutator_set_accumulator_after().clone();
540+
let mutator_set_accumulator = latest_block.mutator_set_accumulator_after().unwrap();
537541
let next_block_height: BlockHeight = latest_block.header().height.next();
538542
info!("Creating coinbase for block of height {next_block_height}.");
539543

@@ -591,6 +595,8 @@ pub(crate) async fn create_block_transaction(
591595
.await
592596
}
593597

598+
/// # Panics
599+
/// - If predecessor is invalid
594600
pub(crate) async fn create_block_transaction_from(
595601
predecessor_block: &Block,
596602
global_state_lock: &GlobalStateLock,
@@ -600,7 +606,9 @@ pub(crate) async fn create_block_transaction_from(
600606
) -> Result<(Transaction, Vec<ExpectedUtxo>)> {
601607
let block_capacity_for_transactions = SIZE_20MB_IN_BYTES;
602608

603-
let predecessor_block_ms = predecessor_block.mutator_set_accumulator_after();
609+
let predecessor_block_ms = predecessor_block
610+
.mutator_set_accumulator_after()
611+
.expect("predecessor should be valid");
604612
let mutator_set_hash = predecessor_block_ms.hash();
605613
debug!("Creating block transaction with mutator set hash: {mutator_set_hash}",);
606614

@@ -645,7 +653,7 @@ pub(crate) async fn create_block_transaction_from(
645653
// Guarantees that some merge happens in below loop, which sets merge-bit.
646654
if transactions_to_merge.is_empty() {
647655
let nop = TransactionDetails::nop(
648-
predecessor_block.mutator_set_accumulator_after(),
656+
predecessor_block_ms,
649657
timestamp,
650658
global_state_lock.cli().network,
651659
);
@@ -1178,7 +1186,10 @@ pub(crate) mod tests {
11781186
make_mock_transaction_with_mutator_set_hash(
11791187
vec![],
11801188
outputs,
1181-
previous_block.mutator_set_accumulator_after().hash(),
1189+
previous_block
1190+
.mutator_set_accumulator_after()
1191+
.unwrap()
1192+
.hash(),
11821193
),
11831194
dummy_expected_utxo(),
11841195
)
@@ -1759,7 +1770,7 @@ pub(crate) mod tests {
17591770
let transaction = make_mock_transaction_with_mutator_set_hash(
17601771
vec![],
17611772
vec![],
1762-
prev_block.mutator_set_accumulator_after().hash(),
1773+
prev_block.mutator_set_accumulator_after().unwrap().hash(),
17631774
);
17641775

17651776
let guesser_key = HashLockKey::from_preimage(Digest::default());
@@ -2394,7 +2405,7 @@ pub(crate) mod tests {
23942405
let transaction = make_mock_transaction_with_mutator_set_hash(
23952406
vec![],
23962407
vec![],
2397-
prev_block.mutator_set_accumulator_after().hash(),
2408+
prev_block.mutator_set_accumulator_after().unwrap().hash(),
23982409
);
23992410

24002411
// gen guesser key

src/models/blockchain/block/mock_block_generator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ impl MockBlockGenerator {
146146
// create the nop-tx and merge into the coinbase transaction to set the
147147
// merge bit to allow the tx to be included in a block.
148148
let nop_details = TransactionDetails::nop(
149-
predecessor_block.mutator_set_accumulator_after(),
149+
predecessor_block.mutator_set_accumulator_after().unwrap(),
150150
timestamp,
151151
network,
152152
);

src/models/blockchain/block/mod.rs

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -374,12 +374,16 @@ impl Block {
374374
///
375375
/// Includes the guesser-fee UTXOs which are not included by the
376376
/// `mutator_set_accumulator` field on the block body.
377-
pub fn mutator_set_accumulator_after(&self) -> MutatorSetAccumulator {
377+
pub fn mutator_set_accumulator_after(
378+
&self,
379+
) -> Result<MutatorSetAccumulator, BlockValidationError> {
378380
let mut msa = self.kernel.body.mutator_set_accumulator.clone();
379-
let mutator_set_update = MutatorSetUpdate::new(vec![], self.guesser_fee_addition_records());
381+
let mutator_set_update =
382+
MutatorSetUpdate::new(vec![], self.guesser_fee_addition_records()?);
380383
mutator_set_update.apply_to_accumulator(&mut msa)
381384
.expect("mutator set update derived from guesser fees should be applicable to mutator set accumulator contained in body");
382-
msa
385+
386+
Ok(msa)
383387
}
384388

385389
#[inline]
@@ -819,11 +823,9 @@ impl Block {
819823
}
820824

821825
// 2.a)
826+
let msa_before = previous_block.mutator_set_accumulator_after()?;
822827
for removal_record in &self.kernel.body.transaction_kernel.inputs {
823-
if !previous_block
824-
.mutator_set_accumulator_after()
825-
.can_remove(removal_record)
826-
{
828+
if !msa_before.can_remove(removal_record) {
827829
return Err(BlockValidationError::RemovalRecordsValid);
828830
}
829831
}
@@ -847,7 +849,7 @@ impl Block {
847849
self.body().transaction_kernel.inputs.clone(),
848850
self.body().transaction_kernel.outputs.clone(),
849851
);
850-
let mut msa = previous_block.mutator_set_accumulator_after();
852+
let mut msa = msa_before;
851853
let ms_update_result = mutator_set_update.apply_to_accumulator(&mut msa);
852854

853855
// 2.c)
@@ -1025,26 +1027,32 @@ impl Block {
10251027

10261028
/// The amount rewarded to the guesser who finds a valid nonce for this
10271029
/// block.
1028-
pub(crate) fn total_guesser_reward(&self) -> NativeCurrencyAmount {
1029-
self.body().transaction_kernel.fee
1030+
pub(crate) fn total_guesser_reward(
1031+
&self,
1032+
) -> Result<NativeCurrencyAmount, BlockValidationError> {
1033+
if self.body().transaction_kernel.fee.is_negative() {
1034+
return Err(BlockValidationError::NegativeFee);
1035+
}
1036+
1037+
Ok(self.body().transaction_kernel.fee)
10301038
}
10311039

10321040
/// Get the block's guesser fee UTXOs.
10331041
///
10341042
/// The amounts in the UTXOs are taken from the transaction fee.
10351043
///
10361044
/// The genesis block does not have a guesser reward.
1037-
pub(crate) fn guesser_fee_utxos(&self) -> Vec<Utxo> {
1045+
pub(crate) fn guesser_fee_utxos(&self) -> Result<Vec<Utxo>, BlockValidationError> {
10381046
const MINER_REWARD_TIME_LOCK_PERIOD: Timestamp = Timestamp::years(3);
10391047

10401048
if self.header().height.is_genesis() {
1041-
return vec![];
1049+
return Ok(vec![]);
10421050
}
10431051

10441052
let lock = self.header().guesser_digest;
10451053
let lock_script = HashLockKey::lock_script_from_after_image(lock);
10461054

1047-
let total_guesser_reward = self.total_guesser_reward();
1055+
let total_guesser_reward = self.total_guesser_reward()?;
10481056
let mut value_locked = total_guesser_reward;
10491057
value_locked.div_two();
10501058
let value_unlocked = total_guesser_reward.checked_sub(&value_locked).unwrap();
@@ -1056,15 +1064,18 @@ impl Block {
10561064
let locked_utxo = Utxo::new(lock_script.clone(), coins);
10571065
let unlocked_utxo = Utxo::new_native_currency(lock_script, value_unlocked);
10581066

1059-
vec![locked_utxo, unlocked_utxo]
1067+
Ok(vec![locked_utxo, unlocked_utxo])
10601068
}
10611069

10621070
/// Compute the addition records that correspond to the UTXOs generated for
10631071
/// the block's guesser
10641072
///
10651073
/// The genesis block does not have this addition record.
1066-
pub(crate) fn guesser_fee_addition_records(&self) -> Vec<AdditionRecord> {
1067-
self.guesser_fee_utxos()
1074+
pub(crate) fn guesser_fee_addition_records(
1075+
&self,
1076+
) -> Result<Vec<AdditionRecord>, BlockValidationError> {
1077+
Ok(self
1078+
.guesser_fee_utxos()?
10681079
.into_iter()
10691080
.map(|utxo| {
10701081
let item = Tip5::hash(&utxo);
@@ -1078,21 +1089,22 @@ impl Block {
10781089

10791090
commit(item, sender_randomness, receiver_digest)
10801091
})
1081-
.collect_vec()
1092+
.collect_vec())
10821093
}
10831094

10841095
/// Return the mutator set update corresponding to this block, which sends
10851096
/// the mutator set accumulator after the predecessor to the mutator set
10861097
/// accumulator after self.
1087-
pub(crate) fn mutator_set_update(&self) -> MutatorSetUpdate {
1098+
pub(crate) fn mutator_set_update(&self) -> Result<MutatorSetUpdate, BlockValidationError> {
10881099
let mut mutator_set_update = MutatorSetUpdate::new(
10891100
self.body().transaction_kernel.inputs.clone(),
10901101
self.body().transaction_kernel.outputs.clone(),
10911102
);
10921103

1093-
let extra_addition_records = self.guesser_fee_addition_records();
1104+
let extra_addition_records = self.guesser_fee_addition_records()?;
10941105
mutator_set_update.additions.extend(extra_addition_records);
1095-
mutator_set_update
1106+
1107+
Ok(mutator_set_update)
10961108
}
10971109
}
10981110

@@ -1258,7 +1270,7 @@ pub(crate) mod tests {
12581270
block_proof_witness.appendix(),
12591271
BlockProof::Invalid,
12601272
);
1261-
let total_guesser_reward = block1.total_guesser_reward();
1273+
let total_guesser_reward = block1.total_guesser_reward().unwrap();
12621274
let total_miner_reward = total_composer_reward + total_guesser_reward;
12631275
assert_eq!(NativeCurrencyAmount::coins(128), total_miner_reward);
12641276

@@ -1826,9 +1838,10 @@ pub(crate) mod tests {
18261838
(0.4, guesser_preimage),
18271839
)
18281840
.await;
1829-
let ars = block1.guesser_fee_addition_records();
1841+
let ars = block1.guesser_fee_addition_records().unwrap();
18301842
let ars_from_wallet = block1
18311843
.guesser_fee_utxos()
1844+
.unwrap()
18321845
.iter()
18331846
.map(|utxo| commit(Tip5::hash(utxo), block1.hash(), guesser_preimage.hash()))
18341847
.collect_vec();
@@ -1837,7 +1850,7 @@ pub(crate) mod tests {
18371850
let MutatorSetUpdate {
18381851
removals: _,
18391852
additions,
1840-
} = block1.mutator_set_update();
1853+
} = block1.mutator_set_update().unwrap();
18411854
assert!(
18421855
ars.iter().all(|ar| additions.contains(ar)),
18431856
"All addition records must be present in block's mutator set update"
@@ -1858,7 +1871,7 @@ pub(crate) mod tests {
18581871
let preimage = rand::rng().random::<Digest>();
18591872
block.set_header_guesser_digest(preimage.hash());
18601873

1861-
let guesser_fee_utxos = block.guesser_fee_utxos();
1874+
let guesser_fee_utxos = block.guesser_fee_utxos().unwrap();
18621875

18631876
let lock_script_and_witness =
18641877
HashLockKey::from_preimage(preimage).lock_script_and_witness();
@@ -1881,7 +1894,7 @@ pub(crate) mod tests {
18811894
let network = Network::Main;
18821895
let genesis_block = Block::genesis(network);
18831896
assert!(
1884-
genesis_block.guesser_fee_utxos().is_empty(),
1897+
genesis_block.guesser_fee_utxos().unwrap().is_empty(),
18851898
"Genesis block has no guesser fee UTXOs"
18861899
);
18871900

@@ -1945,7 +1958,7 @@ pub(crate) mod tests {
19451958
let mut ms = block1.body().mutator_set_accumulator.clone();
19461959

19471960
let mutator_set_update_guesser_fees =
1948-
MutatorSetUpdate::new(vec![], block1.guesser_fee_addition_records());
1961+
MutatorSetUpdate::new(vec![], block1.guesser_fee_addition_records().unwrap());
19491962
let mut mutator_set_update_tx = MutatorSetUpdate::new(
19501963
block2.body().transaction_kernel.inputs.clone(),
19511964
block2.body().transaction_kernel.outputs.clone(),

0 commit comments

Comments
 (0)