From c3aa9a523142f39f2e3e64938c87cb42f232bebf Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 14 Mar 2024 16:54:16 +0000 Subject: [PATCH 01/17] added IF ibc contract + test --- unittests/if_ibc_tests.cpp | 212 ++++++++ unittests/test-contracts/CMakeLists.txt | 1 + .../test-contracts/if_ibc/CMakeLists.txt | 8 + unittests/test-contracts/if_ibc/bitset.hpp | 46 ++ unittests/test-contracts/if_ibc/if_ibc.abi | 366 +++++++++++++ unittests/test-contracts/if_ibc/if_ibc.cpp | 252 +++++++++ unittests/test-contracts/if_ibc/if_ibc.hpp | 489 ++++++++++++++++++ unittests/test-contracts/if_ibc/if_ibc.wasm | Bin 0 -> 45894 bytes unittests/test_contracts.hpp.in | 1 + 9 files changed, 1375 insertions(+) create mode 100644 unittests/if_ibc_tests.cpp create mode 100644 unittests/test-contracts/if_ibc/CMakeLists.txt create mode 100644 unittests/test-contracts/if_ibc/bitset.hpp create mode 100644 unittests/test-contracts/if_ibc/if_ibc.abi create mode 100644 unittests/test-contracts/if_ibc/if_ibc.cpp create mode 100644 unittests/test-contracts/if_ibc/if_ibc.hpp create mode 100755 unittests/test-contracts/if_ibc/if_ibc.wasm diff --git a/unittests/if_ibc_tests.cpp b/unittests/if_ibc_tests.cpp new file mode 100644 index 0000000000..f43450f23a --- /dev/null +++ b/unittests/if_ibc_tests.cpp @@ -0,0 +1,212 @@ +// From fork tests +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +#include "fork_test_utilities.hpp" + +using namespace eosio::chain; +using namespace eosio::testing; + +// From params_test +#include +using mvo = mutable_variant_object; + + +BOOST_AUTO_TEST_SUITE(if_ibc) + +// These are the producers and finalizers to use across all chains +const std::vector test_nodes = +{ + "a"_n, "b"_n, "c"_n, "d"_n, "e"_n, + "f"_n, "g"_n, "h"_n, "i"_n, "j"_n, + "k"_n, "l"_n, "m"_n, "n"_n, "o"_n, + "p"_n, "q"_n, "r"_n, "s"_n, "t"_n, + "u"_n +}; + +// Extending the default chain tester +// ( libraries/testing/include/eosio/testing/tester.hpp ) +class ibc_tester : public tester { +public: + const account_name _bridge = "bridge"_n; + + // This is mostly for creating accounts and loading contracts. + void setup(){ + + // load bridge contract + create_account( _bridge ); + set_code( _bridge, eosio::testing::test_contracts::if_ibc_wasm()); + set_abi( _bridge, eosio::testing::test_contracts::if_ibc_abi()); + + } + + void set_policy(){ + auto cr = push_action( _bridge, "setfpolicy"_n, _bridge, mutable_variant_object() + ("from_block_num", 1) + ("policy", mutable_variant_object() + ("generation", 1) + ("fthreshold", 3) + ("last_block_num", 0) + ("finalizers", fc::variants({ + mutable_variant_object() + ("description","finalizer1") + ("fweight", 1) + ("public_key", "b12eba13063c6cdc7bbe40e7de62a1c0f861a9ad55e924cdd5049be9b58e205053968179cede5be79afdcbbb90322406aefb7a5ce64edc2a4482d8656daed1eeacfb4286f661c0f9117dcd83fad451d301b2310946e5cd58808f7b441b280a02") + , + mutable_variant_object() + ("description","finalizer2") + ("fweight", 1) + ("public_key", "0728121cffe7b8ddac41817c3a6faca76ae9de762d9c26602f936ac3e283da756002d3671a2858f54c355f67b31b430b23b957dba426d757eb422db617be4cc13daf41691aa059b0f198fa290014d3c3e4fa1def2abc6a3328adfa7705c75508") + , + mutable_variant_object() + ("description","finalizer3") + ("fweight", 1) + ("public_key", "e06c31c83f70b4fe9507877563bfff49235774d94c98dbf9673d61d082ef589f7dd4865281f37d60d1bb433514d4ef0b787424fb5e53472b1d45d28d90614fad29a4e5e0fe70ea387f7845e22c843f6061f9be20a7af21d8b72d02f4ca494a0a") + , + mutable_variant_object() + ("description","finalizer4") + ("fweight", 1) + ("public_key", "08c9bd408bac02747e493d918e4b3e6bd1a2ffaf9bfca4f2e79dd22e12556bf46e911f25613c24d9f6403996c5246c19ef94aff48094868425eda1e46bcd059c59f3b060521be797f5cc2e6debe2180efa12c0814618a38836a64c3d7440740f") + })) + ) + + ); + } + +/* void check_proof(){ + + auto cr = push_action( _bridge, "checkproof"_n, _bridge, mutable_variant_object() + ("proof", mutable_variant_object() + ("finality_proof", mutable_variant_object() + ("qc_block", mutable_variant_object() + ("light_header_protocol_version_major", 1) + ("light_header_protocol_version_minor", 0) + ("finalizer_policy_generation", 1) + ("active_finalizer_policy", fc::variants()) + ("witness_hash", "888ceeb757ea240d1c1ae2f4f717e67b73dcd592b2ba097f63b4c3e3ca4350e1") + ("finality_mroot", "1d2ab7379301370d3fa1b27a9f4ac077f6ea445a1aa3dbf7e18e9cc2c25b140c") + ) + ("qc", mutable_variant_object() + ("signature", "") + ("finalizers", fc::variants()) + ) + ) + ("target_block_proof_of_inclusion", mutable_variant_object() + ("target_node_index", 7) + ("last_node_index", 7) + ("target", mutable_variant_object() + ("finality_data", mutable_variant_object() + ("major_version", 1) + ("minor_version", 0) + ("finalizer_policy_generation", 1) + ("active_finalizer_policy", fc::variants()) + ("witness_hash", "dff620c1c4d31cade95ed609269a86d4ecb2357f9302d17675c0665c75786508") + ("finality_mroot", "1397eb7c86719f160188fa740fc3610ccb5a6681ad56807dc99a17fe73a7b7fd") + ) + ("dynamic_data", mutable_variant_object() + ("block_num", 28) + ("action_proofs", fc::variants()) + ("action_mroot", "4e890ef0e014f93bd1b31fabf1041ecc9fb1c44e957c2f7b1682333ee426677a") + ) + ) + ("merkle_branches", fc::variants({ + mutable_variant_object() + ("direction", 1) + ("hash", "4e17da018040c80339f2714828d1927d5b616f9af7aa4768c1876df6f05e5602") + , + mutable_variant_object() + ("direction", 1) + ("hash", "7ee0e16f1941fb5a98d80d20ca92e0c689e9284285d5f90ecd4f8f1ea2ffb53c") + , + mutable_variant_object() + ("direction", 1) + ("hash", "401526ba03ec4a955c83cda131dacd3e89becaad2cf04107170e436dd90a553f") + })) + + ) + ) + + ); + + } + */ + void check_proof(){ + + auto cr = push_action( _bridge, "checkproof"_n, _bridge, mutable_variant_object() + ("proof", mutable_variant_object() + ("finality_proof", mutable_variant_object() + ("qc_block", mutable_variant_object() + ("major_version", 1) + ("minor_version", 0) + ("finalizer_policy_generation", 1) + ("witness_hash", "888ceeb757ea240d1c1ae2f4f717e67b73dcd592b2ba097f63b4c3e3ca4350e1") + ("finality_mroot", "1d2ab7379301370d3fa1b27a9f4ac077f6ea445a1aa3dbf7e18e9cc2c25b140c") + ) + ("qc", mutable_variant_object() + ("signature", "") + ("finalizers", fc::variants({})) + ) + ) + ("target_block_proof_of_inclusion", mutable_variant_object() + ("target_node_index", 7) + ("last_node_index", 7) + ("target", fc::variants({"block_data", mutable_variant_object() + ("finality_data", mutable_variant_object() + ("major_version", 1) + ("minor_version", 0) + ("finalizer_policy_generation", 1) + ("witness_hash", "dff620c1c4d31cade95ed609269a86d4ecb2357f9302d17675c0665c75786508") + ("finality_mroot", "1397eb7c86719f160188fa740fc3610ccb5a6681ad56807dc99a17fe73a7b7fd") + ) + ("dynamic_data", mutable_variant_object() + ("block_num", 28) + ("action_proofs", fc::variants()) + ("action_mroot", "4e890ef0e014f93bd1b31fabf1041ecc9fb1c44e957c2f7b1682333ee426677a") + ) + })) + ("merkle_branches", fc::variants({ + mutable_variant_object() + ("direction", 1) + ("hash", "4e17da018040c80339f2714828d1927d5b616f9af7aa4768c1876df6f05e5602") + , + mutable_variant_object() + ("direction", 1) + ("hash", "7ee0e16f1941fb5a98d80d20ca92e0c689e9284285d5f90ecd4f8f1ea2ffb53c") + , + mutable_variant_object() + ("direction", 1) + ("hash", "401526ba03ec4a955c83cda131dacd3e89becaad2cf04107170e436dd90a553f") + })) + + ) + ) + + ); + + } + +}; + +BOOST_AUTO_TEST_CASE( first_test ) try { + + ibc_tester chain_a; + + chain_a.setup(); + + chain_a.set_policy(); + + chain_a.check_proof(); + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/unittests/test-contracts/CMakeLists.txt b/unittests/test-contracts/CMakeLists.txt index a62d82809c..1296344f30 100644 --- a/unittests/test-contracts/CMakeLists.txt +++ b/unittests/test-contracts/CMakeLists.txt @@ -45,3 +45,4 @@ add_subdirectory( crypto_primitives_test ) add_subdirectory( bls_primitives_test ) add_subdirectory( get_block_num_test ) add_subdirectory( nested_container_multi_index ) +add_subdirectory( if_ibc ) diff --git a/unittests/test-contracts/if_ibc/CMakeLists.txt b/unittests/test-contracts/if_ibc/CMakeLists.txt new file mode 100644 index 0000000000..427c77aa9f --- /dev/null +++ b/unittests/test-contracts/if_ibc/CMakeLists.txt @@ -0,0 +1,8 @@ +set( IBC_CONTRACT "if_ibc" ) + +if( EOSIO_COMPILE_TEST_CONTRACTS ) + add_contract( ${IBC_CONTRACT} ${IBC_CONTRACT} ${IBC_CONTRACT}.cpp ) +else() + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${IBC_CONTRACT}.wasm ${CMAKE_CURRENT_BINARY_DIR}/${IBC_CONTRACT}.wasm COPYONLY ) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${IBC_CONTRACT}.abi ${CMAKE_CURRENT_BINARY_DIR}/${IBC_CONTRACT}.abi COPYONLY ) +endif() diff --git a/unittests/test-contracts/if_ibc/bitset.hpp b/unittests/test-contracts/if_ibc/bitset.hpp new file mode 100644 index 0000000000..e91cf55a50 --- /dev/null +++ b/unittests/test-contracts/if_ibc/bitset.hpp @@ -0,0 +1,46 @@ +using namespace eosio; + +class bitset { +public: + bitset(size_t size) + : num_bits(size), data(new uint64_t[(size + 63) / 64]()) {} + + bitset(size_t size, uint64_t* _data) + : num_bits(size), data(_data) {} + + ~bitset() { + delete[] data; + } + + // Set a bit to 1 + void set(size_t index) { + check_bounds(index); + data[index / 64] |= (1ULL << (index % 64)); + } + + // Clear a bit (set to 0) + void clear(size_t index) { + check_bounds(index); + data[index / 64] &= ~(1ULL << (index % 64)); + } + + // Check if a bit is set + bool test(size_t index) const { + check_bounds(index); + return (data[index / 64] & (1ULL << (index % 64))) != 0; + } + + // Size of the bitset + size_t size() const { + return num_bits; + } + +private: + size_t num_bits; + uint64_t* data; + + // Check if the index is within bounds + void check_bounds(size_t index) const { + check(index < num_bits, "bitset index out of bounds"); + } +}; diff --git a/unittests/test-contracts/if_ibc/if_ibc.abi b/unittests/test-contracts/if_ibc/if_ibc.abi new file mode 100644 index 0000000000..a6737df603 --- /dev/null +++ b/unittests/test-contracts/if_ibc/if_ibc.abi @@ -0,0 +1,366 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.2", + "types": [ + { + "new_type_name": "bls_public_key", + "type": "bytes" + }, + { + "new_type_name": "bls_signature", + "type": "bytes" + }, + { + "new_type_name": "target_data", + "type": "variant_block_data_action_data" + } + ], + "structs": [ + { + "name": "action_data", + "base": "", + "fields": [ + { + "name": "action", + "type": "r_action" + }, + { + "name": "action_receipt_digest", + "type": "checksum256" + }, + { + "name": "return_value", + "type": "bytes" + } + ] + }, + { + "name": "block_data", + "base": "", + "fields": [ + { + "name": "finality_data", + "type": "block_finality_data" + }, + { + "name": "dynamic_data", + "type": "dynamic_data_v0" + } + ] + }, + { + "name": "block_finality_data", + "base": "", + "fields": [ + { + "name": "major_version", + "type": "uint32" + }, + { + "name": "minor_version", + "type": "uint32" + }, + { + "name": "finalizer_policy_generation", + "type": "uint32" + }, + { + "name": "active_finalizer_policy", + "type": "fpolicy?" + }, + { + "name": "witness_hash", + "type": "checksum256" + }, + { + "name": "finality_mroot", + "type": "checksum256" + } + ] + }, + { + "name": "checkproof", + "base": "", + "fields": [ + { + "name": "proof", + "type": "proof" + } + ] + }, + { + "name": "clear", + "base": "", + "fields": [] + }, + { + "name": "dynamic_data_v0", + "base": "", + "fields": [ + { + "name": "block_num", + "type": "uint32" + }, + { + "name": "action_proofs", + "type": "proof_of_inclusion[]" + }, + { + "name": "action_mroot", + "type": "checksum256?" + } + ] + }, + { + "name": "finality_proof", + "base": "", + "fields": [ + { + "name": "qc_block", + "type": "block_finality_data" + }, + { + "name": "qc", + "type": "quorum_certificate" + } + ] + }, + { + "name": "finalizer_authority", + "base": "", + "fields": [ + { + "name": "description", + "type": "string" + }, + { + "name": "fweight", + "type": "uint64" + }, + { + "name": "public_key", + "type": "bls_public_key" + } + ] + }, + { + "name": "fpolicy", + "base": "", + "fields": [ + { + "name": "generation", + "type": "uint32" + }, + { + "name": "fthreshold", + "type": "uint64" + }, + { + "name": "finalizers", + "type": "finalizer_authority[]" + } + ] + }, + { + "name": "lastproof", + "base": "", + "fields": [ + { + "name": "id", + "type": "uint64" + }, + { + "name": "block_num", + "type": "uint32" + }, + { + "name": "finality_mroot", + "type": "checksum256" + }, + { + "name": "cache_expiry", + "type": "time_point" + } + ] + }, + { + "name": "merkle_branch", + "base": "", + "fields": [ + { + "name": "direction", + "type": "uint8" + }, + { + "name": "hash", + "type": "checksum256" + } + ] + }, + { + "name": "permission_level", + "base": "", + "fields": [ + { + "name": "actor", + "type": "name" + }, + { + "name": "permission", + "type": "name" + } + ] + }, + { + "name": "proof", + "base": "", + "fields": [ + { + "name": "finality_proof", + "type": "finality_proof?" + }, + { + "name": "target_block_proof_of_inclusion", + "type": "proof_of_inclusion" + } + ] + }, + { + "name": "proof_of_inclusion", + "base": "", + "fields": [ + { + "name": "target_node_index", + "type": "uint64" + }, + { + "name": "last_node_index", + "type": "uint64" + }, + { + "name": "target", + "type": "target_data" + }, + { + "name": "merkle_branches", + "type": "merkle_branch[]" + } + ] + }, + { + "name": "quorum_certificate", + "base": "", + "fields": [ + { + "name": "finalizers", + "type": "uint64[]" + }, + { + "name": "signature", + "type": "bls_signature" + } + ] + }, + { + "name": "r_action", + "base": "r_action_base", + "fields": [ + { + "name": "data", + "type": "bytes" + }, + { + "name": "returnvalue", + "type": "bytes" + } + ] + }, + { + "name": "r_action_base", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "name", + "type": "name" + }, + { + "name": "authorization", + "type": "permission_level[]" + } + ] + }, + { + "name": "setfpolicy", + "base": "", + "fields": [ + { + "name": "policy", + "type": "fpolicy" + }, + { + "name": "from_block_num", + "type": "uint32" + } + ] + }, + { + "name": "storedpolicy", + "base": "fpolicy", + "fields": [ + { + "name": "last_block_num", + "type": "uint32" + }, + { + "name": "cache_expiry", + "type": "time_point" + } + ] + } + ], + "actions": [ + { + "name": "checkproof", + "type": "checkproof", + "ricardian_contract": "" + }, + { + "name": "clear", + "type": "clear", + "ricardian_contract": "" + }, + { + "name": "setfpolicy", + "type": "setfpolicy", + "ricardian_contract": "" + } + ], + "tables": [ + { + "name": "lastproofs", + "type": "lastproof", + "index_type": "i64", + "key_names": [], + "key_types": [] + }, + { + "name": "policies", + "type": "storedpolicy", + "index_type": "i64", + "key_names": [], + "key_types": [] + } + ], + "ricardian_clauses": [], + "variants": [ + { + "name": "variant_block_data_action_data", + "types": ["block_data","action_data"] + } + ], + "action_results": [] +} \ No newline at end of file diff --git a/unittests/test-contracts/if_ibc/if_ibc.cpp b/unittests/test-contracts/if_ibc/if_ibc.cpp new file mode 100644 index 0000000000..ae54aaee71 --- /dev/null +++ b/unittests/test-contracts/if_ibc/if_ibc.cpp @@ -0,0 +1,252 @@ +#include "if_ibc.hpp" + + +//add two numbers from the g1 group (aggregation) +std::vector if_ibc::_g1add(const std::vector& op1, const std::vector& op2) { + check(op1.size() == std::tuple_size::value, "wrong op1 size passed"); + check(op2.size() == std::tuple_size::value, "wrong op2 size passed"); + bls_g1 r; + bls_g1_add(*reinterpret_cast(op1.data()), *reinterpret_cast(op2.data()), r); + std::vector v(r.begin(), r.end()); + return v; +} + +void if_ibc::_maybe_set_finalizer_policy(const fpolicy& policy, const uint32_t from_block_num){ + + policies_table _policies_table(get_self(), get_self().value); + + auto itr = _policies_table.rbegin(); + + //if the new policy is more recent than the most recent we are aware of, we record the new one + if (itr==_policies_table.rend() || itr->generation < policy.generation){ + + //if a previous policy was in force, it is now superseded by the newer one for any future proof verification + if (itr!=_policies_table.rend()){ + auto fwd_itr = itr.base(); + fwd_itr--; + _policies_table.modify( fwd_itr, same_payer, [&]( auto& sfp ) { + sfp.last_block_num = from_block_num; + }); + } + + if_ibc::storedpolicy spolicy; + + spolicy.generation = policy.generation; + spolicy.fthreshold = policy.fthreshold; + spolicy.finalizers = policy.finalizers; + + //policy is in force until a newer policy is proven + spolicy.last_block_num = std::numeric_limits::max(); + + //set cache expiry + spolicy.cache_expiry = add_time(current_time_point(), POLICY_CACHE_EXPIRY); + + _policies_table.emplace( get_self(), [&]( auto& p ) { + p = spolicy; + }); + + + } + +} + +//adds the newly proven root if necessary +void if_ibc::_maybe_add_proven_root(const uint32_t block_num, const checksum256& finality_mroot){ + + proofs_table _proofs_table(get_self(), get_self().value); + + auto block_num_index = _proofs_table.get_index<"blocknum"_n>(); + auto merkle_index = _proofs_table.get_index<"merkleroot"_n>(); + + auto last_itr = block_num_index.rbegin(); + + //if first proven root or newer than the last proven root, we store it + if (last_itr == block_num_index.rend() || last_itr->block_num& pk, const std::vector& sig, std::vector& msg){ + check(pk.size() == std::tuple_size::value, "wrong pk size passed"); + check(sig.size() == std::tuple_size::value, "wrong sig size passed"); + bls_g1 g1_points[2]; + bls_g2 g2_points[2]; + + memcpy(g1_points[0].data(), eosio::detail::G1_ONE_NEG.data(), sizeof(bls_g1)); + memcpy(g2_points[0].data(), sig.data(), sizeof(bls_g2)); + + bls_g2 p_msg; + eosio::detail::g2_fromMessage(msg, eosio::detail::CIPHERSUITE_ID, p_msg); + memcpy(g1_points[1].data(), pk.data(), sizeof(bls_g1)); + memcpy(g2_points[1].data(), p_msg.data(), sizeof(bls_g2)); + + bls_gt r; + bls_pairing(g1_points, g2_points, 2, r); + check(0 == memcmp(r.data(), eosio::detail::GT_ONE.data(), sizeof(bls_gt)), "bls signature verify failed"); +} + +void if_ibc::_check_qc(const quorum_certificate& qc, const checksum256& finality_digest, const uint64_t finalizer_policy_generation){ + + policies_table _policies_table(get_self(), get_self().value); + check(_policies_table.begin() != _policies_table.end(), "must set a finalizer policy before checking proofs"); + + //fetch the finalizer policy where generation num is equal prepare.input.finalizer_policy_generation, and that it is still in force + auto itr = _policies_table.find(finalizer_policy_generation); + check(itr!=_policies_table.end(), "finalizer policy not found"); + storedpolicy target_policy = *itr; + auto fa_itr = target_policy.finalizers.begin(); + auto fa_end_itr = target_policy.finalizers.end(); + size_t finalizer_count = std::distance(fa_itr, fa_end_itr); + std::vector bitset_data(qc.finalizers); + bitset b(finalizer_count, bitset_data.data()); + + bool first = true; + + size_t index = 0; + uint64_t weight = 0; + + bls_public_key agg_pub_key; + + while (fa_itr != fa_end_itr){ + if (b.test(index)){ + if (first){ + first=false; + agg_pub_key = fa_itr->public_key; + } + else agg_pub_key = _g1add(agg_pub_key, fa_itr->public_key); + weight+=fa_itr->fweight; + + } + index++; + fa_itr++; + } + + //verify that we have enough vote weight to meet the quorum threshold of the target policy + check(weight>=target_policy.fthreshold, "insufficient signatures to reach quorum"); + + std::array data = finality_digest.extract_as_byte_array(); + std::vector v_data(data.begin(), data.end()); + + //verify signature validity + _verify(agg_pub_key, qc.signature, v_data); + + +} + +void if_ibc::_check_target_block_proof_of_inclusion(const proof_of_inclusion& proof, const std::optional reference_root){ + + //verify that the proof of inclusion is over a target block + check(std::holds_alternative(proof.target), "must supply proof of inclusion over block data"); + + //resolve the proof to its merkle root + checksum256 finality_mroot = proof.root(); + + if (reference_root.has_value()){ + check(reference_root.value() == finality_mroot, "cannot link proof to proven merkle root"); + } + else { + + proofs_table _proofs_table(get_self(), get_self().value); + + auto merkle_index = _proofs_table.get_index<"merkleroot"_n>(); + + auto itr = merkle_index.find(finality_mroot); + check(itr!= merkle_index.end(), "cannot link proof to proven merkle root"); + + } + + block_data target_block = std::get(proof.target); + + if (target_block.finality_data.active_finalizer_policy.has_value()){ + _maybe_set_finalizer_policy(target_block.finality_data.active_finalizer_policy.value(), target_block.dynamic_data.block_num); + } + +} + +void if_ibc::_check_finality_proof(const finality_proof& finality_proof, const proof_of_inclusion& target_block_proof_of_inclusion){ + + //temporarilly disabled : skip qc verification + //if QC is valid, it means that we have reaced finality on the block referenced by the finality_mroot + //_check_qc(finality_proof.qc, finality_proof.qc_block.finality_digest(), finality_proof.qc_block.finalizer_policy_generation); + + //check if the target proof of inclusion correctly resolves to the root of the finality proof + _check_target_block_proof_of_inclusion(target_block_proof_of_inclusion, finality_proof.qc_block.finality_mroot); + + //if proof of inclusion was successful, the target block and its dynamic data have been validated as final and correct + block_data target_block = std::get(target_block_proof_of_inclusion.target); + + //if the finality_mroot we just proven is more recent than the last root we have stored, store it + _maybe_add_proven_root(target_block.dynamic_data.block_num, finality_proof.qc_block.finality_mroot); + +} + +ACTION if_ibc::setfpolicy(const fpolicy& policy, const uint32_t from_block_num){ + + //can only be called with account authority + require_auth(get_self()); + + policies_table _policies_table(get_self(), get_self().value); + + //can only be used once for the initilization of the contract + check(_policies_table.begin() == _policies_table.end(), "can only set finalizer policy manually for initialization"); + + _maybe_set_finalizer_policy(policy, from_block_num); + +} + +ACTION if_ibc::checkproof(const proof& proof){ + + //if we have a finality proof, we execute the "heavy" code path + if (proof.finality_proof.has_value()){ + _check_finality_proof(proof.finality_proof.value(), proof.target_block_proof_of_inclusion); + } + else { + //if we only have a proof of inclusion of the target block, we execute the "light" code path + _check_target_block_proof_of_inclusion(proof.target_block_proof_of_inclusion, std::nullopt); + } + +} + +//temporary : reset the state +ACTION if_ibc::clear(){ + + require_auth(get_self()); + + proofs_table _proofs_table(get_self(), get_self().value); + policies_table _policies_table(get_self(), get_self().value); + + auto fp_itr = _proofs_table.begin(); + + while (fp_itr!= _proofs_table.end()){ + fp_itr = _proofs_table.erase(fp_itr); + } + + auto p_itr = _policies_table.begin(); + + while (p_itr!= _policies_table.end()){ + p_itr = _policies_table.erase(p_itr); + } + + +} \ No newline at end of file diff --git a/unittests/test-contracts/if_ibc/if_ibc.hpp b/unittests/test-contracts/if_ibc/if_ibc.hpp new file mode 100644 index 0000000000..2c70cf5c15 --- /dev/null +++ b/unittests/test-contracts/if_ibc/if_ibc.hpp @@ -0,0 +1,489 @@ +#include +#include +#include +#include +#include + +#include "bitset.hpp" + +#include +#include + +using namespace eosio; + +CONTRACT if_ibc : public contract { + public: + using contract::contract; + + using bls_public_key = std::vector; + using bls_signature = std::vector; + + const uint32_t POLICY_CACHE_EXPIRY = 600; //10 minutes for testing + const uint32_t PROOF_CACHE_EXPIRY = 600; //10 minutes for testing + + //Compute the next power of 2 for a given number + static uint64_t next_power_of_2(uint64_t value) { + value -= 1; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + value += 1; return value; + } + + static uint64_t clz_power_of_2(uint64_t v) { + return 64 - (__builtin_clzll(v)+1); + } + + //Compute the maximum number of layers of a merkle tree for a given number of leaves + static uint64_t calculate_max_depth(uint64_t node_count) { + if (node_count == 0) return 0; + auto implied_count = next_power_of_2(node_count); + return clz_power_of_2(implied_count) + 1; + } + + static uint32_t reverse_bytes(const uint32_t input){ + uint32_t output = (input>>24 & 0xff)|(input>>8 & 0xff00)|(input<<8 & 0xff0000)|(input<<24 & 0xff000000); + return output; + } + + static checksum256 hash_pair(const std::pair p){ + std::array arr1 = p.first.extract_as_byte_array(); + std::array arr2 = p.second.extract_as_byte_array(); + std::array result; + std::copy (arr1.cbegin(), arr1.cend(), result.begin()); + std::copy (arr2.cbegin(), arr2.cend(), result.begin() + 32); + checksum256 hash = sha256(reinterpret_cast(result.data()), 64); + return hash; + } + + static time_point add_time(const time_point& time, const uint32_t seconds ){ + int64_t total_seconds = (static_cast(time.sec_since_epoch()) + static_cast(seconds)); + microseconds ms = microseconds(total_seconds * 1000000); + time_point tp = time_point(ms); + return tp; + } + + /* + + //discuss : compute merkle branch direction vs providing them as part of the proof + + static std::vector _get_directions(const uint64_t index, const uint64_t last_node_index){ + + std::vector proof; + + uint64_t c_index = index; + uint64_t layers_depth = calculate_max_depth(last_node_index) -1; + uint64_t c_last_node_index = last_node_index; + + for (uint64_t i = 0; i < layers_depth; i++) { + if (c_last_node_index % 2) c_last_node_index+=1; + + bool isLeft = c_index % 2 == 0 ? 0 : 1; + uint64_t pairIndex = isLeft ? c_index - 1 : + c_index == last_node_index - 1 && i < layers_depth - 1 ? c_index : + c_index + 1; + + c_last_node_index/=2; + if (pairIndex < last_node_index) proof.push_back(isLeft); + + c_index = c_index / 2; + + } + + return proof; + + } + + */ + + struct merkle_branch { + + uint8_t direction; + checksum256 hash; + + }; + + + //compute the merkle root of target node and vector of merkle branches + static checksum256 _compute_root(const std::vector proof_nodes, const checksum256& target){ + + checksum256 hash = target; + + for (int i = 0 ; i < proof_nodes.size() ; i++){ + const checksum256 node = proof_nodes[i].hash; + std::array arr = node.extract_as_byte_array(); + + if (proof_nodes[i].direction == 0){ + hash = hash_pair(std::make_pair(hash, node)); + } + else { + hash = hash_pair(std::make_pair(node, hash)); + } + + } + + return hash; + + } + + struct quorum_certificate { + std::vector finalizers; + bls_signature signature; + }; + + struct finalizer_authority { + std::string description; + uint64_t fweight = 0; + bls_public_key public_key; + }; + + struct fpolicy { + + uint32_t generation = 0; ///< sequentially incrementing version number + uint64_t fthreshold = 0; ///< vote fweight threshold to finalize blocks + std::vector finalizers; ///< Instant Finality voter set + + checksum256 digest() const { + std::vector serialized = pack(*this); + return sha256(serialized.data(), serialized.size()); + } + + }; + + //finalizer policy augmented with contextually-relevant data + TABLE storedpolicy : fpolicy { + + uint32_t last_block_num = 0; //last block number where this policy is in force + + time_point cache_expiry; //cache expiry + + uint64_t primary_key() const {return generation;} + uint64_t by_cache_expiry()const { return cache_expiry.sec_since_epoch(); } + + EOSLIB_SERIALIZE( storedpolicy, (generation)(fthreshold)(finalizers)(last_block_num)(cache_expiry)) + + }; + + TABLE lastproof { + + uint64_t id; + + uint32_t block_num; + + checksum256 finality_mroot; + + time_point cache_expiry; + + uint64_t primary_key()const { return id; } + uint64_t by_block_num()const { return block_num; } + uint64_t by_cache_expiry()const { return cache_expiry.sec_since_epoch(); } + checksum256 by_merkle_root()const { return finality_mroot; } + + }; + + struct authseq { + name account; + uint64_t sequence; + + EOSLIB_SERIALIZE( authseq, (account)(sequence) ) + + }; + + struct r_action_base { + name account; + name name; + std::vector authorization; + + }; + + struct r_action : r_action_base { + std::vector data; + std::vector returnvalue; + + checksum256 digest() const { + + checksum256 hashes[2]; + + const r_action_base* base = this; + + const auto action_input_size = pack_size(data); + const auto return_value_size = pack_size(returnvalue); + + const auto rhs_size = action_input_size + return_value_size; + + const auto serialized_base = pack(*base); + const auto serialized_data = pack(data); + const auto serialized_output = pack(returnvalue); + + hashes[0] = sha256(serialized_base.data(), serialized_base.size()); + + std::vector data_digest(action_input_size); + std::vector output_digest(return_value_size); + + std::vector h1_result(rhs_size); + std::copy (serialized_data.cbegin(), serialized_data.cend(), h1_result.begin()); + std::copy (serialized_output.cbegin(), serialized_output.cend(), h1_result.begin() + action_input_size); + + hashes[1] = sha256(reinterpret_cast(h1_result.data()), rhs_size); + + std::array arr1 = hashes[0].extract_as_byte_array(); + std::array arr2 = hashes[1].extract_as_byte_array(); + + std::array result; + std::copy (arr1.cbegin(), arr1.cend(), result.begin()); + std::copy (arr2.cbegin(), arr2.cend(), result.begin() + 32); + + checksum256 final_hash = sha256(reinterpret_cast(result.data()), 64); + + return final_hash; + + } + + EOSLIB_SERIALIZE( r_action, (account)(name)(authorization)(data)(returnvalue)) + + }; + + struct action_receipt { + + name receiver; + + //act_digest is provided instead by obtaining the action digest. Implementation depends on the activation of action_return_value feature + //checksum256 act_digest; + + uint64_t global_sequence = 0; + uint64_t recv_sequence = 0; + + std::vector auth_sequence; + unsigned_int code_sequence = 0; + unsigned_int abi_sequence = 0; + + EOSLIB_SERIALIZE( action_receipt, (receiver)(global_sequence)(recv_sequence)(auth_sequence)(code_sequence)(abi_sequence) ) + + }; + + struct proof_of_inclusion; + + struct dynamic_data_v0 { + + //block_num is always present + uint32_t block_num; + + //can include any number of action_proofs and / or state_proofs pertaining to a given block + //all action_proofs must resolve to the same action_mroot + std::vector action_proofs; + + //can be used instead of providing action_proofs. Useful for proving finalizer policy changes + std::optional action_mroot; + + checksum256 get_action_mroot() const { + + if (action_mroot.has_value()) return action_mroot.value(); + else { + + check(action_proofs.size()>0, "must have at least one action proof"); + + checksum256 root = checksum256(); + + for (auto ap : action_proofs){ + if (root == checksum256()) root = ap.root(); + else check(ap.root() == root, "all action proofs must resolve to the same merkle root"); + } + + return root; + + } + + }; + + }; + + struct block_finality_data { + + //major_version for this block + uint32_t major_version; + + //minor_version for this block + uint32_t minor_version; + + //finalizer_policy_generation for this block + uint32_t finalizer_policy_generation; + + //if the block to prove contains a finalizer policy change, it can be provided + std::optional active_finalizer_policy; + + //if a finalizer policy is present, witness_hash should be the base_digest. Otherwise, witness_hash should be the static_data_digest + checksum256 witness_hash; + + //final_on_qc for this block + checksum256 finality_mroot; + + //returns hash of digest of active_finalizer_policy + witness_hash if active_finalizer_policy is present, otherwise returns witness_hash + checksum256 resolve_witness() const { + if (active_finalizer_policy.has_value()){ + std::vector serialized_policy = pack(active_finalizer_policy.value()); + checksum256 policy_digest = sha256(serialized_policy.data(), serialized_policy.size()); + checksum256 base_fpolicy_digest = hash_pair( std::make_pair( policy_digest, witness_hash) ); + + return base_fpolicy_digest; + } + else { + return witness_hash; + } + }; + + //returns hash of major_version + minor_version + finalizer_policy_generation + resolve_witness() + finality_mroot + checksum256 finality_digest() const { + + std::array result; + + memcpy(&result[0], (uint8_t *)&major_version, 4); + memcpy(&result[4], (uint8_t *)&minor_version, 4); + memcpy(&result[8], (uint8_t *)&finalizer_policy_generation, 4); + + std::array arr1 = finality_mroot.extract_as_byte_array(); + std::array arr2 = resolve_witness().extract_as_byte_array(); + + std::copy (arr1.cbegin(), arr1.cend(), result.begin() + 12); + std::copy (arr2.cbegin(), arr2.cend(), result.begin() + 44); + + checksum256 hash = sha256(reinterpret_cast(result.data()), 76); + + return hash; + + }; + + }; + + struct block_data { + + //finality data + block_finality_data finality_data; + + //dynamic_data to be verified + dynamic_data_v0 dynamic_data; + + //returns hash of finality_digest() and dynamic_data_digest() + checksum256 digest() const { + + checksum256 finality_digest = finality_data.finality_digest(); + + checksum256 action_mroot = dynamic_data.get_action_mroot(); + + std::array result; + + memcpy(&result[0], (uint8_t *)&finality_data.major_version, 4); + memcpy(&result[4], (uint8_t *)&finality_data.minor_version, 4); + memcpy(&result[8], (uint8_t *)&dynamic_data.block_num, 4); + + std::array arr1 = finality_digest.extract_as_byte_array(); + std::array arr2 = action_mroot.extract_as_byte_array(); + + std::copy (arr1.cbegin(), arr1.cend(), result.begin() + 12); + std::copy (arr2.cbegin(), arr2.cend(), result.begin() + 44); + + auto hash = sha256(reinterpret_cast(result.data()), 76); + + return hash; + + }; + + }; + + struct action_data { + + r_action action; //antelope action + checksum256 action_receipt_digest; //required witness hash, actual action_receipt is irrelevant to IBC + + std::vector return_value; //empty if no return value + + //returns the action digest + checksum256 action_digest() const { + return action.digest(); + }; + + //returns the receipt digest, composed of the action_digest() and action_receipt_digest witness hash + checksum256 digest() const { + checksum256 action_receipt_digest = hash_pair( std::make_pair( action_digest(), action_receipt_digest) ); + return action_receipt_digest; + }; + + }; + + using target_data = std::variant; + + + struct proof_of_inclusion { + + uint64_t target_node_index; + uint64_t last_node_index; + + target_data target; + + std::vector merkle_branches; + + //returns the merkle root obtained by hashing target.digest() with merkle_branches + checksum256 root() const { + + auto call_digest = [](const auto& var) -> checksum256 { return var.digest(); }; + + checksum256 digest = std::visit(call_digest, target); + + checksum256 root = _compute_root(merkle_branches, digest); + + return root; + + }; + + }; + + struct finality_proof { + + //block finality data over which we validate a QC + block_finality_data qc_block; + + //signature over finality_digest() of qc_block. + quorum_certificate qc; + + }; + + struct proof { + + //valid configurations : + //1) finality_proof for a QC block, and proof_of_inclusion of a target block within the final_on_strong_qc block represented by the finality_mroot present in header + //2) only a proof_of_inclusion of a target block, which must be included in a merkle tree represented by a root stored in the contract's RAM + std::optional finality_proof; + proof_of_inclusion target_block_proof_of_inclusion; + + }; + + typedef eosio::multi_index< "policies"_n, storedpolicy, + indexed_by<"expiry"_n, const_mem_fun>> policies_table; + + typedef eosio::multi_index< "lastproofs"_n, lastproof, + indexed_by<"blocknum"_n, const_mem_fun>, + indexed_by<"merkleroot"_n, const_mem_fun>, + indexed_by<"expiry"_n, const_mem_fun>> proofs_table; + + std::vector _g1add(const std::vector& op1, const std::vector& op2); + + void _maybe_set_finalizer_policy(const fpolicy& policy, const uint32_t from_block_num); + void _maybe_add_proven_root(const uint32_t block_num, const checksum256& finality_mroot); + + void _garbage_collection(); + + void _verify(const std::vector& pk, const std::vector& sig, std::vector& msg); + void _check_qc(const quorum_certificate& qc, const checksum256& finality_mroot, const uint64_t finalizer_policy_generation); + + void _check_finality_proof(const finality_proof& finality_proof, const proof_of_inclusion& target_block_proof_of_inclusion); + void _check_target_block_proof_of_inclusion(const proof_of_inclusion& proof, const std::optional reference_root); + + ACTION setfpolicy(const fpolicy& policy, const uint32_t from_block_num); //set finality policy + ACTION checkproof(const proof& proof); + + //clearing function, to be removed for production version + ACTION clear(); + +}; \ No newline at end of file diff --git a/unittests/test-contracts/if_ibc/if_ibc.wasm b/unittests/test-contracts/if_ibc/if_ibc.wasm new file mode 100755 index 0000000000000000000000000000000000000000..8f5d1276fd27313442cbb613d217fcf0ab2a9181 GIT binary patch literal 45894 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 Date: Thu, 14 Mar 2024 18:05:05 +0000 Subject: [PATCH 02/17] Cleaned up if_ibc_tests.cpp --- unittests/if_ibc_tests.cpp | 67 -------------------------------------- 1 file changed, 67 deletions(-) diff --git a/unittests/if_ibc_tests.cpp b/unittests/if_ibc_tests.cpp index f43450f23a..e795941909 100644 --- a/unittests/if_ibc_tests.cpp +++ b/unittests/if_ibc_tests.cpp @@ -24,16 +24,6 @@ using mvo = mutable_variant_object; BOOST_AUTO_TEST_SUITE(if_ibc) -// These are the producers and finalizers to use across all chains -const std::vector test_nodes = -{ - "a"_n, "b"_n, "c"_n, "d"_n, "e"_n, - "f"_n, "g"_n, "h"_n, "i"_n, "j"_n, - "k"_n, "l"_n, "m"_n, "n"_n, "o"_n, - "p"_n, "q"_n, "r"_n, "s"_n, "t"_n, - "u"_n -}; - // Extending the default chain tester // ( libraries/testing/include/eosio/testing/tester.hpp ) class ibc_tester : public tester { @@ -83,63 +73,6 @@ class ibc_tester : public tester { ); } -/* void check_proof(){ - - auto cr = push_action( _bridge, "checkproof"_n, _bridge, mutable_variant_object() - ("proof", mutable_variant_object() - ("finality_proof", mutable_variant_object() - ("qc_block", mutable_variant_object() - ("light_header_protocol_version_major", 1) - ("light_header_protocol_version_minor", 0) - ("finalizer_policy_generation", 1) - ("active_finalizer_policy", fc::variants()) - ("witness_hash", "888ceeb757ea240d1c1ae2f4f717e67b73dcd592b2ba097f63b4c3e3ca4350e1") - ("finality_mroot", "1d2ab7379301370d3fa1b27a9f4ac077f6ea445a1aa3dbf7e18e9cc2c25b140c") - ) - ("qc", mutable_variant_object() - ("signature", "") - ("finalizers", fc::variants()) - ) - ) - ("target_block_proof_of_inclusion", mutable_variant_object() - ("target_node_index", 7) - ("last_node_index", 7) - ("target", mutable_variant_object() - ("finality_data", mutable_variant_object() - ("major_version", 1) - ("minor_version", 0) - ("finalizer_policy_generation", 1) - ("active_finalizer_policy", fc::variants()) - ("witness_hash", "dff620c1c4d31cade95ed609269a86d4ecb2357f9302d17675c0665c75786508") - ("finality_mroot", "1397eb7c86719f160188fa740fc3610ccb5a6681ad56807dc99a17fe73a7b7fd") - ) - ("dynamic_data", mutable_variant_object() - ("block_num", 28) - ("action_proofs", fc::variants()) - ("action_mroot", "4e890ef0e014f93bd1b31fabf1041ecc9fb1c44e957c2f7b1682333ee426677a") - ) - ) - ("merkle_branches", fc::variants({ - mutable_variant_object() - ("direction", 1) - ("hash", "4e17da018040c80339f2714828d1927d5b616f9af7aa4768c1876df6f05e5602") - , - mutable_variant_object() - ("direction", 1) - ("hash", "7ee0e16f1941fb5a98d80d20ca92e0c689e9284285d5f90ecd4f8f1ea2ffb53c") - , - mutable_variant_object() - ("direction", 1) - ("hash", "401526ba03ec4a955c83cda131dacd3e89becaad2cf04107170e436dd90a553f") - })) - - ) - ) - - ); - - } - */ void check_proof(){ auto cr = push_action( _bridge, "checkproof"_n, _bridge, mutable_variant_object() From 4f8a5c487c8ea4b6fbb472fbef85be260a335a57 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sun, 17 Mar 2024 14:46:22 +0000 Subject: [PATCH 03/17] Renamed if_ibc to svnn_ibc, fixed issues --- .../{if_ibc_tests.cpp => svnn_ibc_tests.cpp} | 19 ++-- unittests/test-contracts/CMakeLists.txt | 2 +- .../{if_ibc => svnn_ibc}/CMakeLists.txt | 2 +- .../{if_ibc => svnn_ibc}/bitset.hpp | 12 +-- .../if_ibc.abi => svnn_ibc/svnn_ibc.abi} | 0 .../if_ibc.cpp => svnn_ibc/svnn_ibc.cpp} | 85 +++++------------- .../if_ibc.hpp => svnn_ibc/svnn_ibc.hpp} | 83 +---------------- .../if_ibc.wasm => svnn_ibc/svnn_ibc.wasm} | Bin unittests/test_contracts.hpp.in | 2 +- 9 files changed, 36 insertions(+), 169 deletions(-) rename unittests/{if_ibc_tests.cpp => svnn_ibc_tests.cpp} (93%) rename unittests/test-contracts/{if_ibc => svnn_ibc}/CMakeLists.txt (92%) rename unittests/test-contracts/{if_ibc => svnn_ibc}/bitset.hpp (75%) rename unittests/test-contracts/{if_ibc/if_ibc.abi => svnn_ibc/svnn_ibc.abi} (100%) rename unittests/test-contracts/{if_ibc/if_ibc.cpp => svnn_ibc/svnn_ibc.cpp} (80%) rename unittests/test-contracts/{if_ibc/if_ibc.hpp => svnn_ibc/svnn_ibc.hpp} (92%) rename unittests/test-contracts/{if_ibc/if_ibc.wasm => svnn_ibc/svnn_ibc.wasm} (100%) diff --git a/unittests/if_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp similarity index 93% rename from unittests/if_ibc_tests.cpp rename to unittests/svnn_ibc_tests.cpp index e795941909..f1d1fe998a 100644 --- a/unittests/if_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -1,4 +1,3 @@ -// From fork tests #include #include #include @@ -17,12 +16,11 @@ using namespace eosio::chain; using namespace eosio::testing; -// From params_test #include using mvo = mutable_variant_object; -BOOST_AUTO_TEST_SUITE(if_ibc) +BOOST_AUTO_TEST_SUITE(svnn_ibc) // Extending the default chain tester // ( libraries/testing/include/eosio/testing/tester.hpp ) @@ -32,14 +30,13 @@ class ibc_tester : public tester { // This is mostly for creating accounts and loading contracts. void setup(){ - // load bridge contract create_account( _bridge ); - set_code( _bridge, eosio::testing::test_contracts::if_ibc_wasm()); - set_abi( _bridge, eosio::testing::test_contracts::if_ibc_abi()); - + set_code( _bridge, eosio::testing::test_contracts::svnn_ibc_wasm()); + set_abi( _bridge, eosio::testing::test_contracts::svnn_ibc_abi()); } + //set a finalizer policy void set_policy(){ auto cr = push_action( _bridge, "setfpolicy"_n, _bridge, mutable_variant_object() ("from_block_num", 1) @@ -69,12 +66,11 @@ class ibc_tester : public tester { ("public_key", "08c9bd408bac02747e493d918e4b3e6bd1a2ffaf9bfca4f2e79dd22e12556bf46e911f25613c24d9f6403996c5246c19ef94aff48094868425eda1e46bcd059c59f3b060521be797f5cc2e6debe2180efa12c0814618a38836a64c3d7440740f") })) ) - ); } + //verify a proof void check_proof(){ - auto cr = push_action( _bridge, "checkproof"_n, _bridge, mutable_variant_object() ("proof", mutable_variant_object() ("finality_proof", mutable_variant_object() @@ -120,14 +116,10 @@ class ibc_tester : public tester { ("direction", 1) ("hash", "401526ba03ec4a955c83cda131dacd3e89becaad2cf04107170e436dd90a553f") })) - ) ) - ); - } - }; BOOST_AUTO_TEST_CASE( first_test ) try { @@ -137,7 +129,6 @@ BOOST_AUTO_TEST_CASE( first_test ) try { chain_a.setup(); chain_a.set_policy(); - chain_a.check_proof(); } FC_LOG_AND_RETHROW() diff --git a/unittests/test-contracts/CMakeLists.txt b/unittests/test-contracts/CMakeLists.txt index 1296344f30..accc16bf7b 100644 --- a/unittests/test-contracts/CMakeLists.txt +++ b/unittests/test-contracts/CMakeLists.txt @@ -45,4 +45,4 @@ add_subdirectory( crypto_primitives_test ) add_subdirectory( bls_primitives_test ) add_subdirectory( get_block_num_test ) add_subdirectory( nested_container_multi_index ) -add_subdirectory( if_ibc ) +add_subdirectory( svnn_ibc ) diff --git a/unittests/test-contracts/if_ibc/CMakeLists.txt b/unittests/test-contracts/svnn_ibc/CMakeLists.txt similarity index 92% rename from unittests/test-contracts/if_ibc/CMakeLists.txt rename to unittests/test-contracts/svnn_ibc/CMakeLists.txt index 427c77aa9f..31dfbd568d 100644 --- a/unittests/test-contracts/if_ibc/CMakeLists.txt +++ b/unittests/test-contracts/svnn_ibc/CMakeLists.txt @@ -1,4 +1,4 @@ -set( IBC_CONTRACT "if_ibc" ) +set( IBC_CONTRACT "svnn_ibc" ) if( EOSIO_COMPILE_TEST_CONTRACTS ) add_contract( ${IBC_CONTRACT} ${IBC_CONTRACT} ${IBC_CONTRACT}.cpp ) diff --git a/unittests/test-contracts/if_ibc/bitset.hpp b/unittests/test-contracts/svnn_ibc/bitset.hpp similarity index 75% rename from unittests/test-contracts/if_ibc/bitset.hpp rename to unittests/test-contracts/svnn_ibc/bitset.hpp index e91cf55a50..ba237ab76b 100644 --- a/unittests/test-contracts/if_ibc/bitset.hpp +++ b/unittests/test-contracts/svnn_ibc/bitset.hpp @@ -3,14 +3,10 @@ using namespace eosio; class bitset { public: bitset(size_t size) - : num_bits(size), data(new uint64_t[(size + 63) / 64]()) {} + : num_bits(size), data((size + 63) / 64) {} - bitset(size_t size, uint64_t* _data) - : num_bits(size), data(_data) {} - - ~bitset() { - delete[] data; - } + bitset(size_t size, const std::vector& raw_bitset) + : num_bits(size), data(raw_bitset) {} // Set a bit to 1 void set(size_t index) { @@ -37,7 +33,7 @@ class bitset { private: size_t num_bits; - uint64_t* data; + std::vector data; // Check if the index is within bounds void check_bounds(size_t index) const { diff --git a/unittests/test-contracts/if_ibc/if_ibc.abi b/unittests/test-contracts/svnn_ibc/svnn_ibc.abi similarity index 100% rename from unittests/test-contracts/if_ibc/if_ibc.abi rename to unittests/test-contracts/svnn_ibc/svnn_ibc.abi diff --git a/unittests/test-contracts/if_ibc/if_ibc.cpp b/unittests/test-contracts/svnn_ibc/svnn_ibc.cpp similarity index 80% rename from unittests/test-contracts/if_ibc/if_ibc.cpp rename to unittests/test-contracts/svnn_ibc/svnn_ibc.cpp index ae54aaee71..a5ec47f869 100644 --- a/unittests/test-contracts/if_ibc/if_ibc.cpp +++ b/unittests/test-contracts/svnn_ibc/svnn_ibc.cpp @@ -1,8 +1,7 @@ -#include "if_ibc.hpp" - +#include "svnn_ibc.hpp" //add two numbers from the g1 group (aggregation) -std::vector if_ibc::_g1add(const std::vector& op1, const std::vector& op2) { +std::vector svnn_ibc::_g1add(const std::vector& op1, const std::vector& op2) { check(op1.size() == std::tuple_size::value, "wrong op1 size passed"); check(op2.size() == std::tuple_size::value, "wrong op2 size passed"); bls_g1 r; @@ -11,12 +10,9 @@ std::vector if_ibc::_g1add(const std::vector& op1, const std::vector return v; } -void if_ibc::_maybe_set_finalizer_policy(const fpolicy& policy, const uint32_t from_block_num){ - +void svnn_ibc::_maybe_set_finalizer_policy(const fpolicy& policy, const uint32_t from_block_num){ policies_table _policies_table(get_self(), get_self().value); - auto itr = _policies_table.rbegin(); - //if the new policy is more recent than the most recent we are aware of, we record the new one if (itr==_policies_table.rend() || itr->generation < policy.generation){ @@ -28,66 +24,50 @@ void if_ibc::_maybe_set_finalizer_policy(const fpolicy& policy, const uint32_t f sfp.last_block_num = from_block_num; }); } - - if_ibc::storedpolicy spolicy; - + svnn_ibc::storedpolicy spolicy; spolicy.generation = policy.generation; spolicy.fthreshold = policy.fthreshold; spolicy.finalizers = policy.finalizers; - + //policy is in force until a newer policy is proven spolicy.last_block_num = std::numeric_limits::max(); - //set cache expiry spolicy.cache_expiry = add_time(current_time_point(), POLICY_CACHE_EXPIRY); - _policies_table.emplace( get_self(), [&]( auto& p ) { p = spolicy; }); - - } - } //adds the newly proven root if necessary -void if_ibc::_maybe_add_proven_root(const uint32_t block_num, const checksum256& finality_mroot){ - +void svnn_ibc::_maybe_add_proven_root(const uint32_t block_num, const checksum256& finality_mroot){ proofs_table _proofs_table(get_self(), get_self().value); - auto block_num_index = _proofs_table.get_index<"blocknum"_n>(); auto merkle_index = _proofs_table.get_index<"merkleroot"_n>(); - auto last_itr = block_num_index.rbegin(); //if first proven root or newer than the last proven root, we store it if (last_itr == block_num_index.rend() || last_itr->block_num& pk, const std::vector& sig, std::vector& msg){ +//verify that a signature over a given message has been generated with the private key matching the public key +void svnn_ibc::_verify(const std::vector& pk, const std::vector& sig, std::vector& msg){ check(pk.size() == std::tuple_size::value, "wrong pk size passed"); check(sig.size() == std::tuple_size::value, "wrong sig size passed"); bls_g1 g1_points[2]; @@ -106,7 +86,8 @@ void if_ibc::_verify(const std::vector& pk, const std::vector& sig, check(0 == memcmp(r.data(), eosio::detail::GT_ONE.data(), sizeof(bls_gt)), "bls signature verify failed"); } -void if_ibc::_check_qc(const quorum_certificate& qc, const checksum256& finality_digest, const uint64_t finalizer_policy_generation){ +//verify that the quorum certificate over the finality digest is valid +void svnn_ibc::_check_qc(const quorum_certificate& qc, const checksum256& finality_digest, const uint64_t finalizer_policy_generation){ policies_table _policies_table(get_self(), get_self().value); check(_policies_table.begin() != _policies_table.end(), "must set a finalizer policy before checking proofs"); @@ -119,7 +100,7 @@ void if_ibc::_check_qc(const quorum_certificate& qc, const checksum256& finality auto fa_end_itr = target_policy.finalizers.end(); size_t finalizer_count = std::distance(fa_itr, fa_end_itr); std::vector bitset_data(qc.finalizers); - bitset b(finalizer_count, bitset_data.data()); + bitset b(finalizer_count, bitset_data); bool first = true; @@ -144,48 +125,35 @@ void if_ibc::_check_qc(const quorum_certificate& qc, const checksum256& finality //verify that we have enough vote weight to meet the quorum threshold of the target policy check(weight>=target_policy.fthreshold, "insufficient signatures to reach quorum"); - std::array data = finality_digest.extract_as_byte_array(); std::vector v_data(data.begin(), data.end()); - //verify signature validity _verify(agg_pub_key, qc.signature, v_data); - - } -void if_ibc::_check_target_block_proof_of_inclusion(const proof_of_inclusion& proof, const std::optional reference_root){ +void svnn_ibc::_check_target_block_proof_of_inclusion(const proof_of_inclusion& proof, const std::optional reference_root){ //verify that the proof of inclusion is over a target block - check(std::holds_alternative(proof.target), "must supply proof of inclusion over block data"); + check(std::holds_alternative(proof.target), "must supply proof of inclusion over block data"); //resolve the proof to its merkle root checksum256 finality_mroot = proof.root(); - if (reference_root.has_value()){ check(reference_root.value() == finality_mroot, "cannot link proof to proven merkle root"); } else { - proofs_table _proofs_table(get_self(), get_self().value); - auto merkle_index = _proofs_table.get_index<"merkleroot"_n>(); - auto itr = merkle_index.find(finality_mroot); check(itr!= merkle_index.end(), "cannot link proof to proven merkle root"); - } - - block_data target_block = std::get(proof.target); - + block_data target_block = std::get(proof.target); if (target_block.finality_data.active_finalizer_policy.has_value()){ _maybe_set_finalizer_policy(target_block.finality_data.active_finalizer_policy.value(), target_block.dynamic_data.block_num); } - } -void if_ibc::_check_finality_proof(const finality_proof& finality_proof, const proof_of_inclusion& target_block_proof_of_inclusion){ - +void svnn_ibc::_check_finality_proof(const finality_proof& finality_proof, const proof_of_inclusion& target_block_proof_of_inclusion){ //temporarilly disabled : skip qc verification //if QC is valid, it means that we have reaced finality on the block referenced by the finality_mroot //_check_qc(finality_proof.qc, finality_proof.qc_block.finality_digest(), finality_proof.qc_block.finalizer_policy_generation); @@ -194,14 +162,13 @@ void if_ibc::_check_finality_proof(const finality_proof& finality_proof, const p _check_target_block_proof_of_inclusion(target_block_proof_of_inclusion, finality_proof.qc_block.finality_mroot); //if proof of inclusion was successful, the target block and its dynamic data have been validated as final and correct - block_data target_block = std::get(target_block_proof_of_inclusion.target); + block_data target_block = std::get(target_block_proof_of_inclusion.target); //if the finality_mroot we just proven is more recent than the last root we have stored, store it _maybe_add_proven_root(target_block.dynamic_data.block_num, finality_proof.qc_block.finality_mroot); - } -ACTION if_ibc::setfpolicy(const fpolicy& policy, const uint32_t from_block_num){ +ACTION svnn_ibc::setfpolicy(const fpolicy& policy, const uint32_t from_block_num){ //can only be called with account authority require_auth(get_self()); @@ -212,10 +179,9 @@ ACTION if_ibc::setfpolicy(const fpolicy& policy, const uint32_t from_block_num){ check(_policies_table.begin() == _policies_table.end(), "can only set finalizer policy manually for initialization"); _maybe_set_finalizer_policy(policy, from_block_num); - } -ACTION if_ibc::checkproof(const proof& proof){ +ACTION svnn_ibc::checkproof(const proof& proof){ //if we have a finality proof, we execute the "heavy" code path if (proof.finality_proof.has_value()){ @@ -225,28 +191,19 @@ ACTION if_ibc::checkproof(const proof& proof){ //if we only have a proof of inclusion of the target block, we execute the "light" code path _check_target_block_proof_of_inclusion(proof.target_block_proof_of_inclusion, std::nullopt); } - } //temporary : reset the state -ACTION if_ibc::clear(){ - +ACTION svnn_ibc::clear(){ require_auth(get_self()); - proofs_table _proofs_table(get_self(), get_self().value); policies_table _policies_table(get_self(), get_self().value); - auto fp_itr = _proofs_table.begin(); - while (fp_itr!= _proofs_table.end()){ fp_itr = _proofs_table.erase(fp_itr); } - auto p_itr = _policies_table.begin(); - while (p_itr!= _policies_table.end()){ p_itr = _policies_table.erase(p_itr); } - - } \ No newline at end of file diff --git a/unittests/test-contracts/if_ibc/if_ibc.hpp b/unittests/test-contracts/svnn_ibc/svnn_ibc.hpp similarity index 92% rename from unittests/test-contracts/if_ibc/if_ibc.hpp rename to unittests/test-contracts/svnn_ibc/svnn_ibc.hpp index 2c70cf5c15..f7e394ee6a 100644 --- a/unittests/test-contracts/if_ibc/if_ibc.hpp +++ b/unittests/test-contracts/svnn_ibc/svnn_ibc.hpp @@ -11,7 +11,7 @@ using namespace eosio; -CONTRACT if_ibc : public contract { +CONTRACT svnn_ibc : public contract { public: using contract::contract; @@ -21,27 +21,9 @@ CONTRACT if_ibc : public contract { const uint32_t POLICY_CACHE_EXPIRY = 600; //10 minutes for testing const uint32_t PROOF_CACHE_EXPIRY = 600; //10 minutes for testing - //Compute the next power of 2 for a given number - static uint64_t next_power_of_2(uint64_t value) { - value -= 1; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - value |= value >> 32; - value += 1; return value; - } - - static uint64_t clz_power_of_2(uint64_t v) { - return 64 - (__builtin_clzll(v)+1); - } - - //Compute the maximum number of layers of a merkle tree for a given number of leaves static uint64_t calculate_max_depth(uint64_t node_count) { if (node_count == 0) return 0; - auto implied_count = next_power_of_2(node_count); - return clz_power_of_2(implied_count) + 1; + return std::llround(std::log2(node_count)) + 2; } static uint32_t reverse_bytes(const uint32_t input){ @@ -80,53 +62,39 @@ CONTRACT if_ibc : public contract { for (uint64_t i = 0; i < layers_depth; i++) { if (c_last_node_index % 2) c_last_node_index+=1; - bool isLeft = c_index % 2 == 0 ? 0 : 1; uint64_t pairIndex = isLeft ? c_index - 1 : c_index == last_node_index - 1 && i < layers_depth - 1 ? c_index : c_index + 1; - c_last_node_index/=2; if (pairIndex < last_node_index) proof.push_back(isLeft); - c_index = c_index / 2; - } - return proof; - } */ struct merkle_branch { - uint8_t direction; checksum256 hash; - }; //compute the merkle root of target node and vector of merkle branches static checksum256 _compute_root(const std::vector proof_nodes, const checksum256& target){ - checksum256 hash = target; - for (int i = 0 ; i < proof_nodes.size() ; i++){ const checksum256 node = proof_nodes[i].hash; std::array arr = node.extract_as_byte_array(); - if (proof_nodes[i].direction == 0){ hash = hash_pair(std::make_pair(hash, node)); } else { hash = hash_pair(std::make_pair(node, hash)); } - } - return hash; - } struct quorum_certificate { @@ -204,42 +172,28 @@ CONTRACT if_ibc : public contract { std::vector returnvalue; checksum256 digest() const { - checksum256 hashes[2]; - const r_action_base* base = this; - const auto action_input_size = pack_size(data); const auto return_value_size = pack_size(returnvalue); - const auto rhs_size = action_input_size + return_value_size; - const auto serialized_base = pack(*base); const auto serialized_data = pack(data); const auto serialized_output = pack(returnvalue); - hashes[0] = sha256(serialized_base.data(), serialized_base.size()); - std::vector data_digest(action_input_size); std::vector output_digest(return_value_size); - std::vector h1_result(rhs_size); std::copy (serialized_data.cbegin(), serialized_data.cend(), h1_result.begin()); std::copy (serialized_output.cbegin(), serialized_output.cend(), h1_result.begin() + action_input_size); - hashes[1] = sha256(reinterpret_cast(h1_result.data()), rhs_size); - std::array arr1 = hashes[0].extract_as_byte_array(); std::array arr2 = hashes[1].extract_as_byte_array(); - std::array result; std::copy (arr1.cbegin(), arr1.cend(), result.begin()); std::copy (arr2.cbegin(), arr2.cend(), result.begin() + 32); - checksum256 final_hash = sha256(reinterpret_cast(result.data()), 64); - return final_hash; - } EOSLIB_SERIALIZE( r_action, (account)(name)(authorization)(data)(returnvalue)) @@ -279,25 +233,17 @@ CONTRACT if_ibc : public contract { std::optional action_mroot; checksum256 get_action_mroot() const { - if (action_mroot.has_value()) return action_mroot.value(); else { - check(action_proofs.size()>0, "must have at least one action proof"); - checksum256 root = checksum256(); - for (auto ap : action_proofs){ if (root == checksum256()) root = ap.root(); else check(ap.root() == root, "all action proofs must resolve to the same merkle root"); } - return root; - } - }; - }; struct block_finality_data { @@ -326,7 +272,6 @@ CONTRACT if_ibc : public contract { std::vector serialized_policy = pack(active_finalizer_policy.value()); checksum256 policy_digest = sha256(serialized_policy.data(), serialized_policy.size()); checksum256 base_fpolicy_digest = hash_pair( std::make_pair( policy_digest, witness_hash) ); - return base_fpolicy_digest; } else { @@ -336,23 +281,16 @@ CONTRACT if_ibc : public contract { //returns hash of major_version + minor_version + finalizer_policy_generation + resolve_witness() + finality_mroot checksum256 finality_digest() const { - std::array result; - memcpy(&result[0], (uint8_t *)&major_version, 4); memcpy(&result[4], (uint8_t *)&minor_version, 4); memcpy(&result[8], (uint8_t *)&finalizer_policy_generation, 4); - std::array arr1 = finality_mroot.extract_as_byte_array(); std::array arr2 = resolve_witness().extract_as_byte_array(); - std::copy (arr1.cbegin(), arr1.cend(), result.begin() + 12); std::copy (arr2.cbegin(), arr2.cend(), result.begin() + 44); - checksum256 hash = sha256(reinterpret_cast(result.data()), 76); - return hash; - }; }; @@ -367,29 +305,19 @@ CONTRACT if_ibc : public contract { //returns hash of finality_digest() and dynamic_data_digest() checksum256 digest() const { - checksum256 finality_digest = finality_data.finality_digest(); - checksum256 action_mroot = dynamic_data.get_action_mroot(); - std::array result; - memcpy(&result[0], (uint8_t *)&finality_data.major_version, 4); memcpy(&result[4], (uint8_t *)&finality_data.minor_version, 4); memcpy(&result[8], (uint8_t *)&dynamic_data.block_num, 4); - std::array arr1 = finality_digest.extract_as_byte_array(); std::array arr2 = action_mroot.extract_as_byte_array(); - std::copy (arr1.cbegin(), arr1.cend(), result.begin() + 12); std::copy (arr2.cbegin(), arr2.cend(), result.begin() + 44); - - auto hash = sha256(reinterpret_cast(result.data()), 76); - + checksum256 hash = sha256(reinterpret_cast(result.data()), 76); return hash; - }; - }; struct action_data { @@ -426,15 +354,10 @@ CONTRACT if_ibc : public contract { //returns the merkle root obtained by hashing target.digest() with merkle_branches checksum256 root() const { - auto call_digest = [](const auto& var) -> checksum256 { return var.digest(); }; - checksum256 digest = std::visit(call_digest, target); - checksum256 root = _compute_root(merkle_branches, digest); - return root; - }; }; diff --git a/unittests/test-contracts/if_ibc/if_ibc.wasm b/unittests/test-contracts/svnn_ibc/svnn_ibc.wasm similarity index 100% rename from unittests/test-contracts/if_ibc/if_ibc.wasm rename to unittests/test-contracts/svnn_ibc/svnn_ibc.wasm diff --git a/unittests/test_contracts.hpp.in b/unittests/test_contracts.hpp.in index eb9279ff7a..ae110b75ca 100644 --- a/unittests/test_contracts.hpp.in +++ b/unittests/test_contracts.hpp.in @@ -50,7 +50,7 @@ namespace eosio { MAKE_READ_WASM_ABI(bls_primitives_test, bls_primitives_test, test-contracts) MAKE_READ_WASM_ABI(get_block_num_test, get_block_num_test, test-contracts) MAKE_READ_WASM_ABI(nested_container_multi_index, nested_container_multi_index, test-contracts) - MAKE_READ_WASM_ABI(if_ibc, if_ibc, test-contracts) + MAKE_READ_WASM_ABI(svnn_ibc, svnn_ibc, test-contracts) }; } /// eosio::testing From dadd0774eeed483a83d110df0e45d3f34cd1a17a Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Tue, 19 Mar 2024 14:13:19 -0400 Subject: [PATCH 04/17] Add new Savanna action digest (wip). --- libraries/chain/apply_context.cpp | 4 +- .../include/eosio/chain/action_receipt.hpp | 17 ++------ libraries/chain/include/eosio/chain/trace.hpp | 41 ++++++++++++++++++- unittests/api_tests.cpp | 15 +++---- unittests/restart_chain_tests.cpp | 14 ++++--- 5 files changed, 60 insertions(+), 31 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 75d42dbe35..0ab815970c 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -184,10 +184,10 @@ void apply_context::exec_one() r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor ); } - trx_context.executed_action_receipt_digests.emplace_back( r.digest() ); - finalize_trace( trace, start ); + trx_context.executed_action_receipt_digests.emplace_back( trace.digest_legacy() ); + if ( control.contracts_console() ) { print_debug(receiver, trace); } diff --git a/libraries/chain/include/eosio/chain/action_receipt.hpp b/libraries/chain/include/eosio/chain/action_receipt.hpp index 4afe35371d..b921b115be 100644 --- a/libraries/chain/include/eosio/chain/action_receipt.hpp +++ b/libraries/chain/include/eosio/chain/action_receipt.hpp @@ -1,8 +1,9 @@ #pragma once #include +#include -namespace eosio { namespace chain { +namespace eosio::chain { /** * For each action dispatched this receipt is generated @@ -15,20 +16,8 @@ namespace eosio { namespace chain { flat_map auth_sequence; fc::unsigned_int code_sequence = 0; ///< total number of setcodes fc::unsigned_int abi_sequence = 0; ///< total number of setabis - - digest_type digest()const { - digest_type::encoder e; - fc::raw::pack(e, receiver); - fc::raw::pack(e, act_digest); - fc::raw::pack(e, global_sequence); - fc::raw::pack(e, recv_sequence); - fc::raw::pack(e, auth_sequence); - fc::raw::pack(e, code_sequence); - fc::raw::pack(e, abi_sequence); - return e.result(); - } }; -} } /// namespace eosio::chain +} /// namespace eosio::chain FC_REFLECT( eosio::chain::action_receipt, (receiver)(act_digest)(global_sequence)(recv_sequence)(auth_sequence)(code_sequence)(abi_sequence) ) diff --git a/libraries/chain/include/eosio/chain/trace.hpp b/libraries/chain/include/eosio/chain/trace.hpp index 22957918b2..b615afc079 100644 --- a/libraries/chain/include/eosio/chain/trace.hpp +++ b/libraries/chain/include/eosio/chain/trace.hpp @@ -4,7 +4,7 @@ #include #include -namespace eosio { namespace chain { +namespace eosio::chain { struct account_delta { account_delta( const account_name& n, int64_t d):account(n),delta(d){} @@ -45,6 +45,43 @@ namespace eosio { namespace chain { std::optional except; std::optional error_code; std::vector return_value; + + digest_type digest_savanna() const { + assert(!!receipt); + const action_receipt& r = *receipt; + + digest_type::encoder e; + fc::raw::pack(e, r.receiver); + fc::raw::pack(e, r.recv_sequence); + fc::raw::pack(e, act.account); + fc::raw::pack(e, act.name); + fc::raw::pack(e, r.act_digest); + + { + digest_type::encoder e2; + fc::raw::pack(e2, r.global_sequence); + fc::raw::pack(e2, r.auth_sequence); + fc::raw::pack(e2, r.code_sequence); + fc::raw::pack(e2, r.abi_sequence); + fc::raw::pack(e, e2.result()); + } + return e.result(); + } + + digest_type digest_legacy()const { + assert(!!receipt); + const action_receipt& r = *receipt; + + digest_type::encoder e; + fc::raw::pack(e, r.receiver); + fc::raw::pack(e, r.act_digest); + fc::raw::pack(e, r.global_sequence); + fc::raw::pack(e, r.recv_sequence); + fc::raw::pack(e, r.auth_sequence); + fc::raw::pack(e, r.code_sequence); + fc::raw::pack(e, r.abi_sequence); + return e.result(); + } }; struct transaction_trace { @@ -80,7 +117,7 @@ namespace eosio { namespace chain { auth.permission == eosio::chain::config::active_name; } -} } /// namespace eosio::chain +} /// namespace eosio::chain FC_REFLECT( eosio::chain::account_delta, (account)(delta) ) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index a25e96f0ad..1dd2fbb5a5 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -907,16 +907,17 @@ BOOST_AUTO_TEST_CASE(light_validation_skip_cfa) try { BOOST_CHECK(*trace->receipt == *other_trace->receipt); BOOST_CHECK_EQUAL(2, other_trace->action_traces.size()); + auto check_action_traces = [](const auto& t, const auto& ot) { + BOOST_CHECK_EQUAL("", ot.console); // cfa not executed for light validation (trusted producer) + BOOST_CHECK_EQUAL(t.receipt->global_sequence, ot.receipt->global_sequence); + BOOST_CHECK_EQUAL(t.digest_legacy(), ot.digest_legacy()); // digest_legacy because test doesn't switch to Savanna + }; + BOOST_CHECK(other_trace->action_traces.at(0).context_free); // cfa - BOOST_CHECK_EQUAL("", other_trace->action_traces.at(0).console); // cfa not executed for light validation (trusted producer) - BOOST_CHECK_EQUAL(trace->action_traces.at(0).receipt->global_sequence, other_trace->action_traces.at(0).receipt->global_sequence); - BOOST_CHECK_EQUAL(trace->action_traces.at(0).receipt->digest(), other_trace->action_traces.at(0).receipt->digest()); + check_action_traces(trace->action_traces.at(0), other_trace->action_traces.at(0)); BOOST_CHECK(!other_trace->action_traces.at(1).context_free); // non-cfa - BOOST_CHECK_EQUAL("", other_trace->action_traces.at(1).console); - BOOST_CHECK_EQUAL(trace->action_traces.at(1).receipt->global_sequence, other_trace->action_traces.at(1).receipt->global_sequence); - BOOST_CHECK_EQUAL(trace->action_traces.at(1).receipt->digest(), other_trace->action_traces.at(1).receipt->digest()); - + check_action_traces(trace->action_traces.at(1), other_trace->action_traces.at(1)); other.close(); diff --git a/unittests/restart_chain_tests.cpp b/unittests/restart_chain_tests.cpp index 913a72902a..d60fe80ef8 100644 --- a/unittests/restart_chain_tests.cpp +++ b/unittests/restart_chain_tests.cpp @@ -226,15 +226,17 @@ BOOST_AUTO_TEST_CASE(test_light_validation_restart_from_block_log) { BOOST_CHECK(*trace->receipt == *other_trace->receipt); BOOST_CHECK_EQUAL(2u, other_trace->action_traces.size()); + auto check_action_traces = [](const auto& t, const auto& ot) { + BOOST_CHECK_EQUAL("", ot.console); // cfa not executed for replay + BOOST_CHECK_EQUAL(t.receipt->global_sequence, ot.receipt->global_sequence); + BOOST_CHECK_EQUAL(t.digest_legacy(), ot.digest_legacy()); // digest_legacy because test doesn't switch to Savanna + }; + BOOST_CHECK(other_trace->action_traces.at(0).context_free); // cfa - BOOST_CHECK_EQUAL("", other_trace->action_traces.at(0).console); // cfa not executed for replay - BOOST_CHECK_EQUAL(trace->action_traces.at(0).receipt->global_sequence, other_trace->action_traces.at(0).receipt->global_sequence); - BOOST_CHECK_EQUAL(trace->action_traces.at(0).receipt->digest(), other_trace->action_traces.at(0).receipt->digest()); + check_action_traces(trace->action_traces.at(0), other_trace->action_traces.at(0)); BOOST_CHECK(!other_trace->action_traces.at(1).context_free); // non-cfa - BOOST_CHECK_EQUAL("", other_trace->action_traces.at(1).console); - BOOST_CHECK_EQUAL(trace->action_traces.at(1).receipt->global_sequence, other_trace->action_traces.at(1).receipt->global_sequence); - BOOST_CHECK_EQUAL(trace->action_traces.at(1).receipt->digest(), other_trace->action_traces.at(1).receipt->digest()); + check_action_traces(trace->action_traces.at(1), other_trace->action_traces.at(1)); } BOOST_AUTO_TEST_SUITE_END() From 9c703672558070972d5fd4bca9ac36f918dacc67 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Tue, 19 Mar 2024 14:15:07 -0400 Subject: [PATCH 05/17] merge `origin/hotstuff_integration` --- libraries/appbase | 2 +- libraries/eos-vm | 2 +- libraries/libfc/libraries/bn256 | 2 +- tests/abieos | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/appbase b/libraries/appbase index d7a7580dd7..b75b31e14f 160000 --- a/libraries/appbase +++ b/libraries/appbase @@ -1 +1 @@ -Subproject commit d7a7580dd7ff1d8e7979157abc869865a3a43220 +Subproject commit b75b31e14f966fa3de6246e120dcba36c6ce5264 diff --git a/libraries/eos-vm b/libraries/eos-vm index ee0b142058..33e91ebf90 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit ee0b142058212073a78f71872dd54160ca69eb14 +Subproject commit 33e91ebf90088152477b7e7795cf5c871e70cb5c diff --git a/libraries/libfc/libraries/bn256 b/libraries/libfc/libraries/bn256 index b5adbb76d4..eae77bf03d 160000 --- a/libraries/libfc/libraries/bn256 +++ b/libraries/libfc/libraries/bn256 @@ -1 +1 @@ -Subproject commit b5adbb76d456e85385296a1388ba18f6f07d9d9e +Subproject commit eae77bf03dcf02579e967b0749ed5c175bd06c5a diff --git a/tests/abieos b/tests/abieos index ae6854ea7b..815fca5fb3 160000 --- a/tests/abieos +++ b/tests/abieos @@ -1 +1 @@ -Subproject commit ae6854ea7bad22f3dc4554a60124319af7e6cd30 +Subproject commit 815fca5fb3cd632eaffff098b14809a99229bebc From 84cc1f771731d0425ed7e97b335abf622afdb19d Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Tue, 19 Mar 2024 16:22:43 -0400 Subject: [PATCH 06/17] Revert "merge `origin/hotstuff_integration`" This reverts commit 9c703672558070972d5fd4bca9ac36f918dacc67. --- libraries/appbase | 2 +- libraries/eos-vm | 2 +- libraries/libfc/libraries/bn256 | 2 +- tests/abieos | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/appbase b/libraries/appbase index b75b31e14f..d7a7580dd7 160000 --- a/libraries/appbase +++ b/libraries/appbase @@ -1 +1 @@ -Subproject commit b75b31e14f966fa3de6246e120dcba36c6ce5264 +Subproject commit d7a7580dd7ff1d8e7979157abc869865a3a43220 diff --git a/libraries/eos-vm b/libraries/eos-vm index 33e91ebf90..ee0b142058 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit 33e91ebf90088152477b7e7795cf5c871e70cb5c +Subproject commit ee0b142058212073a78f71872dd54160ca69eb14 diff --git a/libraries/libfc/libraries/bn256 b/libraries/libfc/libraries/bn256 index eae77bf03d..b5adbb76d4 160000 --- a/libraries/libfc/libraries/bn256 +++ b/libraries/libfc/libraries/bn256 @@ -1 +1 @@ -Subproject commit eae77bf03dcf02579e967b0749ed5c175bd06c5a +Subproject commit b5adbb76d456e85385296a1388ba18f6f07d9d9e diff --git a/tests/abieos b/tests/abieos index 815fca5fb3..ae6854ea7b 160000 --- a/tests/abieos +++ b/tests/abieos @@ -1 +1 @@ -Subproject commit 815fca5fb3cd632eaffff098b14809a99229bebc +Subproject commit ae6854ea7bad22f3dc4554a60124319af7e6cd30 From d19c3f09a2d2ce269e2be4f46e0cf411fa8282e8 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 20 Mar 2024 12:22:06 +0000 Subject: [PATCH 07/17] Added cleaner log base 2 function + integrity check in bitset constructor --- unittests/test-contracts/svnn_ibc/bitset.hpp | 7 ++++++- unittests/test-contracts/svnn_ibc/svnn_ibc.hpp | 10 ++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/unittests/test-contracts/svnn_ibc/bitset.hpp b/unittests/test-contracts/svnn_ibc/bitset.hpp index ba237ab76b..eea00387b2 100644 --- a/unittests/test-contracts/svnn_ibc/bitset.hpp +++ b/unittests/test-contracts/svnn_ibc/bitset.hpp @@ -1,3 +1,5 @@ +#include + using namespace eosio; class bitset { @@ -6,7 +8,10 @@ class bitset { : num_bits(size), data((size + 63) / 64) {} bitset(size_t size, const std::vector& raw_bitset) - : num_bits(size), data(raw_bitset) {} + : num_bits(size), data(raw_bitset) { + check(raw_bitset.size() == (size + 63) / 64, "invalid raw bitset size"); + + } // Set a bit to 1 void set(size_t index) { diff --git a/unittests/test-contracts/svnn_ibc/svnn_ibc.hpp b/unittests/test-contracts/svnn_ibc/svnn_ibc.hpp index f7e394ee6a..6ac6d8597b 100644 --- a/unittests/test-contracts/svnn_ibc/svnn_ibc.hpp +++ b/unittests/test-contracts/svnn_ibc/svnn_ibc.hpp @@ -21,11 +21,13 @@ CONTRACT svnn_ibc : public contract { const uint32_t POLICY_CACHE_EXPIRY = 600; //10 minutes for testing const uint32_t PROOF_CACHE_EXPIRY = 600; //10 minutes for testing - static uint64_t calculate_max_depth(uint64_t node_count) { - if (node_count == 0) return 0; - return std::llround(std::log2(node_count)) + 2; + //Compute the maximum number of layers of a merkle tree for a given number of leaves + uint64_t calculate_max_depth(uint64_t node_count) { + if(node_count <= 1) + return node_count; + return 64 - __builtin_clzll(2 << (64 - 1 - __builtin_clzll ((node_count - 1)))); } - + static uint32_t reverse_bytes(const uint32_t input){ uint32_t output = (input>>24 & 0xff)|(input>>8 & 0xff00)|(input<<8 & 0xff0000)|(input<<24 & 0xff000000); return output; From 5804b24a23cf447e8fe012ee04f6fc83d209a7bc Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 20 Mar 2024 12:22:34 +0000 Subject: [PATCH 08/17] Added cleaner log base 2 function + integrity check in bitset constructor --- unittests/test-contracts/svnn_ibc/bitset.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/unittests/test-contracts/svnn_ibc/bitset.hpp b/unittests/test-contracts/svnn_ibc/bitset.hpp index eea00387b2..711e089fad 100644 --- a/unittests/test-contracts/svnn_ibc/bitset.hpp +++ b/unittests/test-contracts/svnn_ibc/bitset.hpp @@ -1,5 +1,3 @@ -#include - using namespace eosio; class bitset { From b4c445defd91b595de8a78f751d40137ff4bc3da Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Thu, 21 Mar 2024 10:10:33 -0400 Subject: [PATCH 09/17] Support optional computation and storage of two `action_digest` variants. --- libraries/chain/apply_context.cpp | 19 ++- libraries/chain/controller.cpp | 143 ++++++++++++------ .../eosio/chain/transaction_context.hpp | 3 +- libraries/chain/transaction_context.cpp | 1 + 4 files changed, 111 insertions(+), 55 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 0ab815970c..09db434fb1 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -186,7 +186,10 @@ void apply_context::exec_one() finalize_trace( trace, start ); - trx_context.executed_action_receipt_digests.emplace_back( trace.digest_legacy() ); + if (trx_context.executed_action_receipt_digests_l) + trx_context.executed_action_receipt_digests_l->emplace_back( trace.digest_legacy() ); + if (trx_context.executed_action_receipt_digests_s) + trx_context.executed_action_receipt_digests_s->emplace_back( trace.digest_savanna() ); if ( control.contracts_console() ) { print_debug(receiver, trace); @@ -218,17 +221,17 @@ void apply_context::exec() exec_one(); } - if( _cfa_inline_actions.size() > 0 || _inline_actions.size() > 0 ) { + if( !_cfa_inline_actions.empty() || !_inline_actions.empty() ) { EOS_ASSERT( recurse_depth < control.get_global_properties().configuration.max_inline_action_depth, transaction_exception, "max inline action depth per transaction reached" ); - } - for( uint32_t ordinal : _cfa_inline_actions ) { - trx_context.execute_action( ordinal, recurse_depth + 1 ); - } + for( uint32_t ordinal : _cfa_inline_actions ) { + trx_context.execute_action( ordinal, recurse_depth + 1 ); + } - for( uint32_t ordinal : _inline_actions ) { - trx_context.execute_action( ordinal, recurse_depth + 1 ); + for( uint32_t ordinal : _inline_actions ) { + trx_context.execute_action( ordinal, recurse_depth + 1 ); + } } } /// exec() diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 851c20ae54..4c0b0c06e1 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -158,7 +158,7 @@ struct completed_block { // to be used during Legacy to Savanna transistion where action_mroot // needs to be converted from Legacy merkle to Savanna merkle - std::optional action_receipt_digests; + std::optional action_receipt_digests_savanna; bool is_legacy() const { return std::holds_alternative(bsp.internal()); } @@ -229,7 +229,7 @@ struct assembled_block { std::optional new_producer_authority_cache; // Passed to completed_block, to be used by Legacy to Savanna transisition - std::optional action_receipt_digests; + std::optional action_receipt_digests_savanna; }; // -------------------------------------------------------------------------------- @@ -317,9 +317,9 @@ struct assembled_block { v); } - std::optional get_action_receipt_digests() const { + std::optional get_action_receipt_digests_savanna() const { return std::visit( - overloaded{[](const assembled_block_legacy& ab) -> std::optional { return ab.action_receipt_digests; }, + overloaded{[](const assembled_block_legacy& ab) -> std::optional { return ab.action_receipt_digests_savanna; }, [](const assembled_block_if& ab) -> std::optional { return {}; }}, v); } @@ -365,7 +365,7 @@ struct assembled_block { std::move(ab.pending_block_header_state), std::move(ab.unsigned_block), std::move(ab.trx_metas), pfs, validator, signer); - return completed_block{block_handle{std::move(bsp)}, std::move(ab.action_receipt_digests)}; + return completed_block{block_handle{std::move(bsp)}, std::move(ab.action_receipt_digests_savanna)}; }, [&](assembled_block_if& ab) { auto bsp = std::make_shared(ab.bhs, std::move(ab.trx_metas), @@ -381,17 +381,26 @@ struct building_block { // -------------------------------------------------------------------------------- struct building_block_common { using checksum_or_digests = std::variant; + enum class store_action_digests { legacy, savanna, both }; + const vector new_protocol_feature_activations; size_t num_new_protocol_features_that_have_activated = 0; deque pending_trx_metas; deque pending_trx_receipts; checksum_or_digests trx_mroot_or_receipt_digests {digests_t{}}; - digests_t action_receipt_digests; + std::optional action_receipt_digests_l; // legacy + std::optional action_receipt_digests_s; // savanna std::optional new_finalizer_policy; - building_block_common(const vector& new_protocol_feature_activations) : + building_block_common(const vector& new_protocol_feature_activations, store_action_digests sad) : new_protocol_feature_activations(new_protocol_feature_activations) - {} + { + if (sad == store_action_digests::legacy || sad == store_action_digests::both) + action_receipt_digests_l = digests_t{}; + + if (sad == store_action_digests::savanna || sad == store_action_digests::both) + action_receipt_digests_s = digests_t{}; + } bool is_protocol_feature_activated(const digest_type& digest, const flat_set& activated_features) const { if (activated_features.find(digest) != activated_features.end()) @@ -407,20 +416,39 @@ struct building_block { auto orig_trx_metas_size = pending_trx_metas.size(); auto orig_trx_receipt_digests_size = std::holds_alternative(trx_mroot_or_receipt_digests) ? std::get(trx_mroot_or_receipt_digests).size() : 0; - auto orig_action_receipt_digests_size = action_receipt_digests.size(); + auto orig_action_receipt_digests_l_size = action_receipt_digests_l ? action_receipt_digests_l->size() : 0; + auto orig_action_receipt_digests_s_size = action_receipt_digests_s ? action_receipt_digests_s->size() : 0; return [this, orig_trx_receipts_size, orig_trx_metas_size, orig_trx_receipt_digests_size, - orig_action_receipt_digests_size]() + orig_action_receipt_digests_l_size, + orig_action_receipt_digests_s_size]() { pending_trx_receipts.resize(orig_trx_receipts_size); pending_trx_metas.resize(orig_trx_metas_size); if (std::holds_alternative(trx_mroot_or_receipt_digests)) std::get(trx_mroot_or_receipt_digests).resize(orig_trx_receipt_digests_size); - action_receipt_digests.resize(orig_action_receipt_digests_size); + if (action_receipt_digests_l) + action_receipt_digests_l->resize(orig_action_receipt_digests_l_size); + if (action_receipt_digests_s) + action_receipt_digests_s->resize(orig_action_receipt_digests_s_size); }; } + + void initialize_action_receipts(transaction_context& trx_context) const { + if (action_receipt_digests_l) + trx_context.executed_action_receipt_digests_l = digests_t{}; + if (action_receipt_digests_s) + trx_context.executed_action_receipt_digests_s = digests_t{}; + } + + void store_trx_action_receipts(transaction_context& trx_context) { + if (action_receipt_digests_l) + fc::move_append( *action_receipt_digests_l, std::move(*trx_context.executed_action_receipt_digests_l) ); + if (action_receipt_digests_s) + fc::move_append( *action_receipt_digests_s, std::move(*trx_context.executed_action_receipt_digests_s) ); + } }; // -------------------------------------------------------------------------------- @@ -429,10 +457,11 @@ struct building_block { std::optional new_pending_producer_schedule; building_block_legacy( const block_header_state_legacy& prev, - block_timestamp_type when, - uint16_t num_prev_blocks_to_confirm, - const vector& new_protocol_feature_activations) - : building_block_common(new_protocol_feature_activations), + block_timestamp_type when, + uint16_t num_prev_blocks_to_confirm, + const vector& new_protocol_feature_activations, + store_action_digests sad) + : building_block_common(new_protocol_feature_activations, sad), pending_block_header_state(prev.next(when, num_prev_blocks_to_confirm)) {} @@ -453,8 +482,8 @@ struct building_block { const proposer_policy_ptr active_proposer_policy; // Cached: parent.get_next_active_proposer_policy(timestamp) const uint32_t block_num; // Cached: parent.block_num() + 1 - building_block_if(const block_state& parent, const building_block_input& input) - : building_block_common(input.new_protocol_feature_activations) + building_block_if(const block_state& parent, const building_block_input& input, store_action_digests sad) + : building_block_common(input.new_protocol_feature_activations, sad) , parent (parent) , timestamp(input.timestamp) , active_producer_authority{input.producer, @@ -491,12 +520,13 @@ struct building_block { // legacy constructor building_block(const block_header_state_legacy& prev, block_timestamp_type when, uint16_t num_prev_blocks_to_confirm, const vector& new_protocol_feature_activations) : - v(building_block_legacy(prev, when, num_prev_blocks_to_confirm, new_protocol_feature_activations)) + v(building_block_legacy(prev, when, num_prev_blocks_to_confirm, new_protocol_feature_activations, + building_block_common::store_action_digests::legacy)) {} // if constructor building_block(const block_state& prev, const building_block_input& input) : - v(building_block_if(prev, input)) + v(building_block_if(prev, input, building_block_common::store_action_digests::savanna)) {} bool is_legacy() const { return std::holds_alternative(v); } @@ -585,8 +615,20 @@ struct building_block { [](auto& bb) -> building_block_common::checksum_or_digests& { return bb.trx_mroot_or_receipt_digests; }, v); } - digests_t& action_receipt_digests() { - return std::visit([](auto& bb) -> digests_t& { return bb.action_receipt_digests; }, v); + std::optional& action_receipt_digests_l() { + return std::visit([](auto& bb) -> std::optional& { return bb.action_receipt_digests_l; }, v); + } + + std::optional& action_receipt_digests_s() { + return std::visit([](auto& bb) -> std::optional& { return bb.action_receipt_digests_s; }, v); + } + + void initialize_action_receipts(transaction_context& trx_context) const { + return std::visit([&](const auto& bb) { return bb.initialize_action_receipts(trx_context); }, v); + } + + void store_trx_action_receipts(transaction_context& trx_context) { + return std::visit([&](auto& bb) { return bb.store_trx_action_receipts(trx_context); }, v); } const producer_authority_schedule& active_producers() const { @@ -660,7 +702,8 @@ struct building_block { bool validating, std::optional validating_qc_data, const block_state_ptr& validating_bsp) { - digests_t& action_receipts = action_receipt_digests(); + auto& action_receipts_l = action_receipt_digests_l(); + auto& action_receipts_s = action_receipt_digests_s(); return std::visit( overloaded{ [&](building_block_legacy& bb) -> assembled_block { @@ -670,11 +713,11 @@ struct building_block { auto trx_merkle_fut = post_async_task(ioc, [&]() { return legacy_merkle(std::move(trx_receipts)); }); auto action_merkle_fut = - post_async_task(ioc, [&]() { return legacy_merkle(std::move(action_receipts)); }); + post_async_task(ioc, [&]() { return legacy_merkle(std::move(*action_receipts_l)); }); return std::make_pair(trx_merkle_fut.get(), action_merkle_fut.get()); }, [&](const checksum256_type& trx_checksum) { - return std::make_pair(trx_checksum, legacy_merkle(std::move(action_receipts))); + return std::make_pair(trx_checksum, legacy_merkle(std::move(*action_receipts_l))); }}, trx_mroot_or_receipt_digests()); @@ -690,7 +733,7 @@ struct building_block { std::move(bb.pending_block_header_state), std::move(bb.pending_trx_metas), std::move(block_ptr), std::move(bb.new_pending_producer_schedule), - std::move(bb.action_receipt_digests)} + std::move(bb.action_receipt_digests_l)} }; }, [&](building_block_if& bb) -> assembled_block { @@ -699,11 +742,11 @@ struct building_block { overloaded{[&](digests_t& trx_receipts) { // calculate the two merkle roots in separate threads auto trx_merkle_fut = post_async_task(ioc, [&]() { return calculate_merkle(std::move(trx_receipts)); }); - auto action_merkle_fut = post_async_task(ioc, [&]() { return calculate_merkle(std::move(action_receipts)); }); + auto action_merkle_fut = post_async_task(ioc, [&]() { return calculate_merkle(std::move(*action_receipts_s)); }); return std::make_pair(trx_merkle_fut.get(), action_merkle_fut.get()); }, [&](const checksum256_type& trx_checksum) { - return std::make_pair(trx_checksum, calculate_merkle(std::move(action_receipts))); + return std::make_pair(trx_checksum, calculate_merkle(std::move(*action_receipts_s))); }}, trx_mroot_or_receipt_digests()); @@ -2174,6 +2217,10 @@ struct controller_impl { trx_context.explicit_billed_cpu_time = explicit_billed_cpu_time; trx_context.billed_cpu_time_us = billed_cpu_time_us; trx_context.enforce_whiteblacklist = enforce_whiteblacklist; + + auto& bb = std::get(pending->_block_stage); + bb.initialize_action_receipts(trx_context); + transaction_trace_ptr trace = trx_context.trace; auto handle_exception = [&](const auto& e) @@ -2193,8 +2240,8 @@ struct controller_impl { auto restore = make_block_restore_point(); trace->receipt = push_receipt( gtrx.trx_id, transaction_receipt::soft_fail, trx_context.billed_cpu_time_us, trace->net_usage ); - auto& bb = std::get(pending->_block_stage); - fc::move_append( bb.action_receipt_digests(), std::move(trx_context.executed_action_receipt_digests) ); + + bb.store_trx_action_receipts(trx_context); // store action_digests for this trx into building_block trx_context.squash(); restore.cancel(); @@ -2342,6 +2389,9 @@ struct controller_impl { trx_context.enforce_whiteblacklist = gtrx.sender.empty() ? true : !sender_avoids_whitelist_blacklist_enforcement( gtrx.sender ); trace = trx_context.trace; + auto& bb = std::get(pending->_block_stage); + bb.initialize_action_receipts(trx_context); + auto handle_exception = [&](const auto& e) { cpu_time_to_bill_us = trx_context.update_billed_cpu_time( fc::time_point::now() ); @@ -2379,8 +2429,7 @@ struct controller_impl { trx_context.billed_cpu_time_us, trace->net_usage ); - fc::move_append( std::get(pending->_block_stage).action_receipt_digests(), - std::move(trx_context.executed_action_receipt_digests) ); + bb.store_trx_action_receipts(trx_context); // store action_digests for this trx into building_block trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); @@ -2554,6 +2603,9 @@ struct controller_impl { trx_context.subjective_cpu_bill_us = subjective_cpu_bill_us; trace = trx_context.trace; + auto& bb = std::get(pending->_block_stage); + bb.initialize_action_receipts(trx_context); + auto handle_exception =[&](const auto& e) { trace->error_code = controller::convert_exception_to_error_code( e ); @@ -2589,7 +2641,6 @@ struct controller_impl { auto restore = make_block_restore_point( trx->is_read_only() ); - auto& bb = std::get(pending->_block_stage); trx->billed_cpu_time_us = trx_context.billed_cpu_time_us; if (!trx->implicit() && !trx->is_read_only()) { transaction_receipt::status_enum s = (trx_context.delay == fc::seconds(0)) @@ -2606,17 +2657,17 @@ struct controller_impl { } if ( !trx->is_read_only() ) { - fc::move_append( bb.action_receipt_digests(), - std::move(trx_context.executed_action_receipt_digests) ); - if ( !trx->is_dry_run() ) { - // call the accept signal but only once for this transaction - if (!trx->accepted) { - trx->accepted = true; - } - - dmlog_applied_transaction(trace, &trn); - emit(applied_transaction, std::tie(trace, trx->packed_trx())); - } + bb.store_trx_action_receipts(trx_context); // store action_digests for this trx into building_block + + if ( !trx->is_dry_run() ) { + // call the accept signal but only once for this transaction + if (!trx->accepted) { + trx->accepted = true; + } + + dmlog_applied_transaction(trace, &trn); + emit(applied_transaction, std::tie(trace, trx->packed_trx())); + } } if ( trx->is_transient() ) { @@ -3013,8 +3064,8 @@ struct controller_impl { // Calculate Merkel tree root in Savanna way so that it is stored in // Leaf Node when building block_state. - assert(cb.action_receipt_digests); - auto action_mroot = calculate_merkle((*cb.action_receipt_digests)); + assert(cb.action_receipt_digests_savanna); + auto action_mroot = calculate_merkle((*cb.action_receipt_digests_savanna)); auto new_head = std::make_shared(*head, action_mroot); @@ -3280,7 +3331,7 @@ struct controller_impl { bsp->set_trxs_metas( ab.extract_trx_metas(), !skip_auth_checks ); } // create completed_block with the existing block_state as we just verified it is the same as assembled_block - pending->_block_stage = completed_block{ block_handle{bsp}, ab.get_action_receipt_digests() }; + pending->_block_stage = completed_block{ block_handle{bsp}, ab.get_action_receipt_digests_savanna() }; br = pending->_block_report; // copy before commit block destroys pending commit_block(s); diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 806e75d9dd..3f6803810e 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -141,7 +141,8 @@ namespace eosio { namespace chain { fc::time_point published; - deque executed_action_receipt_digests; + std::optional executed_action_receipt_digests_l; + std::optional executed_action_receipt_digests_s; flat_set bill_to_accounts; flat_set validate_ram_usage; diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 262d7995a7..a458200e58 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -47,6 +47,7 @@ namespace eosio { namespace chain { ,undo_session() ,trace(std::make_shared()) ,start(s) + ,executed_action_receipt_digests_l(digests_t{}) ,transaction_timer(std::move(tmr)) ,trx_type(type) ,net_usage(trace->net_usage) From 528c089e345c2bb48ec888a03a4849a8d4ab7fa8 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Thu, 21 Mar 2024 10:38:54 -0400 Subject: [PATCH 10/17] Construct `executed_action_receipt_digests_*` empty by default. --- libraries/chain/transaction_context.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index a458200e58..262d7995a7 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -47,7 +47,6 @@ namespace eosio { namespace chain { ,undo_session() ,trace(std::make_shared()) ,start(s) - ,executed_action_receipt_digests_l(digests_t{}) ,transaction_timer(std::move(tmr)) ,trx_type(type) ,net_usage(trace->net_usage) From 6cfb3fbda9fa8c83752c3ff98c58ee9f69b5f0d7 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Thu, 21 Mar 2024 14:10:15 -0400 Subject: [PATCH 11/17] Rename functions. --- libraries/chain/controller.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 4c0b0c06e1..405fdab18e 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -436,14 +436,14 @@ struct building_block { }; } - void initialize_action_receipts(transaction_context& trx_context) const { + void update_transaction_context_to_specify_needed_action_digests(transaction_context& trx_context) const { if (action_receipt_digests_l) trx_context.executed_action_receipt_digests_l = digests_t{}; if (action_receipt_digests_s) trx_context.executed_action_receipt_digests_s = digests_t{}; } - void store_trx_action_receipts(transaction_context& trx_context) { + void move_action_digests_from_transaction_context(transaction_context& trx_context) { if (action_receipt_digests_l) fc::move_append( *action_receipt_digests_l, std::move(*trx_context.executed_action_receipt_digests_l) ); if (action_receipt_digests_s) @@ -623,12 +623,12 @@ struct building_block { return std::visit([](auto& bb) -> std::optional& { return bb.action_receipt_digests_s; }, v); } - void initialize_action_receipts(transaction_context& trx_context) const { - return std::visit([&](const auto& bb) { return bb.initialize_action_receipts(trx_context); }, v); + void update_transaction_context_to_specify_needed_action_digests(transaction_context& trx_context) const { + return std::visit([&](const auto& bb) { return bb.update_transaction_context_to_specify_needed_action_digests(trx_context); }, v); } - void store_trx_action_receipts(transaction_context& trx_context) { - return std::visit([&](auto& bb) { return bb.store_trx_action_receipts(trx_context); }, v); + void move_action_digests_from_transaction_context(transaction_context& trx_context) { + return std::visit([&](auto& bb) { return bb.move_action_digests_from_transaction_context(trx_context); }, v); } const producer_authority_schedule& active_producers() const { @@ -2219,7 +2219,7 @@ struct controller_impl { trx_context.enforce_whiteblacklist = enforce_whiteblacklist; auto& bb = std::get(pending->_block_stage); - bb.initialize_action_receipts(trx_context); + bb.update_transaction_context_to_specify_needed_action_digests(trx_context); transaction_trace_ptr trace = trx_context.trace; @@ -2241,7 +2241,7 @@ struct controller_impl { trace->receipt = push_receipt( gtrx.trx_id, transaction_receipt::soft_fail, trx_context.billed_cpu_time_us, trace->net_usage ); - bb.store_trx_action_receipts(trx_context); // store action_digests for this trx into building_block + bb.move_action_digests_from_transaction_context(trx_context); // store action_digests for this trx into building_block trx_context.squash(); restore.cancel(); @@ -2390,7 +2390,7 @@ struct controller_impl { trace = trx_context.trace; auto& bb = std::get(pending->_block_stage); - bb.initialize_action_receipts(trx_context); + bb.update_transaction_context_to_specify_needed_action_digests(trx_context); auto handle_exception = [&](const auto& e) { @@ -2429,7 +2429,7 @@ struct controller_impl { trx_context.billed_cpu_time_us, trace->net_usage ); - bb.store_trx_action_receipts(trx_context); // store action_digests for this trx into building_block + bb.move_action_digests_from_transaction_context(trx_context); // store action_digests for this trx into building_block trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); @@ -2604,7 +2604,7 @@ struct controller_impl { trace = trx_context.trace; auto& bb = std::get(pending->_block_stage); - bb.initialize_action_receipts(trx_context); + bb.update_transaction_context_to_specify_needed_action_digests(trx_context); auto handle_exception =[&](const auto& e) { @@ -2657,7 +2657,7 @@ struct controller_impl { } if ( !trx->is_read_only() ) { - bb.store_trx_action_receipts(trx_context); // store action_digests for this trx into building_block + bb.move_action_digests_from_transaction_context(trx_context); // store action_digests for this trx into building_block if ( !trx->is_dry_run() ) { // call the accept signal but only once for this transaction From 2799ca72b1bbe42092c70384672fb329ed24b0a8 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 22 Mar 2024 10:22:46 -0400 Subject: [PATCH 12/17] Use struct to store two versions of `action_receipt_digests` --- benchmark/bls.cpp | 3 +- libraries/chain/apply_context.cpp | 8 +- libraries/chain/controller.cpp | 111 ++++++------------ .../eosio/chain/incremental_merkle.hpp | 4 +- .../chain/include/eosio/chain/merkle.hpp | 4 +- .../eosio/chain/transaction_context.hpp | 44 ++++++- libraries/chain/merkle.cpp | 4 +- libraries/chain/transaction_context.cpp | 2 + 8 files changed, 91 insertions(+), 89 deletions(-) diff --git a/benchmark/bls.cpp b/benchmark/bls.cpp index 4c86e78ba1..2178c17ba8 100644 --- a/benchmark/bls.cpp +++ b/benchmark/bls.cpp @@ -70,7 +70,8 @@ struct interface_in_benchmark { // build transaction context from the packed transaction timer = std::make_unique(); trx_timer = std::make_unique(*timer); - trx_ctx = std::make_unique(*chain->control.get(), *ptrx, ptrx->id(), std::move(*trx_timer)); + trx_ctx = std::make_unique(*chain->control.get(), *ptrx, ptrx->id(), std::move(*trx_timer), + action_digests_t::store_which_t::legacy); trx_ctx->max_transaction_time_subjective = fc::microseconds::maximum(); trx_ctx->init_for_input_trx( ptrx->get_unprunable_size(), ptrx->get_prunable_size() ); trx_ctx->exec(); // this is required to generate action traces to be used by apply_context constructor diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 09db434fb1..473c137ffd 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -186,10 +186,10 @@ void apply_context::exec_one() finalize_trace( trace, start ); - if (trx_context.executed_action_receipt_digests_l) - trx_context.executed_action_receipt_digests_l->emplace_back( trace.digest_legacy() ); - if (trx_context.executed_action_receipt_digests_s) - trx_context.executed_action_receipt_digests_s->emplace_back( trace.digest_savanna() ); + if (trx_context.executed_action_receipts.digests_l) + trx_context.executed_action_receipts.digests_l->emplace_back( trace.digest_legacy() ); + if (trx_context.executed_action_receipts.digests_s) + trx_context.executed_action_receipts.digests_s->emplace_back( trace.digest_savanna() ); if ( control.contracts_console() ) { print_debug(receiver, trace); diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 405fdab18e..54993a365e 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -381,25 +381,20 @@ struct building_block { // -------------------------------------------------------------------------------- struct building_block_common { using checksum_or_digests = std::variant; - enum class store_action_digests { legacy, savanna, both }; const vector new_protocol_feature_activations; size_t num_new_protocol_features_that_have_activated = 0; deque pending_trx_metas; deque pending_trx_receipts; checksum_or_digests trx_mroot_or_receipt_digests {digests_t{}}; - std::optional action_receipt_digests_l; // legacy - std::optional action_receipt_digests_s; // savanna + action_digests_t action_receipt_digests; std::optional new_finalizer_policy; - building_block_common(const vector& new_protocol_feature_activations, store_action_digests sad) : - new_protocol_feature_activations(new_protocol_feature_activations) + building_block_common(const vector& new_protocol_feature_activations, + action_digests_t::store_which_t sad) : + new_protocol_feature_activations(new_protocol_feature_activations), + action_receipt_digests(sad) { - if (sad == store_action_digests::legacy || sad == store_action_digests::both) - action_receipt_digests_l = digests_t{}; - - if (sad == store_action_digests::savanna || sad == store_action_digests::both) - action_receipt_digests_s = digests_t{}; } bool is_protocol_feature_activated(const digest_type& digest, const flat_set& activated_features) const { @@ -416,39 +411,20 @@ struct building_block { auto orig_trx_metas_size = pending_trx_metas.size(); auto orig_trx_receipt_digests_size = std::holds_alternative(trx_mroot_or_receipt_digests) ? std::get(trx_mroot_or_receipt_digests).size() : 0; - auto orig_action_receipt_digests_l_size = action_receipt_digests_l ? action_receipt_digests_l->size() : 0; - auto orig_action_receipt_digests_s_size = action_receipt_digests_s ? action_receipt_digests_s->size() : 0; + auto orig_action_receipt_digests_size = action_receipt_digests.size(); return [this, orig_trx_receipts_size, orig_trx_metas_size, orig_trx_receipt_digests_size, - orig_action_receipt_digests_l_size, - orig_action_receipt_digests_s_size]() + orig_action_receipt_digests_size]() { pending_trx_receipts.resize(orig_trx_receipts_size); pending_trx_metas.resize(orig_trx_metas_size); if (std::holds_alternative(trx_mroot_or_receipt_digests)) std::get(trx_mroot_or_receipt_digests).resize(orig_trx_receipt_digests_size); - if (action_receipt_digests_l) - action_receipt_digests_l->resize(orig_action_receipt_digests_l_size); - if (action_receipt_digests_s) - action_receipt_digests_s->resize(orig_action_receipt_digests_s_size); + action_receipt_digests.resize(orig_action_receipt_digests_size); }; } - - void update_transaction_context_to_specify_needed_action_digests(transaction_context& trx_context) const { - if (action_receipt_digests_l) - trx_context.executed_action_receipt_digests_l = digests_t{}; - if (action_receipt_digests_s) - trx_context.executed_action_receipt_digests_s = digests_t{}; - } - - void move_action_digests_from_transaction_context(transaction_context& trx_context) { - if (action_receipt_digests_l) - fc::move_append( *action_receipt_digests_l, std::move(*trx_context.executed_action_receipt_digests_l) ); - if (action_receipt_digests_s) - fc::move_append( *action_receipt_digests_s, std::move(*trx_context.executed_action_receipt_digests_s) ); - } }; // -------------------------------------------------------------------------------- @@ -460,7 +436,7 @@ struct building_block { block_timestamp_type when, uint16_t num_prev_blocks_to_confirm, const vector& new_protocol_feature_activations, - store_action_digests sad) + action_digests_t::store_which_t sad) : building_block_common(new_protocol_feature_activations, sad), pending_block_header_state(prev.next(when, num_prev_blocks_to_confirm)) {} @@ -482,7 +458,7 @@ struct building_block { const proposer_policy_ptr active_proposer_policy; // Cached: parent.get_next_active_proposer_policy(timestamp) const uint32_t block_num; // Cached: parent.block_num() + 1 - building_block_if(const block_state& parent, const building_block_input& input, store_action_digests sad) + building_block_if(const block_state& parent, const building_block_input& input, action_digests_t::store_which_t sad) : building_block_common(input.new_protocol_feature_activations, sad) , parent (parent) , timestamp(input.timestamp) @@ -521,12 +497,12 @@ struct building_block { building_block(const block_header_state_legacy& prev, block_timestamp_type when, uint16_t num_prev_blocks_to_confirm, const vector& new_protocol_feature_activations) : v(building_block_legacy(prev, when, num_prev_blocks_to_confirm, new_protocol_feature_activations, - building_block_common::store_action_digests::legacy)) + action_digests_t::store_which_t::both)) // [todo] should be both only when transition starts {} // if constructor building_block(const block_state& prev, const building_block_input& input) : - v(building_block_if(prev, input, building_block_common::store_action_digests::savanna)) + v(building_block_if(prev, input, action_digests_t::store_which_t::savanna)) {} bool is_legacy() const { return std::holds_alternative(v); } @@ -615,20 +591,8 @@ struct building_block { [](auto& bb) -> building_block_common::checksum_or_digests& { return bb.trx_mroot_or_receipt_digests; }, v); } - std::optional& action_receipt_digests_l() { - return std::visit([](auto& bb) -> std::optional& { return bb.action_receipt_digests_l; }, v); - } - - std::optional& action_receipt_digests_s() { - return std::visit([](auto& bb) -> std::optional& { return bb.action_receipt_digests_s; }, v); - } - - void update_transaction_context_to_specify_needed_action_digests(transaction_context& trx_context) const { - return std::visit([&](const auto& bb) { return bb.update_transaction_context_to_specify_needed_action_digests(trx_context); }, v); - } - - void move_action_digests_from_transaction_context(transaction_context& trx_context) { - return std::visit([&](auto& bb) { return bb.move_action_digests_from_transaction_context(trx_context); }, v); + action_digests_t& action_receipt_digests() { + return std::visit([](auto& bb) -> action_digests_t& { return bb.action_receipt_digests; }, v); } const producer_authority_schedule& active_producers() const { @@ -702,8 +666,7 @@ struct building_block { bool validating, std::optional validating_qc_data, const block_state_ptr& validating_bsp) { - auto& action_receipts_l = action_receipt_digests_l(); - auto& action_receipts_s = action_receipt_digests_s(); + auto& action_receipts = action_receipt_digests(); return std::visit( overloaded{ [&](building_block_legacy& bb) -> assembled_block { @@ -713,11 +676,11 @@ struct building_block { auto trx_merkle_fut = post_async_task(ioc, [&]() { return legacy_merkle(std::move(trx_receipts)); }); auto action_merkle_fut = - post_async_task(ioc, [&]() { return legacy_merkle(std::move(*action_receipts_l)); }); + post_async_task(ioc, [&]() { return legacy_merkle(std::move(*action_receipts.digests_l)); }); return std::make_pair(trx_merkle_fut.get(), action_merkle_fut.get()); }, [&](const checksum256_type& trx_checksum) { - return std::make_pair(trx_checksum, legacy_merkle(std::move(*action_receipts_l))); + return std::make_pair(trx_checksum, legacy_merkle(std::move(*action_receipts.digests_l))); }}, trx_mroot_or_receipt_digests()); @@ -733,7 +696,7 @@ struct building_block { std::move(bb.pending_block_header_state), std::move(bb.pending_trx_metas), std::move(block_ptr), std::move(bb.new_pending_producer_schedule), - std::move(bb.action_receipt_digests_l)} + std::move(bb.action_receipt_digests.digests_s)} }; }, [&](building_block_if& bb) -> assembled_block { @@ -742,11 +705,12 @@ struct building_block { overloaded{[&](digests_t& trx_receipts) { // calculate the two merkle roots in separate threads auto trx_merkle_fut = post_async_task(ioc, [&]() { return calculate_merkle(std::move(trx_receipts)); }); - auto action_merkle_fut = post_async_task(ioc, [&]() { return calculate_merkle(std::move(*action_receipts_s)); }); + auto action_merkle_fut = + post_async_task(ioc, [&]() { return calculate_merkle(std::move(*action_receipts.digests_s)); }); return std::make_pair(trx_merkle_fut.get(), action_merkle_fut.get()); }, [&](const checksum256_type& trx_checksum) { - return std::make_pair(trx_checksum, calculate_merkle(std::move(*action_receipts_s))); + return std::make_pair(trx_checksum, calculate_merkle(std::move(*action_receipts.digests_s))); }}, trx_mroot_or_receipt_digests()); @@ -2204,9 +2168,11 @@ struct controller_impl { etrx.set_reference_block( chain_head.id() ); } + auto& bb = std::get(pending->_block_stage); + transaction_checktime_timer trx_timer(timer); const packed_transaction trx( std::move( etrx ) ); - transaction_context trx_context( self, trx, trx.id(), std::move(trx_timer), start ); + transaction_context trx_context( self, trx, trx.id(), std::move(trx_timer), bb.action_receipt_digests().store_which(), start ); if (auto dm_logger = get_deep_mind_logger(trx_context.is_transient())) { dm_logger->on_onerror(etrx); @@ -2218,9 +2184,6 @@ struct controller_impl { trx_context.billed_cpu_time_us = billed_cpu_time_us; trx_context.enforce_whiteblacklist = enforce_whiteblacklist; - auto& bb = std::get(pending->_block_stage); - bb.update_transaction_context_to_specify_needed_action_digests(trx_context); - transaction_trace_ptr trace = trx_context.trace; auto handle_exception = [&](const auto& e) @@ -2241,7 +2204,7 @@ struct controller_impl { trace->receipt = push_receipt( gtrx.trx_id, transaction_receipt::soft_fail, trx_context.billed_cpu_time_us, trace->net_usage ); - bb.move_action_digests_from_transaction_context(trx_context); // store action_digests for this trx into building_block + bb.action_receipt_digests().append(std::move(trx_context.executed_action_receipts)); trx_context.squash(); restore.cancel(); @@ -2378,9 +2341,10 @@ struct controller_impl { in_trx_requiring_checks = true; uint32_t cpu_time_to_bill_us = billed_cpu_time_us; + auto& bb = std::get(pending->_block_stage); transaction_checktime_timer trx_timer( timer ); - transaction_context trx_context( self, *trx->packed_trx(), gtrx.trx_id, std::move(trx_timer) ); + transaction_context trx_context( self, *trx->packed_trx(), gtrx.trx_id, std::move(trx_timer), bb.action_receipt_digests().store_which() ); trx_context.leeway = fc::microseconds(0); // avoid stealing cpu resource trx_context.block_deadline = block_deadline; trx_context.max_transaction_time_subjective = max_transaction_time; @@ -2389,9 +2353,6 @@ struct controller_impl { trx_context.enforce_whiteblacklist = gtrx.sender.empty() ? true : !sender_avoids_whitelist_blacklist_enforcement( gtrx.sender ); trace = trx_context.trace; - auto& bb = std::get(pending->_block_stage); - bb.update_transaction_context_to_specify_needed_action_digests(trx_context); - auto handle_exception = [&](const auto& e) { cpu_time_to_bill_us = trx_context.update_billed_cpu_time( fc::time_point::now() ); @@ -2429,7 +2390,7 @@ struct controller_impl { trx_context.billed_cpu_time_us, trace->net_usage ); - bb.move_action_digests_from_transaction_context(trx_context); // store action_digests for this trx into building_block + bb.action_receipt_digests().append(std::move(trx_context.executed_action_receipts)); trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); @@ -2590,9 +2551,12 @@ struct controller_impl { } } + auto& bb = std::get(pending->_block_stage); + const signed_transaction& trn = trx->packed_trx()->get_signed_transaction(); transaction_checktime_timer trx_timer(timer); - transaction_context trx_context(self, *trx->packed_trx(), trx->id(), std::move(trx_timer), start, trx->get_trx_type()); + transaction_context trx_context(self, *trx->packed_trx(), trx->id(), std::move(trx_timer), + bb.action_receipt_digests().store_which(), start, trx->get_trx_type()); if ((bool)subjective_cpu_leeway && is_speculative_block()) { trx_context.leeway = *subjective_cpu_leeway; } @@ -2603,9 +2567,6 @@ struct controller_impl { trx_context.subjective_cpu_bill_us = subjective_cpu_bill_us; trace = trx_context.trace; - auto& bb = std::get(pending->_block_stage); - bb.update_transaction_context_to_specify_needed_action_digests(trx_context); - auto handle_exception =[&](const auto& e) { trace->error_code = controller::convert_exception_to_error_code( e ); @@ -2657,7 +2618,7 @@ struct controller_impl { } if ( !trx->is_read_only() ) { - bb.move_action_digests_from_transaction_context(trx_context); // store action_digests for this trx into building_block + bb.action_receipt_digests().append(std::move(trx_context.executed_action_receipts)); if ( !trx->is_dry_run() ) { // call the accept signal but only once for this transaction @@ -2965,7 +2926,7 @@ struct controller_impl { EOS_ASSERT( std::holds_alternative(pending->_block_stage), block_validate_exception, "cannot call commit_block until pending block is completed" ); - const auto& cb = std::get(pending->_block_stage); + auto& cb = std::get(pending->_block_stage); if (s != controller::block_status::irreversible) { auto add_completed_block = [&](auto& forkdb) { @@ -3062,10 +3023,10 @@ struct controller_impl { assert(std::holds_alternative(chain_head.internal())); block_state_legacy_ptr head = std::get(chain_head.internal()); // will throw if called after transistion - // Calculate Merkel tree root in Savanna way so that it is stored in + // Calculate Merkle tree root in Savanna way so that it is stored in // Leaf Node when building block_state. assert(cb.action_receipt_digests_savanna); - auto action_mroot = calculate_merkle((*cb.action_receipt_digests_savanna)); + auto action_mroot = calculate_merkle(std::move(*cb.action_receipt_digests_savanna)); auto new_head = std::make_shared(*head, action_mroot); diff --git a/libraries/chain/include/eosio/chain/incremental_merkle.hpp b/libraries/chain/include/eosio/chain/incremental_merkle.hpp index 1f157d20f7..224f5754b3 100644 --- a/libraries/chain/include/eosio/chain/incremental_merkle.hpp +++ b/libraries/chain/include/eosio/chain/incremental_merkle.hpp @@ -56,7 +56,7 @@ constexpr int clz_power_2(uint64_t value) { * @param node_count - the number of nodes in the implied tree * @return the max depth of the minimal tree that stores them */ -constexpr int calcluate_max_depth(uint64_t node_count) { +constexpr int calculate_max_depth(uint64_t node_count) { if (node_count == 0) { return 0; } @@ -166,7 +166,7 @@ class incremental_merkle_impl { */ const DigestType& append(const DigestType& digest) { bool partial = false; - auto max_depth = detail::calcluate_max_depth(_node_count + 1); + auto max_depth = detail::calculate_max_depth(_node_count + 1); auto current_depth = max_depth - 1; auto index = _node_count; auto top = digest; diff --git a/libraries/chain/include/eosio/chain/merkle.hpp b/libraries/chain/include/eosio/chain/merkle.hpp index f7cbc0ae68..70932926da 100644 --- a/libraries/chain/include/eosio/chain/merkle.hpp +++ b/libraries/chain/include/eosio/chain/merkle.hpp @@ -19,11 +19,11 @@ namespace eosio { namespace chain { * Uses make_canonical_pair which before hashing sets the first bit of the previous hashes * to 0 or 1 to indicate the side it is on. */ - digest_type legacy_merkle( deque ids ); + digest_type legacy_merkle( deque&& ids ); /** * Calculates the merkle root of a set of digests. Does not manipulate the digests. */ - digest_type calculate_merkle( deque ids ); + digest_type calculate_merkle( deque&& ids ); } } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 3f6803810e..81a9855017 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -33,6 +33,45 @@ namespace eosio { namespace chain { friend controller_impl; }; + struct action_digests_t { + enum class store_which_t { legacy, savanna, both }; + + std::optional digests_l; // legacy + std::optional digests_s; // savanna + + action_digests_t(store_which_t sw) { + if (sw == store_which_t::legacy || sw == store_which_t::both) + digests_l = digests_t{}; + if (sw == store_which_t::savanna || sw == store_which_t::both) + digests_s = digests_t{}; + } + + void append(action_digests_t&& o) { + if (digests_l) + fc::move_append(*digests_l, std::move(*o.digests_l)); + if (digests_s) + fc::move_append(*digests_s, std::move(*o.digests_s)); + } + + store_which_t store_which() const { + if (digests_l && digests_s) + return store_which_t::both; + if (digests_l) + return store_which_t::legacy; + assert(digests_s); + return store_which_t::savanna; + } + + std::pair size() const { + return { digests_l ? digests_l->size() : 0, digests_s ? digests_s->size() : 0 }; + } + + void resize(std::pair sz) { + if (digests_l) digests_l->resize(sz.first); + if (digests_s) digests_s->resize(sz.second); + } + }; + class transaction_context { private: void init( uint64_t initial_net_usage); @@ -43,6 +82,7 @@ namespace eosio { namespace chain { const packed_transaction& t, const transaction_id_type& trx_id, // trx_id diff than t.id() before replace_deferred transaction_checktime_timer&& timer, + action_digests_t::store_which_t sad, fc::time_point start = fc::time_point::now(), transaction_metadata::trx_type type = transaction_metadata::trx_type::input); ~transaction_context(); @@ -140,9 +180,7 @@ namespace eosio { namespace chain { fc::time_point published; - - std::optional executed_action_receipt_digests_l; - std::optional executed_action_receipt_digests_s; + action_digests_t executed_action_receipts; flat_set bill_to_accounts; flat_set validate_ram_usage; diff --git a/libraries/chain/merkle.cpp b/libraries/chain/merkle.cpp index de3468e7f7..fc3ed100f5 100644 --- a/libraries/chain/merkle.cpp +++ b/libraries/chain/merkle.cpp @@ -32,7 +32,7 @@ bool is_canonical_right(const digest_type& val) { } -digest_type legacy_merkle(deque ids) { +digest_type legacy_merkle(deque&& ids) { if( 0 == ids.size() ) { return digest_type(); } while( ids.size() > 1 ) { @@ -49,7 +49,7 @@ digest_type legacy_merkle(deque ids) { return ids.front(); } -digest_type calculate_merkle( deque ids ) { +digest_type calculate_merkle( deque&& ids ) { if( 0 == ids.size() ) { return digest_type(); } while( ids.size() > 1 ) { diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 262d7995a7..d03b539f88 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -39,6 +39,7 @@ namespace eosio { namespace chain { const packed_transaction& t, const transaction_id_type& trx_id, transaction_checktime_timer&& tmr, + action_digests_t::store_which_t sad, fc::time_point s, transaction_metadata::trx_type type) :control(c) @@ -47,6 +48,7 @@ namespace eosio { namespace chain { ,undo_session() ,trace(std::make_shared()) ,start(s) + ,executed_action_receipts(sad) ,transaction_timer(std::move(tmr)) ,trx_type(type) ,net_usage(trace->net_usage) From 5e895c4dafb7c82f73346ee38bc730ecf8eec529 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 22 Mar 2024 10:30:34 -0400 Subject: [PATCH 13/17] Simplify two namespaces. --- libraries/chain/apply_context.cpp | 4 ++-- libraries/chain/transaction_context.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index 473c137ffd..c323a7fbf9 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -14,7 +14,7 @@ using boost::container::flat_set; -namespace eosio { namespace chain { +namespace eosio::chain { static inline void print_debug(account_name receiver, const action_trace& ar) { if (!ar.console.empty()) { @@ -1108,4 +1108,4 @@ bool apply_context::should_use_eos_vm_oc()const { } -} } /// eosio::chain +} /// eosio::chain diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index d03b539f88..76e9354af0 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -11,7 +11,7 @@ #include -namespace eosio { namespace chain { +namespace eosio::chain { transaction_checktime_timer::transaction_checktime_timer(platform_timer& timer) : expired(timer.expired), _timer(timer) { @@ -830,4 +830,4 @@ namespace eosio { namespace chain { } -} } /// eosio::chain +} /// eosio::chain From 7a22f7ec5048b84eb7fe56b86ce7b1f2208f28a3 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 22 Mar 2024 10:35:48 -0400 Subject: [PATCH 14/17] rename `sad` into `store_which`. --- libraries/chain/controller.cpp | 12 ++++++------ libraries/chain/transaction_context.cpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 54993a365e..5cf44caecf 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -391,9 +391,9 @@ struct building_block { std::optional new_finalizer_policy; building_block_common(const vector& new_protocol_feature_activations, - action_digests_t::store_which_t sad) : + action_digests_t::store_which_t store_which) : new_protocol_feature_activations(new_protocol_feature_activations), - action_receipt_digests(sad) + action_receipt_digests(store_which) { } @@ -436,8 +436,8 @@ struct building_block { block_timestamp_type when, uint16_t num_prev_blocks_to_confirm, const vector& new_protocol_feature_activations, - action_digests_t::store_which_t sad) - : building_block_common(new_protocol_feature_activations, sad), + action_digests_t::store_which_t store_which) + : building_block_common(new_protocol_feature_activations, store_which), pending_block_header_state(prev.next(when, num_prev_blocks_to_confirm)) {} @@ -458,8 +458,8 @@ struct building_block { const proposer_policy_ptr active_proposer_policy; // Cached: parent.get_next_active_proposer_policy(timestamp) const uint32_t block_num; // Cached: parent.block_num() + 1 - building_block_if(const block_state& parent, const building_block_input& input, action_digests_t::store_which_t sad) - : building_block_common(input.new_protocol_feature_activations, sad) + building_block_if(const block_state& parent, const building_block_input& input, action_digests_t::store_which_t store_which) + : building_block_common(input.new_protocol_feature_activations, store_which) , parent (parent) , timestamp(input.timestamp) , active_producer_authority{input.producer, diff --git a/libraries/chain/transaction_context.cpp b/libraries/chain/transaction_context.cpp index 76e9354af0..03cfb55d32 100644 --- a/libraries/chain/transaction_context.cpp +++ b/libraries/chain/transaction_context.cpp @@ -39,7 +39,7 @@ namespace eosio::chain { const packed_transaction& t, const transaction_id_type& trx_id, transaction_checktime_timer&& tmr, - action_digests_t::store_which_t sad, + action_digests_t::store_which_t store_which, fc::time_point s, transaction_metadata::trx_type type) :control(c) @@ -48,7 +48,7 @@ namespace eosio::chain { ,undo_session() ,trace(std::make_shared()) ,start(s) - ,executed_action_receipts(sad) + ,executed_action_receipts(store_which) ,transaction_timer(std::move(tmr)) ,trx_type(type) ,net_usage(trace->net_usage) From 26f72bd21b56a5d6fa6b1c44d256836778fab0a9 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 22 Mar 2024 10:57:52 -0400 Subject: [PATCH 15/17] Encapsulate use of `action_digests_t` better. --- libraries/chain/apply_context.cpp | 5 +---- .../chain/include/eosio/chain/transaction_context.hpp | 7 +++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index c323a7fbf9..cc7f44f9f0 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -186,10 +186,7 @@ void apply_context::exec_one() finalize_trace( trace, start ); - if (trx_context.executed_action_receipts.digests_l) - trx_context.executed_action_receipts.digests_l->emplace_back( trace.digest_legacy() ); - if (trx_context.executed_action_receipts.digests_s) - trx_context.executed_action_receipts.digests_s->emplace_back( trace.digest_savanna() ); + trx_context.executed_action_receipts.compute_and_append_digests_from(trace); if ( control.contracts_console() ) { print_debug(receiver, trace); diff --git a/libraries/chain/include/eosio/chain/transaction_context.hpp b/libraries/chain/include/eosio/chain/transaction_context.hpp index 81a9855017..525794e929 100644 --- a/libraries/chain/include/eosio/chain/transaction_context.hpp +++ b/libraries/chain/include/eosio/chain/transaction_context.hpp @@ -53,6 +53,13 @@ namespace eosio { namespace chain { fc::move_append(*digests_s, std::move(*o.digests_s)); } + void compute_and_append_digests_from(action_trace& trace) { + if (digests_l) + digests_l->emplace_back(trace.digest_legacy()); + if (digests_s) + digests_s->emplace_back(trace.digest_savanna()); + } + store_which_t store_which() const { if (digests_l && digests_s) return store_which_t::both; From 1b92f0236f01ff6a5ddbd7c84031a9620b57f045 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 22 Mar 2024 11:26:38 -0400 Subject: [PATCH 16/17] Move back `finalize_trace` after the digest computation. --- libraries/chain/apply_context.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/apply_context.cpp b/libraries/chain/apply_context.cpp index cc7f44f9f0..aba88fbc61 100644 --- a/libraries/chain/apply_context.cpp +++ b/libraries/chain/apply_context.cpp @@ -184,10 +184,10 @@ void apply_context::exec_one() r.auth_sequence[auth.actor] = next_auth_sequence( auth.actor ); } - finalize_trace( trace, start ); - trx_context.executed_action_receipts.compute_and_append_digests_from(trace); + finalize_trace( trace, start ); + if ( control.contracts_console() ) { print_debug(receiver, trace); } From cc657914ba1c3b7f808fdcbb38cc4f3720c04b06 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 22 Mar 2024 12:00:50 -0400 Subject: [PATCH 17/17] Remove outdated comment. --- libraries/chain/block_header_state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index b51ddc50df..7879555842 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -81,7 +81,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con // header // ------ result.header = { - .timestamp = input.timestamp, // [greg todo] do we have to do the slot++ stuff from the legacy version? + .timestamp = input.timestamp, .producer = input.producer, .confirmed = 0, .previous = input.parent_id,