Skip to content

Commit d29c1f4

Browse files
authored
Merge pull request #1164 from AntelopeIO/validate_action_mroot
Validate finality mroot of Savanna blocks as a part of header validation
2 parents 5b2d6df + b2608a0 commit d29c1f4

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

libraries/chain/controller.cpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -3959,7 +3959,7 @@ struct controller_impl {
39593959
// Called from net-threads. It is thread safe as signed_block is never modified after creation.
39603960
// -----------------------------------------------------------------------------
39613961
std::optional<qc_t> verify_basic_proper_block_invariants( const block_id_type& id, const signed_block_ptr& b,
3962-
const block_header_state& prev ) {
3962+
const block_state& prev ) {
39633963
assert(b->is_proper_svnn_block());
39643964

39653965
auto qc_ext_id = quorum_certificate_extension::extension_id();
@@ -4054,6 +4054,20 @@ struct controller_impl {
40544054
"QC is_strong (${s1}) in block extension does not match is_strong_qc (${s2}) in header extension. Block number: ${b}",
40554055
("s1", qc_proof.is_strong())("s2", new_qc_claim.is_strong_qc)("b", block_num) );
40564056

4057+
// `valid` structure can be modified while this function is running on net thread.
4058+
// Use is_valid() instead. It uses atomic `validated` and when it is true, `valid`
4059+
// has been constructed.
4060+
if (prev.is_valid()) {
4061+
assert(prev.valid);
4062+
4063+
// compute finality mroot using previous block state and new qc claim
4064+
auto computed_finality_mroot = prev.get_finality_mroot_claim(new_qc_claim);
4065+
const auto& supplied_finality_mroot = b->action_mroot;
4066+
EOS_ASSERT( computed_finality_mroot == supplied_finality_mroot, block_validate_exception,
4067+
"computed finality mroot (${computed}) does not match supplied finality mroot ${supplied} by header extension. Block number: ${b}, block id: ${id}",
4068+
("computed", computed_finality_mroot)("supplied", supplied_finality_mroot)("b", block_num)("id", id) );
4069+
}
4070+
40574071
return std::optional{qc_proof};
40584072
}
40594073

unittests/block_tests.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -421,4 +421,28 @@ BOOST_FIXTURE_TEST_CASE( invalid_qc_claim_block_num_test, validating_tester ) {
421421
}) ;
422422
}
423423

424+
// Verify that a block with an invalid action mroot is rejected
425+
BOOST_FIXTURE_TEST_CASE( invalid_action_mroot_test, tester )
426+
{
427+
// Create a block with transaction
428+
create_account("newacc"_n);
429+
auto b = produce_block();
430+
431+
// Make a copy of the block and corrupt its action mroot
432+
auto copy_b = b->clone();
433+
copy_b->action_mroot = digest_type::hash("corrupted");
434+
435+
// Re-sign the block
436+
copy_b->producer_signature = get_private_key(config::system_account_name, "active").sign(copy_b->calculate_id());
437+
438+
// Push the block containing corruptted action mroot. It should fail
439+
BOOST_REQUIRE_EXCEPTION(push_block(signed_block::create_signed_block(std::move(copy_b))),
440+
fc::exception,
441+
[] (const fc::exception &e)->bool {
442+
return e.code() == block_validate_exception::code_value &&
443+
e.to_detail_string().find("computed finality mroot") != std::string::npos &&
444+
e.to_detail_string().find("does not match supplied finality mroot") != std::string::npos;
445+
});
446+
}
447+
424448
BOOST_AUTO_TEST_SUITE_END()

unittests/forked_tests_savanna.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,10 @@ BOOST_FIXTURE_TEST_CASE(fork_with_bad_block_savanna, savanna_cluster::cluster_t)
115115
if (j <= i) {
116116
auto copy_b = b->clone();
117117
if (j == i) {
118-
// corrupt this block (forks[j].blocks[j] is corrupted)
119-
copy_b->action_mroot._hash[0] ^= 0x1ULL;
118+
// Corrupt this block (forks[j].blocks[j] is corrupted).
119+
// Do not corrupt the block by modifying action_mroot, as action_mroot is checked
120+
// by block header validation, _nodes[0].push_block(b) would fail.
121+
copy_b->confirmed++;
120122
} else if (j < i) {
121123
// link to a corrupted chain (fork.blocks[j] was corrupted)
122124
copy_b->previous = fork.blocks.back()->calculate_id();
@@ -177,7 +179,7 @@ BOOST_FIXTURE_TEST_CASE(fork_with_bad_block_savanna, savanna_cluster::cluster_t)
177179

178180
// push the block which should attempt the corrupted fork and fail
179181
BOOST_REQUIRE_EXCEPTION(_nodes[0].push_block(fork.blocks.back()), fc::exception,
180-
fc_exception_message_starts_with( "finality_mroot does not match"));
182+
fc_exception_message_starts_with( "Block ID does not match"));
181183
BOOST_REQUIRE_EQUAL(_nodes[0].head().id(), node0_head);
182184
}
183185
}

0 commit comments

Comments
 (0)