From 686986e18876bccca37a9c1ebf1734d2f6eae30e Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 29 Jul 2024 06:18:47 -0600 Subject: [PATCH 01/75] Added placeholder svnn_finality_violation.cpp and updated cmake files --- unittests/svnn_finality_violation_tests.cpp | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 unittests/svnn_finality_violation_tests.cpp diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp new file mode 100644 index 0000000000..8931faaabf --- /dev/null +++ b/unittests/svnn_finality_violation_tests.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include + +#include + +#include +#include +#include "fork_test_utilities.hpp" + +#include + +#include "finality_proof.hpp" + +using namespace eosio::chain; +using namespace eosio::testing; + +using mvo = mutable_variant_object; + +BOOST_AUTO_TEST_SUITE(svnn_finality_violation) + + BOOST_AUTO_TEST_CASE(finality_violation_test) { try { + + BOOST_TEST(true); + + } FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_SUITE_END() From b345e76d76237e25d736dd741fadd19ab0b60da2 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 29 Jul 2024 06:35:43 -0600 Subject: [PATCH 02/75] Added placeholder finality_violation smart contract --- .../savanna/finality_violation/CMakeLists.txt | 9 +++++++ .../finality_violation/finality_violation.abi | 23 ++++++++++++++++++ .../finality_violation/finality_violation.cpp | 5 ++++ .../finality_violation/finality_violation.hpp | 18 ++++++++++++++ .../finality_violation.wasm | Bin 0 -> 3351 bytes 5 files changed, 55 insertions(+) create mode 100644 unittests/test-contracts/savanna/finality_violation/CMakeLists.txt create mode 100644 unittests/test-contracts/savanna/finality_violation/finality_violation.abi create mode 100644 unittests/test-contracts/savanna/finality_violation/finality_violation.cpp create mode 100644 unittests/test-contracts/savanna/finality_violation/finality_violation.hpp create mode 100755 unittests/test-contracts/savanna/finality_violation/finality_violation.wasm diff --git a/unittests/test-contracts/savanna/finality_violation/CMakeLists.txt b/unittests/test-contracts/savanna/finality_violation/CMakeLists.txt new file mode 100644 index 0000000000..33b00c9ce1 --- /dev/null +++ b/unittests/test-contracts/savanna/finality_violation/CMakeLists.txt @@ -0,0 +1,9 @@ +set( FINALITY_VIOLATION_CONTRACT "finality_violation" ) + +if( EOSIO_COMPILE_TEST_CONTRACTS ) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common) + add_contract( ${FINALITY_VIOLATION_CONTRACT} ${FINALITY_VIOLATION_CONTRACT} ${FINALITY_VIOLATION_CONTRACT}.cpp ) +else() + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${FINALITY_VIOLATION_CONTRACT}.wasm ${CMAKE_CURRENT_BINARY_DIR}/${FINALITY_VIOLATION_CONTRACT}.wasm COPYONLY ) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${FINALITY_VIOLATION_CONTRACT}.abi ${CMAKE_CURRENT_BINARY_DIR}/${FINALITY_VIOLATION_CONTRACT}.abi COPYONLY ) +endif() diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi new file mode 100644 index 0000000000..8f6a59a000 --- /dev/null +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -0,0 +1,23 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.2", + "types": [], + "structs": [ + { + "name": "test", + "base": "", + "fields": [] + } + ], + "actions": [ + { + "name": "test", + "type": "test", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "variants": [], + "action_results": [] +} \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp new file mode 100644 index 0000000000..26a51e7c7c --- /dev/null +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -0,0 +1,5 @@ +#include "finality_violation.hpp" + +ACTION finality_violation::test(){ + +} \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp new file mode 100644 index 0000000000..796aa0d4be --- /dev/null +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include + +#include "../common/savanna.hpp" + +using namespace eosio; +using namespace savanna; + +CONTRACT finality_violation : public contract { + public: + using contract::contract; + + ACTION test(); + +}; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm new file mode 100755 index 0000000000000000000000000000000000000000..56beefe887ca6f076bb1a6b72806bcd04fe314fb GIT binary patch literal 3351 zcmbVOVQgDh6+ZXA=VZyXm)A0F&@SWots5DTv82>3`BPsfRT*qew-DIB7uSAG?ZmNb zyMatBc88HF6hwa%jlYwSV5snCL(uWZw9%B6N=%vxotRL5^hZHO4X8f~8-nlLXD468CB+h(nhG<6CYBXcjYBf^d+q8tR{71>vIOth>hrE{P)OTDad(wSWXUgSZq3Tz2 zg@6>R9$;9GX));7ZJ>5_metP`vjIif#AM)P$uAVM6x&qHj{1dksavs`qX)X63PGik zo()j1!H4Pofq_1U1{^%n*+tIQZ?_#0CDGd(u>^^p2d$dW^(Ltr=?_;ohu+YH!=;5t2TC$*8^d-wIfynKh8k1G}FSl`0=*+2gH=O5M# z5G>Bt>m3$KlQX2=5W=dt6c;kdKbN$1`bdm;HcS*F_@}u61-;0}g_U%L*&tkj<+y|> z@}3nJt~?TxQBn$;Z?<>Zc~1;U@}#a+T&e%{&kw$yKdtL$Uv7MZohW#LR-A?#VcanO zFo`bQ7)2#uEnUmUEsWTx$_Sc6jFF}R5YaRwS0HO=(L+2k0V&irk{&GbdLXY$r#(u@ z2D6mfsk-!KHpg7#dB33&7aWvGlCB-)Reiomj(}J79ZnRlxis&IZH@&ymPkaGV{%2? zvE6cPv12=QY_%P81aWDyvEA_&Nst4cBloz-8>xg4wMW5s&VxQ7qgX@^qR93Z)q0Y( z>{-)cj4MgCUr1D2gT!7gxdlFs>rqT-iUVj!zLE4$NtAOJktrvs9)LKW>cgN#0=~@* zcqWinIWPK`NYmpIeuiWd8Ap27niRK?K#U3Kz%E=~O?tL=kz9xkFn8i+do3P8nGuEU z!irIVugOJ5n}B7Gt4UsuKxv!IttN38Y%QzEyKacQ%fZ3g&NFFJCa6!j5fworlj>(U zI%vIC$8jnc|;IT0PAzp&fIYgtIR$ z88ji^haDfFa>78kIDzY_s6fEjEvjP#;P)2XW zXJx#zI?#UmN@sNdzk`mojn4UTu1*7ibW{gOn{-yjcU0$EC!Ho@;p(i7>vuj1omJ)# z9yW$xI%{2Yp#60h9l-CPV{fB#&CqFf(*e>Zoi#(}d)zy?dDaP6&%V}~JU3yggtNY~ z%1e$F$64lv`Vav!$HoLkEbQaUnUx%M9ZIyqI^hCtOrQ~jvE|c`!;Mw5f)P#M)fnd{ z_>F|seWV`2O~{=w1Xxd)#^9TvK7p$Z&9R6ZFKnE9VUU1o+K2-|-DdyX9%h6Zaooi` zF07=B6o+lesm6;8%eb}7n~-}IhS*?id$vV)Z$foMb$g;Cs*P4u6DEnCj9W74?DA~$ z%9Tl6XLXye7~65<5+@WnvBL{}&XXX^va=hPh6O=gup;ISrJgvM&dmo|w_0@5^Z0&C zR|B_%@0to7pHBIyXQq5=Kl&G!Nqw~~{omu%(?`2;4U+!u6;g{WPZ(8$qI z|Al8K#-_fghw**wPfdMo+JAo1KQ{f$R&?!n*!KA3adz>aY Date: Mon, 29 Jul 2024 07:28:34 -0600 Subject: [PATCH 03/75] Converted proof_test_cluster to use fixed size nodes, added vote_propagation flags --- unittests/finality_proof.hpp | 15 ++++++++++----- unittests/svnn_ibc_tests.cpp | 8 ++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index c1251a542c..f1a50d2755 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -115,8 +115,7 @@ namespace finality_proof { int32_t blocks_since_proposed = 0; }; - template - class proof_test_cluster : public finality_test_cluster { + class proof_test_cluster : public finality_test_cluster<4> { public: /***** @@ -144,6 +143,8 @@ namespace finality_proof { block_timestamp_type timestamp; block_timestamp_type parent_timestamp; + std::vector vote_propagation = {1,1,1}; + // counter to (optimistically) track internal policy changes std::unordered_map blocks_since_proposed_policy; @@ -211,10 +212,14 @@ namespace finality_proof { blocks_since_proposed_policy[last_proposed_finalizer_policy_digest] = {last_proposed_finalizer_policy, 0}; } } + + //process votes + if (vote_propagation.size() == 0) this->process_votes(1, this->num_needed_for_quorum); //enough to reach quorum threshold + else this->process_finalizer_votes(vote_propagation); //enough to reach quorum threshold - //process votes and collect / compute the IBC-relevant data - this->process_votes(1, this->num_needed_for_quorum); //enough to reach quorum threshold + //this->process_votes(1, this->num_needed_for_quorum); //enough to reach quorum threshold + // compute the IBC-relevant data finality_data_t finality_data = *this->node0.control->head_finality_data(); digest_type action_mroot = finality_data.action_mroot; digest_type base_digest = finality_data.base_digest; @@ -307,7 +312,7 @@ namespace finality_proof { } proof_test_cluster(finality_cluster_config_t config = {.transition_to_savanna = false}) - : finality_test_cluster(config) { + : finality_test_cluster<4>(config) { } diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index d0a0f3a982..11c2fdd664 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -56,7 +56,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) BOOST_AUTO_TEST_CASE(ibc_test) { try { // cluster is set up with the head about to produce IF Genesis - finality_proof::proof_test_cluster<4> cluster; + finality_proof::proof_test_cluster cluster; // produce IF Genesis block auto genesis_block_result = cluster.produce_block(); @@ -97,9 +97,9 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) auto block_5_result = cluster.produce_block(); //block num : 9 auto block_6_result = cluster.produce_block(); //block num : 10 - BOOST_TEST(block_4_result.qc_data.qc.has_value()); - BOOST_TEST(block_5_result.qc_data.qc.has_value()); - BOOST_TEST(block_6_result.qc_data.qc.has_value()); + BOOST_REQUIRE(block_4_result.qc_data.qc.has_value()); + BOOST_REQUIRE(block_5_result.qc_data.qc.has_value()); + BOOST_REQUIRE(block_6_result.qc_data.qc.has_value()); // create a few proofs we'll use to perform tests From 0f83dea1235d72d660e191470feab9d2df20c14a Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 29 Jul 2024 07:42:50 -0600 Subject: [PATCH 04/75] Added vote propagation tests --- unittests/svnn_finality_violation_tests.cpp | 44 ++++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 8931faaabf..49fdb8d335 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -20,9 +20,49 @@ using mvo = mutable_variant_object; BOOST_AUTO_TEST_SUITE(svnn_finality_violation) - BOOST_AUTO_TEST_CASE(finality_violation_test) { try { + BOOST_AUTO_TEST_CASE(cluster_vote_propagation_tests) { try { - BOOST_TEST(true); + finality_proof::proof_test_cluster cluster_1; + finality_proof::proof_test_cluster cluster_2; + finality_proof::proof_test_cluster cluster_3; + + cluster_1.vote_propagation = {1,1,1}; //all finalizers present + cluster_2.vote_propagation = {1,1,0}; //one finalizer missing + cluster_3.vote_propagation = {1,0,0}; //2 finalizers missing (insufficient for finality progress) + + auto genesis_1_block_result = cluster_1.produce_block(); + auto block_1_1_result = cluster_1.produce_block(); + auto block_2_1_result = cluster_1.produce_block(); + auto block_3_1_result = cluster_1.produce_block(); + auto block_4_1_result = cluster_1.produce_block(); + + BOOST_TEST(block_4_1_result.qc_data.qc.has_value()); + + auto genesis_2_block_result = cluster_2.produce_block(); + auto block_1_2_result = cluster_2.produce_block(); + auto block_2_2_result = cluster_2.produce_block(); + auto block_3_2_result = cluster_2.produce_block(); + auto block_4_2_result = cluster_2.produce_block(); + + BOOST_TEST(block_4_2_result.qc_data.qc.has_value()); + + auto genesis_3_block_result = cluster_3.produce_block(); + auto block_1_3_result = cluster_3.produce_block(); + auto block_2_3_result = cluster_3.produce_block(); + auto block_3_3_result = cluster_3.produce_block(); + auto block_4_3_result = cluster_3.produce_block(); + + BOOST_TEST(!block_4_3_result.qc_data.qc.has_value()); + + } FC_LOG_AND_RETHROW() } + + BOOST_AUTO_TEST_CASE(finality_violation_tests) { try { + + finality_proof::proof_test_cluster cluster_1; + finality_proof::proof_test_cluster cluster_2; + + cluster_1.vote_propagation = {1,1,0}; + cluster_2.vote_propagation = {1,0,1}; } FC_LOG_AND_RETHROW() } From 2eff1990d8b5a03762a03d92d36c2fe12db17db0 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 29 Jul 2024 08:13:56 -0600 Subject: [PATCH 05/75] Initial set up for finality violation tests --- unittests/svnn_finality_violation_tests.cpp | 85 +++++++++++++++++++-- unittests/test_contracts.hpp.in | 1 + 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 49fdb8d335..ef120140ce 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -58,11 +58,86 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_AUTO_TEST_CASE(finality_violation_tests) { try { - finality_proof::proof_test_cluster cluster_1; - finality_proof::proof_test_cluster cluster_2; - - cluster_1.vote_propagation = {1,1,0}; - cluster_2.vote_propagation = {1,0,1}; + //prepare a few actions we will be using for this test + mutable_variant_object create_action = mvo() + ( "issuer", "eosio"_n) + ( "maximum_supply", "100.0000 EOS"); + + mutable_variant_object issue_action = mvo() + ( "to", "eosio"_n) + ( "quantity", "100.0000 EOS") + ( "memo", ""); + + mutable_variant_object initial_transfer = mvo() + ("from", "eosio"_n) + ("to", "user1"_n) + ("quantity", "100.0000 EOS") + ("memo", ""); + + mutable_variant_object user1_transfer = mvo() + ("from", "user1"_n) + ("to", "user2"_n) + ("quantity", "1.0000 EOS") + ("memo", ""); + + //setup a "fake" chain and a "real" chain + finality_proof::proof_test_cluster fake_chain; + finality_proof::proof_test_cluster real_chain; + + //byzantine finalizers 0 and 1 are colluding and partition the network so that honest finalizers 2 and 3 are separated + fake_chain.vote_propagation = {1,1,0}; + real_chain.vote_propagation = {1,0,1}; + + fake_chain.node0.create_accounts( { "user1"_n, "user2"_n, "violation"_n, "eosio.token"_n } ); + real_chain.node0.create_accounts( { "user1"_n, "user2"_n, "violation"_n, "eosio.token"_n } ); + + fake_chain.node0.set_code( "eosio.token"_n, test_contracts::eosio_token_wasm() ); + fake_chain.node0.set_abi( "eosio.token"_n, test_contracts::eosio_token_abi() ); + fake_chain.node0.set_code( "violation"_n, test_contracts::finality_violation_wasm() ); + fake_chain.node0.set_abi( "violation"_n, test_contracts::finality_violation_abi() ); + + real_chain.node0.set_code( "eosio.token"_n, test_contracts::eosio_token_wasm() ); + real_chain.node0.set_abi( "eosio.token"_n, test_contracts::eosio_token_abi() ); + real_chain.node0.set_code( "violation"_n, test_contracts::finality_violation_wasm() ); + real_chain.node0.set_abi( "violation"_n, test_contracts::finality_violation_abi() ); + + fake_chain.node0.push_action("eosio.token"_n, "create"_n, "eosio.token"_n, create_action); + fake_chain.node0.push_action("eosio.token"_n, "issue"_n, "eosio"_n, issue_action); + fake_chain.node0.push_action("eosio.token"_n, "transfer"_n, "eosio"_n, initial_transfer); + + real_chain.node0.push_action("eosio.token"_n, "create"_n, "eosio.token"_n, create_action); + real_chain.node0.push_action("eosio.token"_n, "issue"_n, "eosio"_n, issue_action); + real_chain.node0.push_action("eosio.token"_n, "transfer"_n, "eosio"_n, initial_transfer); + + //produce a few blocks on the fake chain + auto fake_chain_genesis_block_result = fake_chain.produce_block(); + auto fake_chain_block_1_result = fake_chain.produce_block(); + auto fake_chain_block_2_result = fake_chain.produce_block(); + auto fake_chain_block_3_result = fake_chain.produce_block(); + auto fake_chain_block_4_result = fake_chain.produce_block(); + + BOOST_REQUIRE(fake_chain_block_4_result.qc_data.qc.has_value()); + + //produce a few blocks on the real chain + auto real_chain_genesis_block_result = real_chain.produce_block(); + auto real_chain_block_1_result = real_chain.produce_block(); + auto real_chain_block_2_result = real_chain.produce_block(); + auto real_chain_block_3_result = real_chain.produce_block(); + auto real_chain_block_4_result = real_chain.produce_block(); + + BOOST_REQUIRE(real_chain_block_4_result.qc_data.qc.has_value()); + + //verify the two chains are the same so far + BOOST_TEST(fake_chain_block_4_result.finality_digest == real_chain_block_4_result.finality_digest); + + //create a fork by pushing a transaction on the fake chain + fake_chain.node0.push_action("eosio.token"_n, "transfer"_n, "user1"_n, user1_transfer); + + auto fake_chain_block_5_result = real_chain.produce_block(); + auto real_chain_block_5_result = real_chain.produce_block(); + + //verify the chains are forked + BOOST_TEST(fake_chain_block_5_result.finality_digest != real_chain_block_5_result.finality_digest); } FC_LOG_AND_RETHROW() } diff --git a/unittests/test_contracts.hpp.in b/unittests/test_contracts.hpp.in index 64d3d26890..99d175f93e 100644 --- a/unittests/test_contracts.hpp.in +++ b/unittests/test_contracts.hpp.in @@ -51,6 +51,7 @@ namespace eosio { 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(ibc, ibc, test-contracts/savanna) + MAKE_READ_WASM_ABI(finality_violation, finality_violation, test-contracts/savanna) }; } /// eosio::testing From 38ccb3bbded51063733878ed1b316510df7f0f76 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 07:51:25 -0600 Subject: [PATCH 06/75] Progress on initial tests --- unittests/svnn_finality_violation_tests.cpp | 102 ++++++++++++++++++-- 1 file changed, 95 insertions(+), 7 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index ef120140ce..92ff2566ab 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -80,10 +80,67 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) ("quantity", "1.0000 EOS") ("memo", ""); + //setup a light client data cache + + //data retained for every policy change + struct policy_sunset_data { + + finalizer_policy policy; + + ibc_block_data_t last_qc_block; + qc_data_t last_qc; + + } + + struct data_cache { + + //The data_cache stores data relevant to finality violation proofs. + //It only operates in optimistic mode, which is sufficient for finality violation proofs testing purposes + + //store the reversible blocks + std::vector reversible_blocks; + + //store the last block over which we have a QC, as well as said QC + ibc_block_data_t high_qc_block; + qc_data_t high_qc; + + //store the last final block, as wall as the first QC over it. The last_final_qc and high_qc together + ibc_block_data_t last_final_block; + qc_data_t last_final_qc; + + //store all policies sunset data + std::vector policy_sunsets; + + //observe a stream of blocks as they are received, and store minimal data required to construct finality violation proofs in the future + ibc_block_data_t scan_block(ibc_block_data_t block){ + + if (reversible_blocks.size()>=2){ + last_final_block = high_qc_block; + last_final_qc = high_qc; + reversible_blocks.erase(reversible_blocks.begin()); + } + + if (reversible_blocks.size()>=1){ + high_qc_block = reversible_blocks[reversible_blocks.size()-1]; + high_qc = block.qc_data.qc.value(); + } + + reversible_blocks.push_back(block); + + return block; + } + + }; + + data_cache light_client_data; + //setup a "fake" chain and a "real" chain finality_proof::proof_test_cluster fake_chain; finality_proof::proof_test_cluster real_chain; + //initial finalizer policy A + auto policy_a = cluster.fin_policy_indices_0; // start from original set of indices + //byzantine finalizers 0 and 1 are colluding and partition the network so that honest finalizers 2 and 3 are separated fake_chain.vote_propagation = {1,1,0}; real_chain.vote_propagation = {1,0,1}; @@ -110,11 +167,11 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) real_chain.node0.push_action("eosio.token"_n, "transfer"_n, "eosio"_n, initial_transfer); //produce a few blocks on the fake chain - auto fake_chain_genesis_block_result = fake_chain.produce_block(); - auto fake_chain_block_1_result = fake_chain.produce_block(); - auto fake_chain_block_2_result = fake_chain.produce_block(); - auto fake_chain_block_3_result = fake_chain.produce_block(); - auto fake_chain_block_4_result = fake_chain.produce_block(); + auto fake_chain_genesis_block_result = light_client_data.scan_block(fake_chain.produce_block()) ; + auto fake_chain_block_1_result = light_client_data.scan_block(fake_chain.produce_block()); + auto fake_chain_block_2_result = light_client_data.scan_block(fake_chain.produce_block()); + auto fake_chain_block_3_result = light_client_data.scan_block(fake_chain.produce_block()); + auto fake_chain_block_4_result = light_client_data.scan_block(fake_chain.produce_block()); BOOST_REQUIRE(fake_chain_block_4_result.qc_data.qc.has_value()); @@ -133,12 +190,43 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //create a fork by pushing a transaction on the fake chain fake_chain.node0.push_action("eosio.token"_n, "transfer"_n, "user1"_n, user1_transfer); - auto fake_chain_block_5_result = real_chain.produce_block(); + auto fake_chain_block_5_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_5_result = real_chain.produce_block(); - //verify the chains are forked + //verify the chains have diverged BOOST_TEST(fake_chain_block_5_result.finality_digest != real_chain_block_5_result.finality_digest); + //qc over block #5. Policy A is now locked on #5 + auto fake_chain_block_6_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_6_result = real_chain.produce_block(); + + //qc over block #6 makes block #5 final. Since these blocks are different, this is a finality violation. + //moving forward, any + auto fake_chain_block_7_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_7_result = real_chain.produce_block(); + + //produce more blocks + auto fake_chain_block_8_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_8_result = real_chain.produce_block(); + + auto fake_chain_block_9_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_9_result = real_chain.produce_block(); + + //prove finality violation (violation of rule #1 : Don't vote on different blocks with the same timestamp) + + //on the fake chain, we only retain the last block data for a given finalizer policy (#9 in this case) + //#9 contains a QC on #8, which makes #7 final. + + return; + + indices1[0] = 1; // update key used for node0 in policy, which will result in a new policy we will call B + + // take note of policy digest prior to changes + digest_type previous_policy_digest = cluster.active_finalizer_policy_digest; + + // change the finalizer policy by rotating the key of node0 + cluster.node0.finkeys.set_finalizer_policy(indices1); + } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From cbcbc276f1a6123d3c9013a0cbcb42e99319fb7e Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 09:24:05 -0600 Subject: [PATCH 07/75] Comments related to rule #1 violation --- unittests/finality_proof.hpp | 9 ++++--- unittests/svnn_finality_violation_tests.cpp | 27 ++++++++++----------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index f1a50d2755..45fb5c8ac0 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -224,15 +224,18 @@ namespace finality_proof { digest_type action_mroot = finality_data.action_mroot; digest_type base_digest = finality_data.base_digest; - // compute commitments used for proving finality violations - digest_type level_3_commitments_digest = fc::sha256::hash(level_3_commitments_t{ + level_3_commitments_t lvl3_commitments { .reversible_blocks_mroot = finality_data.reversible_blocks_mroot, .latest_qc_claim_block_num = finality_data.latest_qc_claim_block_num, .latest_qc_claim_finality_digest = finality_data.latest_qc_claim_finality_digest, .latest_qc_claim_timestamp = finality_data.latest_qc_claim_timestamp, .timestamp = timestamp, .base_digest = base_digest - }); + }; + + + // compute commitments used for proving finality violations + digest_type level_3_commitments_digest = fc::sha256::hash(lvl3_commitments); // compute commitments used for proving finalizer policy changes digest_type level_2_commitments_digest = fc::sha256::hash(level_2_commitments_t{ diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 92ff2566ab..23d7eefdca 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -16,6 +16,8 @@ using namespace eosio::chain; using namespace eosio::testing; +using namespace finality_proof; + using mvo = mutable_variant_object; BOOST_AUTO_TEST_SUITE(svnn_finality_violation) @@ -90,7 +92,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) ibc_block_data_t last_qc_block; qc_data_t last_qc; - } + }; struct data_cache { @@ -102,11 +104,11 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //store the last block over which we have a QC, as well as said QC ibc_block_data_t high_qc_block; - qc_data_t high_qc; + qc_t high_qc; //store the last final block, as wall as the first QC over it. The last_final_qc and high_qc together ibc_block_data_t last_final_block; - qc_data_t last_final_qc; + qc_t last_final_qc; //store all policies sunset data std::vector policy_sunsets; @@ -139,7 +141,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) finality_proof::proof_test_cluster real_chain; //initial finalizer policy A - auto policy_a = cluster.fin_policy_indices_0; // start from original set of indices + auto policy_indices = fake_chain.fin_policy_indices_0; // start from original set of indices //byzantine finalizers 0 and 1 are colluding and partition the network so that honest finalizers 2 and 3 are separated fake_chain.vote_propagation = {1,1,0}; @@ -205,28 +207,25 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_7_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_7_result = real_chain.produce_block(); - //produce more blocks auto fake_chain_block_8_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_8_result = real_chain.produce_block(); - auto fake_chain_block_9_result = light_client_data.scan_block(fake_chain.produce_block()); - auto real_chain_block_9_result = real_chain.produce_block(); - //prove finality violation (violation of rule #1 : Don't vote on different blocks with the same timestamp) - //on the fake chain, we only retain the last block data for a given finalizer policy (#9 in this case) - //#9 contains a QC on #8, which makes #7 final. + //block #8 on the real chain contains a strong QC over a different block #7 than what the light client recorded from the fake chain + + //this is a rule #1 finality violation proof return; - indices1[0] = 1; // update key used for node0 in policy, which will result in a new policy we will call B +/* policy_indices[0] = 1; // update key used for node0 in policy, which will result in a new policy we will call B // take note of policy digest prior to changes - digest_type previous_policy_digest = cluster.active_finalizer_policy_digest; + digest_type previous_policy_digest = fake_chain.active_finalizer_policy_digest; // change the finalizer policy by rotating the key of node0 - cluster.node0.finkeys.set_finalizer_policy(indices1); - + cluster.node0.finkeys.set_finalizer_policy(policy_indices); +*/ } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From a686f0089ab37c52a9222e3d10072428be2e7db1 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 09:32:07 -0600 Subject: [PATCH 08/75] Updated ibc_block_data_t structure to expose all level 2 and level 3 commitments --- unittests/finality_proof.hpp | 31 +++++++++++++++++++++---------- unittests/svnn_ibc_tests.cpp | 30 +++++++++++++++--------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 45fb5c8ac0..6fcc5527f9 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -24,11 +24,20 @@ namespace finality_proof { block_timestamp_type last_pending_finalizer_policy_start_timestamp; digest_type last_proposed_finalizer_policy_digest; digest_type finality_digest; - digest_type level_3_commitments_digest; - digest_type level_2_commitments_digest; + level_3_commitments_t level_3_commitments; + level_2_commitments_t level_2_commitments; digest_type finality_leaf; digest_type finality_root; block_timestamp_type parent_timestamp; + + digest_type level_3_commitments_digest() const { + return fc::sha256::hash(level_3_commitments); + } + + digest_type level_2_commitments_digest() const { + return fc::sha256::hash(level_2_commitments); + } + }; static digest_type hash_pair(const digest_type& a, const digest_type& b) { @@ -235,14 +244,16 @@ namespace finality_proof { // compute commitments used for proving finality violations - digest_type level_3_commitments_digest = fc::sha256::hash(lvl3_commitments); + //digest_type level_3_commitments_digest = fc::sha256::hash(lvl3_commitments); - // compute commitments used for proving finalizer policy changes - digest_type level_2_commitments_digest = fc::sha256::hash(level_2_commitments_t{ + level_2_commitments_t lvl2_commitments { .last_pending_fin_pol_digest = last_pending_finalizer_policy_digest, .last_pending_fin_pol_start_timestamp = last_pending_finalizer_policy_start_timestamp, - .l3_commitments_digest = level_3_commitments_digest - }); + .l3_commitments_digest = fc::sha256::hash(lvl3_commitments) + }; + + // compute commitments used for proving finalizer policy changes + //digest_type level_2_commitments_digest = fc::sha256::hash(lvl2_commitments); // during IF transition, finality_root is always set to an empty digest digest_type finality_root = digest_type(); @@ -255,7 +266,7 @@ namespace finality_proof { .active_finalizer_policy_generation = is_genesis ? 1 : active_finalizer_policy.generation, .last_pending_finalizer_policy_generation = is_genesis ? 1 : last_pending_finalizer_policy.generation, .finality_tree_digest = finality_root, - .l2_commitments_digest = level_2_commitments_digest + .l2_commitments_digest = fc::sha256::hash(lvl2_commitments) }); // compute finality leaf @@ -291,8 +302,8 @@ namespace finality_proof { .last_pending_finalizer_policy_start_timestamp = last_pending_finalizer_policy_start_timestamp, .last_proposed_finalizer_policy_digest = last_proposed_finalizer_policy_digest, .finality_digest = finality_digest, - .level_3_commitments_digest = level_3_commitments_digest, - .level_2_commitments_digest = level_2_commitments_digest, + .level_3_commitments = lvl3_commitments, + .level_2_commitments = lvl2_commitments, .finality_leaf = finality_leaf, .finality_root = finality_root , .parent_timestamp = parent_timestamp diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 11c2fdd664..104915c6b0 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -114,7 +114,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_3_result.level_2_commitments_digest) + ("witness_hash", block_3_result.level_2_commitments_digest()) ("finality_mroot", block_3_result.finality_root) ) ("active_policy_qc", mvo() @@ -130,7 +130,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_2_result.level_2_commitments_digest) + ("witness_hash", block_2_result.level_2_commitments_digest()) ("finality_mroot", block_2_result.finality_root) ) ("timestamp", block_2_result.block->timestamp) @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_3_result.level_2_commitments_digest) + ("witness_hash", block_3_result.level_2_commitments_digest()) ("finality_mroot", block_3_result.finality_root) ) ("active_policy_qc", mvo() @@ -188,7 +188,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_4_result.level_2_commitments_digest) + ("witness_hash", block_4_result.level_2_commitments_digest()) ("finality_mroot", block_4_result.finality_root) ) ("active_policy_qc", mvo() @@ -204,7 +204,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_2_result.level_2_commitments_digest) + ("witness_hash", block_2_result.level_2_commitments_digest()) ("finality_mroot", block_2_result.finality_root) ) ("timestamp", block_2_result.block->timestamp) @@ -230,7 +230,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_2_result.level_2_commitments_digest) + ("witness_hash", block_2_result.level_2_commitments_digest()) ("finality_mroot", block_2_result.finality_root) ) ("timestamp", block_2_result.block->timestamp) @@ -388,7 +388,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_8_result.level_2_commitments_digest) + ("witness_hash", block_8_result.level_2_commitments_digest()) ("finality_mroot", block_8_result.finality_root) ) ("active_policy_qc", mvo() @@ -404,7 +404,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_7_result.level_2_commitments_digest) + ("witness_hash", block_7_result.level_2_commitments_digest()) ("finality_mroot", block_7_result.finality_root) ) ("timestamp", block_7_result.block->timestamp) @@ -429,7 +429,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_7_result.level_2_commitments_digest) + ("witness_hash", block_7_result.level_2_commitments_digest()) ("finality_mroot", block_7_result.finality_root) ) ("timestamp", block_7_result.block->timestamp) @@ -491,7 +491,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("minor_version", 0) ("active_finalizer_policy_generation", 1) ("pending_finalizer_policy_generation", 2) - ("witness_hash", block_10_result.level_2_commitments_digest) + ("witness_hash", block_10_result.level_2_commitments_digest()) ("finality_mroot", block_10_result.finality_root) ) ("active_policy_qc", mvo() @@ -507,7 +507,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("witness_hash", block_9_result.level_2_commitments_digest) + ("witness_hash", block_9_result.level_2_commitments_digest()) ("finality_mroot", block_9_result.finality_root) ) ("timestamp", block_9_result.block->timestamp) @@ -550,7 +550,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("minor_version", 0) ("active_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_generation", 2) - ("witness_hash", block_11_result.level_2_commitments_digest) + ("witness_hash", block_11_result.level_2_commitments_digest()) ("finality_mroot", block_11_result.finality_root) ) ("active_policy_qc", mvo() @@ -572,7 +572,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("active_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_generation", 2) ("pending_finalizer_policy", cluster.last_pending_finalizer_policy) - ("witness_hash", block_10_result.level_3_commitments_digest) + ("witness_hash", block_10_result.level_3_commitments_digest()) ("last_pending_finalizer_policy_start_timestamp", block_10_result.last_pending_finalizer_policy_start_timestamp ) ("finality_mroot", block_10_result.finality_root) ) @@ -603,7 +603,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 2) - ("witness_hash", block_12_result.level_2_commitments_digest) + ("witness_hash", block_12_result.level_2_commitments_digest()) ("finality_mroot", block_12_result.finality_root) ) ("active_policy_qc", mvo() @@ -620,7 +620,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("minor_version", 0) ("active_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_generation", 2) - ("witness_hash", block_11_result.level_2_commitments_digest) + ("witness_hash", block_11_result.level_2_commitments_digest()) ("finality_mroot", block_11_result.finality_root) ) ("timestamp", block_11_result.block->timestamp) From a82435a460734071d1f6a762418868f188123e05 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 11:04:55 -0600 Subject: [PATCH 09/75] Updated savanna contract common code to support level 3 commitments --- .../test-contracts/savanna/common/savanna.hpp | 55 ++++++++++++++++-- .../finality_violation/finality_violation.abi | 26 ++++++++- .../finality_violation/finality_violation.cpp | 8 ++- .../finality_violation/finality_violation.hpp | 4 +- .../finality_violation.wasm | Bin 3351 -> 3538 bytes 5 files changed, 82 insertions(+), 11 deletions(-) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index f6fcd083aa..0e123f6f59 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -242,13 +242,30 @@ namespace savanna { }; }; - struct level_3_commitments_t { + // commitments used in the context of finality violation proofs, minus the base digest + struct level_3_commitments_input { checksum256 reversible_blocks_mroot{}; uint32_t latest_qc_claim_block_num{0}; checksum256 latest_qc_claim_finality_digest{}; block_timestamp_type latest_qc_claim_timestamp; block_timestamp_type timestamp; - checksum256 base_digest{}; + }; + + struct level_3_commitments_t : level_3_commitments_input { + checksum256 base_digest; + + level_3_commitments_t(const level_3_commitments_input& base, const checksum256 _base_digest) : + level_3_commitments_input(base), + base_digest(_base_digest){ + } + + EOSLIB_SERIALIZE(level_3_commitments_t, + (reversible_blocks_mroot) + (latest_qc_claim_block_num) + (latest_qc_claim_finality_digest) + (latest_qc_claim_timestamp) + (timestamp) + (base_digest)) }; // commitments used in the context of finalizer policy transitions @@ -304,7 +321,9 @@ namespace savanna { std::optional pending_finalizer_policy; std::optional last_pending_finalizer_policy_start_timestamp; - //if finality violation info is present (not implemented yet), witness_hash should be the base digest. + std::optional level_3_commitments; + + //if level_3_commitments is present, witness_hash should be the base digest. //if finalizer policy transition info is present, witness_hash should be the level 3 commitments digest. //Otherwise, witness_hash should be level 2 commitments digest checksum256 witness_hash; @@ -315,20 +334,44 @@ namespace savanna { //resolves witness hash if it needs to be calculated checksum256 resolve_witness() const { - //todo : add support for finality violation proofs + checksum256 l3_digest; + + if (level_3_commitments.has_value()){ - //finalizer policy transition proofs + check(pending_finalizer_policy.has_value() + && last_pending_finalizer_policy_start_timestamp.has_value() + && witness_hash!=checksum256(), "must provide full level 2 commitments when providing level 3 commitments"); + //finality violation proofs + auto l3_commitments = level_3_commitments.value(); + + auto l3_input = level_3_commitments_input{ + .reversible_blocks_mroot = l3_commitments.reversible_blocks_mroot, + .latest_qc_claim_block_num = l3_commitments.latest_qc_claim_block_num, + .latest_qc_claim_finality_digest = l3_commitments.latest_qc_claim_finality_digest, + .latest_qc_claim_timestamp = l3_commitments.latest_qc_claim_timestamp, + .timestamp = l3_commitments.timestamp + }; + + auto l3_packed = eosio::pack(level_3_commitments_t{ l3_input, witness_hash}); + + l3_digest = sha256(l3_packed.data(), l3_packed.size()); + + } + else l3_digest = witness_hash; + if (pending_finalizer_policy.has_value() && last_pending_finalizer_policy_start_timestamp.has_value() && witness_hash!=checksum256()){ + //finalizer policy transition information + checksum256 policy_digest = pending_finalizer_policy.value().digest(); auto l2_packed = eosio::pack(level_2_commitments_t{ .last_pending_fin_pol_digest = policy_digest, .last_pending_fin_pol_start_timestamp = last_pending_finalizer_policy_start_timestamp.value(), - .l3_commitments_digest = witness_hash + .l3_commitments_digest = l3_digest }); checksum256 l2_digest = sha256(l2_packed.data(), l2_packed.size()); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 8f6a59a000..538187f05d 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -4,15 +4,35 @@ "types": [], "structs": [ { - "name": "test", + "name": "violaterule1", + "base": "", + "fields": [] + }, + { + "name": "violaterule2", + "base": "", + "fields": [] + }, + { + "name": "violaterule3", "base": "", "fields": [] } ], "actions": [ { - "name": "test", - "type": "test", + "name": "violaterule1", + "type": "violaterule1", + "ricardian_contract": "" + }, + { + "name": "violaterule2", + "type": "violaterule2", + "ricardian_contract": "" + }, + { + "name": "violaterule3", + "type": "violaterule3", "ricardian_contract": "" } ], diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 26a51e7c7c..3cd13e9c96 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -1,5 +1,11 @@ #include "finality_violation.hpp" -ACTION finality_violation::test(){ +ACTION finality_violation::violaterule1(){ + +} +ACTION finality_violation::violaterule2(){ + +} +ACTION finality_violation::violaterule3(){ } \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 796aa0d4be..3f80a1c41e 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -13,6 +13,8 @@ CONTRACT finality_violation : public contract { public: using contract::contract; - ACTION test(); + ACTION violaterule1(); + ACTION violaterule2(); + ACTION violaterule3(); }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 56beefe887ca6f076bb1a6b72806bcd04fe314fb..d6868b2e1693b6f51753d52c19d4606fc4dc51ba 100755 GIT binary patch delta 140 zcmbO(bxC@|StcfVxyk35WEh1ezhd%8Z55IeU=UCi&=JrRFc2^jFcC20Ud_m$z#yP4 zAgjQjz!=El;51>y#eG-b9Jvy$zy#!iLXF4MQ3b_|r From e295f9dedc17ecd4e886c8a6ccdd706e0d71414c Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 16:36:09 -0600 Subject: [PATCH 10/75] Added rule #1 finality violation action to smart contract, tests --- unittests/finality_proof.hpp | 37 +++- unittests/svnn_finality_violation_tests.cpp | 75 +++++++- .../test-contracts/savanna/CMakeLists.txt | 2 +- .../test-contracts/savanna/common/savanna.hpp | 19 ++ .../finality_violation/finality_violation.abi | 169 ++++++++++++++++-- .../finality_violation/finality_violation.cpp | 24 ++- .../finality_violation/finality_violation.hpp | 6 +- .../finality_violation.wasm | Bin 3538 -> 37744 bytes unittests/test-contracts/savanna/ibc/ibc.abi | 30 ++++ unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 71217 -> 74059 bytes 10 files changed, 340 insertions(+), 22 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 6fcc5527f9..0eedb2a2d5 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -33,13 +33,46 @@ namespace finality_proof { digest_type level_3_commitments_digest() const { return fc::sha256::hash(level_3_commitments); } - + digest_type level_2_commitments_digest() const { return fc::sha256::hash(level_2_commitments); } }; + static std::string bitset_to_input_string(const boost::dynamic_bitset& bitset) { + static const char* hexchar = "0123456789abcdef"; + + boost::dynamic_bitset bs(bitset); + bs.resize((bs.size() + 7) & ~0x7); + assert(bs.size() % 8 == 0); + + std::string result; + result.resize(bs.size() / 4); + for (size_t i = 0; i < bs.size(); i += 4) { + size_t x = 0; + for (size_t j = 0; j < 4; ++j) + x += bs[i+j] << j; + auto slot = i / 4; + result[slot % 2 ? slot - 1 : slot + 1] = hexchar[x]; // flip the two hex digits for each byte + } + return result; + } + + static std::string binary_to_hex(const std::string& bin) { + boost::dynamic_bitset bitset(bin.size()); + for (size_t i = 0; i < bin.size(); ++i) { + if (bin[i] == '1') { + bitset.set(bin.size() - 1 - i); + } + } + return bitset_to_input_string(bitset); + } + + static auto finalizers_string = [](const vote_bitset_t finalizers) { + return bitset_to_input_string(finalizers); + }; + static digest_type hash_pair(const digest_type& a, const digest_type& b) { return fc::sha256::hash(std::pair(a, b)); } @@ -172,7 +205,7 @@ namespace finality_proof { signed_block_ptr block = result.block; - BOOST_REQUIRE(result.onblock_trace->action_traces.size()>0); + //BOOST_REQUIRE(result.onblock_trace->action_traces.size()>0); action_trace onblock_trace = result.onblock_trace->action_traces[0]; diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 23d7eefdca..3829f3af7a 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -20,6 +20,52 @@ using namespace finality_proof; using mvo = mutable_variant_object; + +mvo prepare_proof( const finalizer_policy active_finalizer_policy, + const ibc_block_data_t fake_qc_block, + const ibc_block_data_t fake_proving_block, + const ibc_block_data_t real_qc_block, + const ibc_block_data_t real_proving_block){ + + return mvo() + ("finalizer_policy", active_finalizer_policy) + ("proof_1", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) + ("pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", fake_qc_block.level_3_commitments) + ("witness_hash", fake_qc_block.base_digest) + ("finality_mroot", fake_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", fake_proving_block.qc_data.qc.value().active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(fake_proving_block.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ) + ) + ("proof_2", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) + ("pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", real_qc_block.level_3_commitments) + ("witness_hash", real_qc_block.base_digest) + ("finality_mroot", real_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", real_proving_block.qc_data.qc.value().active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(real_proving_block.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ) + ); + +} + BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_AUTO_TEST_CASE(cluster_vote_propagation_tests) { try { @@ -114,7 +160,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) std::vector policy_sunsets; //observe a stream of blocks as they are received, and store minimal data required to construct finality violation proofs in the future - ibc_block_data_t scan_block(ibc_block_data_t block){ + ibc_block_data_t scan_block(const ibc_block_data_t& block){ if (reversible_blocks.size()>=2){ last_final_block = high_qc_block; @@ -124,7 +170,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) if (reversible_blocks.size()>=1){ high_qc_block = reversible_blocks[reversible_blocks.size()-1]; - high_qc = block.qc_data.qc.value(); + if (block.qc_data.qc.has_value()) high_qc = block.qc_data.qc.value(); } reversible_blocks.push_back(block); @@ -170,6 +216,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //produce a few blocks on the fake chain auto fake_chain_genesis_block_result = light_client_data.scan_block(fake_chain.produce_block()) ; + auto fake_chain_block_1_result = light_client_data.scan_block(fake_chain.produce_block()); auto fake_chain_block_2_result = light_client_data.scan_block(fake_chain.produce_block()); auto fake_chain_block_3_result = light_client_data.scan_block(fake_chain.produce_block()); @@ -215,8 +262,30 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //block #8 on the real chain contains a strong QC over a different block #7 than what the light client recorded from the fake chain //this is a rule #1 finality violation proof + //it can be demonstrated by verifying that a strong QC on a block of a given timestamp conflicts with another strong QC on a different block of the same timestamp + mutable_variant_object valid_rule_1_proof = prepare_proof( fake_chain.active_finalizer_policy, + fake_chain_block_7_result, + fake_chain_block_8_result, + real_chain_block_7_result, + real_chain_block_8_result); + + //we also prepare an invalid proof + mutable_variant_object invalid_rule_1_proof = prepare_proof( fake_chain.active_finalizer_policy, + fake_chain_block_3_result, + fake_chain_block_4_result, + real_chain_block_3_result, + real_chain_block_4_result); + + + action_trace valid_rule_1_proof_trace = real_chain.node0.push_action("violation"_n, "rule1"_n, "violation"_n, valid_rule_1_proof)->action_traces[0]; + + bool last_action_failed = false; + + try {real_chain.node0.push_action("violation"_n, "rule1"_n, "violation"_n, invalid_rule_1_proof);} + catch (const eosio_assert_message_exception& e){last_action_failed = true;} - return; + //verify action has failed, as expected + BOOST_CHECK(last_action_failed); /* policy_indices[0] = 1; // update key used for node0 in policy, which will result in a new policy we will call B diff --git a/unittests/test-contracts/savanna/CMakeLists.txt b/unittests/test-contracts/savanna/CMakeLists.txt index e4502b30f5..ca4ab9affd 100644 --- a/unittests/test-contracts/savanna/CMakeLists.txt +++ b/unittests/test-contracts/savanna/CMakeLists.txt @@ -1 +1 @@ -add_subdirectory( ibc ) \ No newline at end of file +add_subdirectory( finality_violation ) \ No newline at end of file diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index 0e123f6f59..d679d07da3 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -251,6 +251,25 @@ namespace savanna { block_timestamp_type timestamp; }; +/* struct block_finality_data_internal : block_finality_data { + checksum256 resolved_witness_hash; + + uint32_t resolved_last_pending_finalizer_policy_generation = 0; + + block_finality_data_internal(const block_finality_data& base) : block_finality_data(base){ + resolved_witness_hash = base.resolve_witness(); + resolved_last_pending_finalizer_policy_generation = base.last_pending_finalizer_policy_generation.has_value() ? base.last_pending_finalizer_policy_generation.value() : active_finalizer_policy_generation; + } + + checksum256 finality_digest() const { + auto result = eosio::pack(*this); + checksum256 hash = sha256(result.data(), result.size()); + return hash; + } + + EOSLIB_SERIALIZE(block_finality_data_internal, (major_version)(minor_version)(active_finalizer_policy_generation)(resolved_last_pending_finalizer_policy_generation)(finality_mroot)(resolved_witness_hash)) + }; +*/ struct level_3_commitments_t : level_3_commitments_input { checksum256 base_digest; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 538187f05d..b0d2380bbc 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -4,35 +4,184 @@ "types": [], "structs": [ { - "name": "violaterule1", + "name": "block_finality_data", "base": "", - "fields": [] + "fields": [ + { + "name": "major_version", + "type": "uint32" + }, + { + "name": "minor_version", + "type": "uint32" + }, + { + "name": "active_finalizer_policy_generation", + "type": "uint32" + }, + { + "name": "last_pending_finalizer_policy_generation", + "type": "uint32?" + }, + { + "name": "pending_finalizer_policy", + "type": "finalizer_policy_input?" + }, + { + "name": "last_pending_finalizer_policy_start_timestamp", + "type": "block_timestamp_type?" + }, + { + "name": "level_3_commitments", + "type": "level_3_commitments_input?" + }, + { + "name": "witness_hash", + "type": "checksum256" + }, + { + "name": "finality_mroot", + "type": "checksum256" + } + ] + }, + { + "name": "finality_proof", + "base": "", + "fields": [ + { + "name": "qc_block", + "type": "block_finality_data" + }, + { + "name": "active_policy_qc", + "type": "quorum_certificate" + }, + { + "name": "pending_policy_qc", + "type": "quorum_certificate?" + } + ] + }, + { + "name": "finalizer_authority_input", + "base": "", + "fields": [ + { + "name": "description", + "type": "string" + }, + { + "name": "weight", + "type": "uint64" + }, + { + "name": "public_key", + "type": "string" + } + ] + }, + { + "name": "finalizer_policy_input", + "base": "", + "fields": [ + { + "name": "generation", + "type": "uint32" + }, + { + "name": "threshold", + "type": "uint64" + }, + { + "name": "finalizers", + "type": "finalizer_authority_input[]" + } + ] + }, + { + "name": "level_3_commitments_input", + "base": "", + "fields": [ + { + "name": "reversible_blocks_mroot", + "type": "checksum256" + }, + { + "name": "latest_qc_claim_block_num", + "type": "uint32" + }, + { + "name": "latest_qc_claim_finality_digest", + "type": "checksum256" + }, + { + "name": "latest_qc_claim_timestamp", + "type": "block_timestamp_type" + }, + { + "name": "timestamp", + "type": "block_timestamp_type" + } + ] + }, + { + "name": "quorum_certificate", + "base": "", + "fields": [ + { + "name": "finalizers", + "type": "bytes" + }, + { + "name": "signature", + "type": "string" + } + ] + }, + { + "name": "rule1", + "base": "", + "fields": [ + { + "name": "finalizer_policy", + "type": "finalizer_policy_input" + }, + { + "name": "proof_1", + "type": "finality_proof" + }, + { + "name": "proof_2", + "type": "finality_proof" + } + ] }, { - "name": "violaterule2", + "name": "rule2", "base": "", "fields": [] }, { - "name": "violaterule3", + "name": "rule3", "base": "", "fields": [] } ], "actions": [ { - "name": "violaterule1", - "type": "violaterule1", + "name": "rule1", + "type": "rule1", "ricardian_contract": "" }, { - "name": "violaterule2", - "type": "violaterule2", + "name": "rule2", + "type": "rule2", "ricardian_contract": "" }, { - "name": "violaterule3", - "type": "violaterule3", + "name": "rule3", + "type": "rule3", "ricardian_contract": "" } ], diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 3cd13e9c96..c55b90fb23 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -1,11 +1,29 @@ #include "finality_violation.hpp" -ACTION finality_violation::violaterule1(){ +ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ + + check(proof_1.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); + check(proof_2.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); + + block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; + block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; + + check(timestamp_1 == timestamp_2, "proofs must be over blocks that have the same timestamp"); + + checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); + checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); + + check(digest_1 != digest_2, "finality digests must be different"); + + _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); + _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); } -ACTION finality_violation::violaterule2(){ + +ACTION finality_violation::rule2(){ } -ACTION finality_violation::violaterule3(){ + +ACTION finality_violation::rule3(){ } \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 3f80a1c41e..657b567947 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -13,8 +13,8 @@ CONTRACT finality_violation : public contract { public: using contract::contract; - ACTION violaterule1(); - ACTION violaterule2(); - ACTION violaterule3(); + ACTION rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); + ACTION rule2(); + ACTION rule3(); }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index d6868b2e1693b6f51753d52c19d4606fc4dc51ba..23821da0401f1e120369e3fb375b64a4a3d1ff65 100755 GIT binary patch literal 37744 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 literal 3538 zcmd5zovHL z*tOk2CWzhPNfipBKZ?drCLyswK}Z{dZW1zWghH9LO{1`h3FXIrbf~BS^+#bt@SXeY z#7-n&{Nh>hz31F>zH`2F?!C_;zfurHBu;;9nrK?qYBX)sYBf>Q15HODXfd47bdOfB zL2o$YDd7*kt?wnsOOTgn+H83nOG}UhTL^>@5F|A6in-ee<`*LV@nX43#PeuStmKNG zU#SF830CzKfnB_Uge}ex<5e%+gqEukxFlk_n*&!TJr~zAa8xImi?jg0eRoERxAN47Slb79zaRe|)au z&5U?{HcLG^TUM0(9B9o@FKcW+Q_U6Uy{upLy-M!sfNs<3a^PpZjy39IwSzi&$LETf z+002lHxHaxh=@ zs<}cy7OU=HSk7r7=&;sMJ38CyWs2E=qU@q8aH8ZDidl+nC}u{zf?w)ZY}DwEE~r9K zsrWMi`gQjnx_w}v&!zzzkF%r%@BC-XyC=>cf?dp?6U7 zZY!1-B}M#FX>O7B^hE_JVf74uc4XiF1O3kq?g8#S_Wkw)_Jj7Q{W&{f{ZNoY_Gj$f z4msj6GnqQ~!kfRidVS@$PdO4IYwd%P)OWXQABv=YvRykCaVS;ir}@fvuF~UXQfc{} zw_f`5GdIZoxP>RuVG%}Uhadm_kAM1JO^1cgnR>m`N0B{by)1-Lb0{ukl0Ob9Q@a z&wqUI<@_mCKl4oED;&f^6lldMgb_Ln;}4VQ!i`Z>0@hHqeB8hYL#vFWImH+$8n6_; zhvYJ3WiPnMMebv3pI-XsZfz_aB} z2X!MWAw=yS@SSy`Psk`1QG+O|y-Bs6WGzQlbO_^e(%L5^x~)KB7q{F1AKP&$CKSa1 zG$db6y67a@xr0czldK+q*sj%wL5T!n>lyHLBF}SO)X!0-dnMuw$tEg}@~k!~ZlHh| z6VQQOxV(~dP354t5F21_$F+Ga9zmOth3UYHQGl<=K}DN@WsWOJUXMU&>dLJou^CJy ztEjtbh`hte!Q0F;X|i-upK>Br1Sg-Yeukq1#v3W?rLdQr&@jE1QoI9kKInjaI~B%S zOQ{z@>28-)X(EP7@y8)$J_9e1EP1Yy4V#pUrf=>@u zX)R@c((0yjvy;v>q{XFMPpMl#`LF3*-=xD3OlQ4|4z%xg(EuAWyVF1- z9o+%a2A$>cZQXgTlTH)4aCcV6)f*p$&I)q~HycARoz*Tn(EhTE4&b-ZF}KipP19+1 z(*e>3omEZeTYPqK^Q;lBp1rL-c{Z)PguTAJ!b`Rh$6n?G^&t{uj*T=%EbQgWnU$P% z9ZDR9HNpklNaG*~?aR9#n;WZo1tSi9*TFcO<~I^n_mZ^-HzA*lA;7vq9}K<;>S#T0_6=T{?Tw;f!CT4h{&$<$1 zS+)mpX&4axe?YABMnu0Wt@}>+xw#G!Nmij_Qz{_B}Rnj6?i8Up8sGkor1)L@sWKul~yYL;w2N2RC*;{kKEcrskf!u6fHU7Zss|#QI!3&44PMzHSDBjdt9z~W}cZvQ5ds72v diff --git a/unittests/test-contracts/savanna/ibc/ibc.abi b/unittests/test-contracts/savanna/ibc/ibc.abi index 5ce49e0c63..9ef9d87bf7 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.abi +++ b/unittests/test-contracts/savanna/ibc/ibc.abi @@ -112,6 +112,10 @@ "name": "last_pending_finalizer_policy_start_timestamp", "type": "block_timestamp_type?" }, + { + "name": "level_3_commitments", + "type": "level_3_commitments_input?" + }, { "name": "witness_hash", "type": "checksum256" @@ -266,6 +270,32 @@ } ] }, + { + "name": "level_3_commitments_input", + "base": "", + "fields": [ + { + "name": "reversible_blocks_mroot", + "type": "checksum256" + }, + { + "name": "latest_qc_claim_block_num", + "type": "uint32" + }, + { + "name": "latest_qc_claim_finality_digest", + "type": "checksum256" + }, + { + "name": "latest_qc_claim_timestamp", + "type": "block_timestamp_type" + }, + { + "name": "timestamp", + "type": "block_timestamp_type" + } + ] + }, { "name": "permission_level", "base": "", diff --git a/unittests/test-contracts/savanna/ibc/ibc.wasm b/unittests/test-contracts/savanna/ibc/ibc.wasm index c1aafc3841eefa2c16290982c2d4713f07375cc8..ee85116aaf26ffccc365be40d3b0da76b5b0772d 100755 GIT binary patch delta 7648 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 Date: Wed, 31 Jul 2024 16:36:41 -0600 Subject: [PATCH 11/75] Remove commented out code --- .../test-contracts/savanna/common/savanna.hpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index d679d07da3..0e123f6f59 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -251,25 +251,6 @@ namespace savanna { block_timestamp_type timestamp; }; -/* struct block_finality_data_internal : block_finality_data { - checksum256 resolved_witness_hash; - - uint32_t resolved_last_pending_finalizer_policy_generation = 0; - - block_finality_data_internal(const block_finality_data& base) : block_finality_data(base){ - resolved_witness_hash = base.resolve_witness(); - resolved_last_pending_finalizer_policy_generation = base.last_pending_finalizer_policy_generation.has_value() ? base.last_pending_finalizer_policy_generation.value() : active_finalizer_policy_generation; - } - - checksum256 finality_digest() const { - auto result = eosio::pack(*this); - checksum256 hash = sha256(result.data(), result.size()); - return hash; - } - - EOSLIB_SERIALIZE(block_finality_data_internal, (major_version)(minor_version)(active_finalizer_policy_generation)(resolved_last_pending_finalizer_policy_generation)(finality_mroot)(resolved_witness_hash)) - }; -*/ struct level_3_commitments_t : level_3_commitments_input { checksum256 base_digest; From 84f6c084296e79dcb60a6a80b632f4b5121201a8 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 17:50:46 -0600 Subject: [PATCH 12/75] Removed unused parameter in svnn_ibc_tests --- unittests/svnn_ibc_tests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 104915c6b0..20ea0e26fd 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -490,7 +490,6 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("pending_finalizer_policy_generation", 2) ("witness_hash", block_10_result.level_2_commitments_digest()) ("finality_mroot", block_10_result.finality_root) ) From 3fc7806eab199aaf0c5ef2beb10f3bb648a0d1f6 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 18:10:59 -0600 Subject: [PATCH 13/75] Changed pending_finalizer_policy to last_pending_finalizer_policy in savanna contract for clarity --- unittests/svnn_ibc_tests.cpp | 4 +++- unittests/test-contracts/savanna/CMakeLists.txt | 4 +++- unittests/test-contracts/savanna/common/savanna.hpp | 8 ++++---- .../savanna/finality_violation/finality_violation.abi | 2 +- unittests/test-contracts/savanna/ibc/ibc.abi | 2 +- unittests/test-contracts/savanna/ibc/ibc.cpp | 6 +++--- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 20ea0e26fd..0621da985a 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -570,7 +570,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("minor_version", 0) ("active_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_generation", 2) - ("pending_finalizer_policy", cluster.last_pending_finalizer_policy) + ("last_pending_finalizer_policy", cluster.last_pending_finalizer_policy) ("witness_hash", block_10_result.level_3_commitments_digest()) ("last_pending_finalizer_policy_start_timestamp", block_10_result.last_pending_finalizer_policy_start_timestamp ) ("finality_mroot", block_10_result.finality_root) @@ -647,6 +647,8 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) // The QC provided to prove this also proves a commitment from finalizers to this policy, so the smart contract can accept it. action_trace check_heavy_proof_4_trace = cluster.node0.push_action("ibc"_n, "checkproof"_n, "ibc"_n, heavy_proof_4)->action_traces[0]; + return; + BOOST_CHECK(true); // now that we have successfully proven finalizer policy generation #2, the contract has it, and we can prove heavy_proof_5 action_trace check_heavy_proof_5_trace = cluster.node0.push_action("ibc"_n, "checkproof"_n, "ibc"_n, heavy_proof_5)->action_traces[0]; diff --git a/unittests/test-contracts/savanna/CMakeLists.txt b/unittests/test-contracts/savanna/CMakeLists.txt index ca4ab9affd..894f9c3cfb 100644 --- a/unittests/test-contracts/savanna/CMakeLists.txt +++ b/unittests/test-contracts/savanna/CMakeLists.txt @@ -1 +1,3 @@ -add_subdirectory( finality_violation ) \ No newline at end of file +add_subdirectory( ibc ) +add_subdirectory( finality_violation ) + diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index 0e123f6f59..4770cbe2fe 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -318,7 +318,7 @@ namespace savanna { //Allows the contract to obtain knowledge about them and to record them in its internal state. - std::optional pending_finalizer_policy; + std::optional last_pending_finalizer_policy; std::optional last_pending_finalizer_policy_start_timestamp; std::optional level_3_commitments; @@ -338,7 +338,7 @@ namespace savanna { if (level_3_commitments.has_value()){ - check(pending_finalizer_policy.has_value() + check(last_pending_finalizer_policy.has_value() && last_pending_finalizer_policy_start_timestamp.has_value() && witness_hash!=checksum256(), "must provide full level 2 commitments when providing level 3 commitments"); @@ -360,13 +360,13 @@ namespace savanna { } else l3_digest = witness_hash; - if (pending_finalizer_policy.has_value() + if (last_pending_finalizer_policy.has_value() && last_pending_finalizer_policy_start_timestamp.has_value() && witness_hash!=checksum256()){ //finalizer policy transition information - checksum256 policy_digest = pending_finalizer_policy.value().digest(); + checksum256 policy_digest = last_pending_finalizer_policy.value().digest(); auto l2_packed = eosio::pack(level_2_commitments_t{ .last_pending_fin_pol_digest = policy_digest, diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index b0d2380bbc..0fe5936e8a 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -24,7 +24,7 @@ "type": "uint32?" }, { - "name": "pending_finalizer_policy", + "name": "last_pending_finalizer_policy", "type": "finalizer_policy_input?" }, { diff --git a/unittests/test-contracts/savanna/ibc/ibc.abi b/unittests/test-contracts/savanna/ibc/ibc.abi index 9ef9d87bf7..e8f9ba6a0b 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.abi +++ b/unittests/test-contracts/savanna/ibc/ibc.abi @@ -105,7 +105,7 @@ "type": "uint32?" }, { - "name": "pending_finalizer_policy", + "name": "last_pending_finalizer_policy", "type": "finalizer_policy_input?" }, { diff --git a/unittests/test-contracts/savanna/ibc/ibc.cpp b/unittests/test-contracts/savanna/ibc/ibc.cpp index 4331c7198b..4802e10b84 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.cpp +++ b/unittests/test-contracts/savanna/ibc/ibc.cpp @@ -103,11 +103,11 @@ void ibc::_check_finality_proof(const finality_proof& finality_proof, const bloc auto target = std::get(target_block_proof_of_inclusion.target); - check(target.finality_data.pending_finalizer_policy.has_value(), "must provide pending finalizer policy for transition blocks"); + check(target.finality_data.last_pending_finalizer_policy.has_value(), "must provide pending finalizer policy for transition blocks"); - _check_qc(finality_proof.pending_policy_qc.value(), block_finality_data_internal(finality_proof.qc_block).finality_digest(), target.finality_data.pending_finalizer_policy.value()); + _check_qc(finality_proof.pending_policy_qc.value(), block_finality_data_internal(finality_proof.qc_block).finality_digest(), target.finality_data.last_pending_finalizer_policy.value()); - _maybe_set_finalizer_policy(target.finality_data.pending_finalizer_policy.value(), target.dynamic_data.block_num); + _maybe_set_finalizer_policy(target.finality_data.last_pending_finalizer_policy.value(), target.dynamic_data.block_num); } From 5a26775b2e46f7ba6c15f852ee4d28b552044630 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 18:13:43 -0600 Subject: [PATCH 14/75] Updated finality violation tests to reflect new name --- unittests/svnn_finality_violation_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 3829f3af7a..77467ad432 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -36,7 +36,7 @@ mvo prepare_proof( const finalizer_policy active_finalizer_policy, ("active_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) - ("pending_finalizer_policy", active_finalizer_policy) + ("last_pending_finalizer_policy", active_finalizer_policy) ("level_3_commitments", fake_qc_block.level_3_commitments) ("witness_hash", fake_qc_block.base_digest) ("finality_mroot", fake_qc_block.finality_root) @@ -53,7 +53,7 @@ mvo prepare_proof( const finalizer_policy active_finalizer_policy, ("active_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) - ("pending_finalizer_policy", active_finalizer_policy) + ("last_pending_finalizer_policy", active_finalizer_policy) ("level_3_commitments", real_qc_block.level_3_commitments) ("witness_hash", real_qc_block.base_digest) ("finality_mroot", real_qc_block.finality_root) From 90f5f458dd17cad2087d68757b176648f032bbbc Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 31 Jul 2024 18:16:34 -0600 Subject: [PATCH 15/75] Renamed last_pending_finalizer_policy_generation to pending_finalizer_policy_generation in savanna smart contracts and tests for clarity --- unittests/svnn_finality_violation_tests.cpp | 4 ++-- unittests/svnn_ibc_tests.cpp | 6 +++--- unittests/test-contracts/savanna/common/savanna.hpp | 4 ++-- .../savanna/finality_violation/finality_violation.abi | 2 +- unittests/test-contracts/savanna/ibc/ibc.abi | 2 +- unittests/test-contracts/savanna/ibc/ibc.cpp | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 77467ad432..e81dc360a6 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -34,7 +34,7 @@ mvo prepare_proof( const finalizer_policy active_finalizer_policy, ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) ("last_pending_finalizer_policy", active_finalizer_policy) ("level_3_commitments", fake_qc_block.level_3_commitments) @@ -51,7 +51,7 @@ mvo prepare_proof( const finalizer_policy active_finalizer_policy, ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) ("last_pending_finalizer_policy", active_finalizer_policy) ("level_3_commitments", real_qc_block.level_3_commitments) diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 0621da985a..b5fb1b5353 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -548,7 +548,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_generation", 2) + ("pending_finalizer_policy_generation", 2) ("witness_hash", block_11_result.level_2_commitments_digest()) ("finality_mroot", block_11_result.finality_root) ) @@ -569,7 +569,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_generation", 2) + ("pending_finalizer_policy_generation", 2) ("last_pending_finalizer_policy", cluster.last_pending_finalizer_policy) ("witness_hash", block_10_result.level_3_commitments_digest()) ("last_pending_finalizer_policy_start_timestamp", block_10_result.last_pending_finalizer_policy_start_timestamp ) @@ -618,7 +618,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_generation", 2) + ("pending_finalizer_policy_generation", 2) ("witness_hash", block_11_result.level_2_commitments_digest()) ("finality_mroot", block_11_result.finality_root) ) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index 4770cbe2fe..9fc8c32766 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -314,7 +314,7 @@ namespace savanna { //finalizer_policy_generation for this block uint32_t active_finalizer_policy_generation; - std::optional last_pending_finalizer_policy_generation; + std::optional pending_finalizer_policy_generation; //Allows the contract to obtain knowledge about them and to record them in its internal state. @@ -394,7 +394,7 @@ namespace savanna { block_finality_data_internal(const block_finality_data& base) : block_finality_data(base){ resolved_witness_hash = base.resolve_witness(); - resolved_last_pending_finalizer_policy_generation = base.last_pending_finalizer_policy_generation.has_value() ? base.last_pending_finalizer_policy_generation.value() : active_finalizer_policy_generation; + resolved_last_pending_finalizer_policy_generation = base.pending_finalizer_policy_generation.has_value() ? base.pending_finalizer_policy_generation.value() : active_finalizer_policy_generation; } checksum256 finality_digest() const { diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 0fe5936e8a..bb4486d180 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -20,7 +20,7 @@ "type": "uint32" }, { - "name": "last_pending_finalizer_policy_generation", + "name": "pending_finalizer_policy_generation", "type": "uint32?" }, { diff --git a/unittests/test-contracts/savanna/ibc/ibc.abi b/unittests/test-contracts/savanna/ibc/ibc.abi index e8f9ba6a0b..3b77055a9a 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.abi +++ b/unittests/test-contracts/savanna/ibc/ibc.abi @@ -101,7 +101,7 @@ "type": "uint32" }, { - "name": "last_pending_finalizer_policy_generation", + "name": "pending_finalizer_policy_generation", "type": "uint32?" }, { diff --git a/unittests/test-contracts/savanna/ibc/ibc.cpp b/unittests/test-contracts/savanna/ibc/ibc.cpp index 4802e10b84..528836e506 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.cpp +++ b/unittests/test-contracts/savanna/ibc/ibc.cpp @@ -97,7 +97,7 @@ void ibc::_check_finality_proof(const finality_proof& finality_proof, const bloc //verify QC. If QC is valid, it means that we have reached finality on the block referenced by the finality_mroot _check_qc(finality_proof.active_policy_qc, block_finality_data_internal(finality_proof.qc_block).finality_digest(), finalizer_policy); - if (finality_proof.qc_block.last_pending_finalizer_policy_generation.has_value()){ + if (finality_proof.qc_block.pending_finalizer_policy_generation.has_value()){ check(std::holds_alternative(target_block_proof_of_inclusion.target), "must provide extended data for transition blocks"); From e7d3263038a4efe41b248ebc0f1c305cd4f5ad86 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 05:42:28 -0600 Subject: [PATCH 16/75] Removed temporary checkpoints from svnn_ibc_tests --- unittests/svnn_ibc_tests.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index b5fb1b5353..e4b2cba33a 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -647,18 +647,13 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) // The QC provided to prove this also proves a commitment from finalizers to this policy, so the smart contract can accept it. action_trace check_heavy_proof_4_trace = cluster.node0.push_action("ibc"_n, "checkproof"_n, "ibc"_n, heavy_proof_4)->action_traces[0]; - return; - - BOOST_CHECK(true); // now that we have successfully proven finalizer policy generation #2, the contract has it, and we can prove heavy_proof_5 action_trace check_heavy_proof_5_trace = cluster.node0.push_action("ibc"_n, "checkproof"_n, "ibc"_n, heavy_proof_5)->action_traces[0]; - BOOST_CHECK(true); // we now test light proof we should still be able to verify a proof of finality for block #2 without finality proof, // since the previous root is still cached cluster.node0.push_action("ibc"_n, "checkproof"_n, "ibc"_n, light_proof_1); - BOOST_CHECK(true); cluster.produce_blocks(10); //advance 5 seconds // the root is still cached when performing this action, so the action succeeds. @@ -666,7 +661,6 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) // so subsequent calls with the same action data will fail cluster.node0.push_action("ibc"_n, "checkproof"_n, "ibc"_n, light_proof_1); - BOOST_CHECK(true); cluster.produce_block(); //advance 1 block to avoid duplicate transaction last_action_failed = false; From 9e90bbf3411d751ea6aa28eeea59e4a3b8cf327e Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 06:18:49 -0600 Subject: [PATCH 17/75] Small refactor of finality violation tests --- unittests/svnn_finality_violation_tests.cpp | 43 ++++++++++++++++----- unittests/svnn_ibc_tests.cpp | 32 +++++++-------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index e81dc360a6..bcdf1df980 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -66,6 +66,26 @@ mvo prepare_proof( const finalizer_policy active_finalizer_policy, } +bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_name rule, const mvo proof){ + + bool last_action_failed = false; + try {chain.node0.push_action("violation"_n, rule, "violation"_n, proof);} + catch (const eosio_assert_message_exception& e){last_action_failed = true;} + + return !last_action_failed; + +} + +bool shouldFail(const finality_proof::proof_test_cluster& chain, const account_name rule, const mvo proof){ + + bool last_action_failed = false; + try {chain.node0.push_action("violation"_n, rule, "violation"_n, proof);} + catch (const eosio_assert_message_exception& e){last_action_failed = true;} + + return last_action_failed; + +} + BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_AUTO_TEST_CASE(cluster_vote_propagation_tests) { try { @@ -269,23 +289,28 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) real_chain_block_7_result, real_chain_block_8_result); - //we also prepare an invalid proof - mutable_variant_object invalid_rule_1_proof = prepare_proof( fake_chain.active_finalizer_policy, + //we also prepare a few invalid proofs, which the contract must reject + mutable_variant_object invalid_rule_1_proof_1 = prepare_proof( fake_chain.active_finalizer_policy, fake_chain_block_3_result, fake_chain_block_4_result, real_chain_block_3_result, - real_chain_block_4_result); - + real_chain_block_4_result); //same finality digest, not a violation - action_trace valid_rule_1_proof_trace = real_chain.node0.push_action("violation"_n, "rule1"_n, "violation"_n, valid_rule_1_proof)->action_traces[0]; + mutable_variant_object invalid_rule_1_proof_2 = prepare_proof( fake_chain.active_finalizer_policy, + fake_chain_block_3_result, + fake_chain_block_4_result, + real_chain_block_4_result, + real_chain_block_5_result); //different timestamps - bool last_action_failed = false; - try {real_chain.node0.push_action("violation"_n, "rule1"_n, "violation"_n, invalid_rule_1_proof);} - catch (const eosio_assert_message_exception& e){last_action_failed = true;} + BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); + BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); + BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_2)); //verify action has failed, as expected - BOOST_CHECK(last_action_failed); + // + + //BOOST_CHECK(); /* policy_indices[0] = 1; // update key used for node0 in policy, which will result in a new policy we will call B diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index e4b2cba33a..20eb912514 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -18,7 +18,7 @@ using namespace eosio::testing; using mvo = mutable_variant_object; -std::string bitset_to_input_string(const boost::dynamic_bitset& bitset) { +/*std::string bitset_to_input_string(const boost::dynamic_bitset& bitset) { static const char* hexchar = "0123456789abcdef"; boost::dynamic_bitset bs(bitset); @@ -50,7 +50,7 @@ std::string binary_to_hex(const std::string& bin) { auto active_finalizers_string = [](const finality_proof::ibc_block_data_t& bd) { return bitset_to_input_string(bd.qc_data.qc.value().active_policy_sig.strong_votes.value()); }; - +*/ BOOST_AUTO_TEST_SUITE(svnn_ibc) BOOST_AUTO_TEST_CASE(ibc_test) { try { @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_4_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", active_finalizers_string(block_4_result)) + ("finalizers", finality_proof::finalizers_string(block_4_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -158,7 +158,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_4_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", active_finalizers_string(block_4_result)) + ("finalizers", finality_proof::finalizers_string(block_4_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_5_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", active_finalizers_string(block_5_result)) + ("finalizers", finality_proof::finalizers_string(block_5_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -393,7 +393,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_9_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", active_finalizers_string(block_9_result)) + ("finalizers", finality_proof::finalizers_string(block_9_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -495,7 +495,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_11_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", active_finalizers_string(block_11_result)) + ("finalizers", finality_proof::finalizers_string(block_11_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -554,11 +554,11 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_12_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", active_finalizers_string(block_12_result)) + ("finalizers", finality_proof::finalizers_string(block_12_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ("pending_policy_qc", mvo() ("signature", block_12_result.qc_data.qc.value().pending_policy_sig.value().sig.to_string()) - ("finalizers", active_finalizers_string(block_12_result)) + ("finalizers", finality_proof::finalizers_string(block_12_result.qc_data.qc.value().pending_policy_sig.value().strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -607,7 +607,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_13_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", active_finalizers_string(block_13_result)) + ("finalizers", finality_proof::finalizers_string(block_13_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -683,12 +683,12 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) chain.set_code( "ibc"_n, eosio::testing::test_contracts::ibc_wasm()); chain.set_abi( "ibc"_n, eosio::testing::test_contracts::ibc_abi()); - std::string bitset_1 = binary_to_hex("0"); - std::string bitset_2 = binary_to_hex("011"); - std::string bitset_3 = binary_to_hex("00011101010"); - std::string bitset_4 = binary_to_hex("11011000100001"); - std::string bitset_5 = binary_to_hex("111111111111111111111"); - std::string bitset_6 = binary_to_hex("000000111111111111111"); + std::string bitset_1 = finality_proof::binary_to_hex("0"); + std::string bitset_2 = finality_proof::binary_to_hex("011"); + std::string bitset_3 = finality_proof::binary_to_hex("00011101010"); + std::string bitset_4 = finality_proof::binary_to_hex("11011000100001"); + std::string bitset_5 = finality_proof::binary_to_hex("111111111111111111111"); + std::string bitset_6 = finality_proof::binary_to_hex("000000111111111111111"); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() ("bitset_string", "00") From 484c229cc2d94391617030e5890a8d60df5ad2c7 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 06:41:01 -0600 Subject: [PATCH 18/75] Prepared test for rule #2 --- unittests/svnn_finality_violation_tests.cpp | 31 +++++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index bcdf1df980..dd7b92b6df 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -302,15 +302,34 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) real_chain_block_4_result, real_chain_block_5_result); //different timestamps + BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); + BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); + BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_2)); - BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); - BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); - BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_2)); + //we temporarilly disable a finalizer on the fake chain, which serves to set up a proof of violation of rule #2 + fake_chain.vote_propagation = {1,0,0}; - //verify action has failed, as expected - // + auto fake_chain_block_9_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_9_result = real_chain.produce_block(); - //BOOST_CHECK(); + //fake chain is no longer making progress. Real chain has a QC on #9, but fake chain doesn't + auto fake_chain_block_10_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_10_result = real_chain.produce_block(); + + BOOST_TEST(!fake_chain_block_10_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_10_result.qc_data.qc.has_value()); + + auto fake_chain_block_11_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_11_result = real_chain.produce_block(); + + //last recorded QC on fake chain is over #10. #10 claims a QC over #8. Provide real block #9, which is a proof of violation of rule #2 + mutable_variant_object valid_rule_2_proof = prepare_proof( fake_chain.active_finalizer_policy, + fake_chain_block_10_result, + fake_chain_block_11_result, + real_chain_block_9_result, + real_chain_block_10_result); + + BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof)); /* policy_indices[0] = 1; // update key used for node0 in policy, which will result in a new policy we will call B From 4da02e78b31474d23dc4e4f77f6d26f219ae7916 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 07:33:52 -0600 Subject: [PATCH 19/75] Implemented rule #2 verification in savanna contracts, improved proof generation used in finality violation tests --- unittests/finality_proof.hpp | 9 +--- unittests/svnn_finality_violation_tests.cpp | 44 +++++++++++------- .../finality_violation/finality_violation.abi | 15 +++++- .../finality_violation/finality_violation.cpp | 21 ++++++++- .../finality_violation/finality_violation.hpp | 2 +- .../finality_violation.wasm | Bin 37744 -> 40902 bytes 6 files changed, 64 insertions(+), 27 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 0eedb2a2d5..4b295c38e3 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -266,6 +266,7 @@ namespace finality_proof { digest_type action_mroot = finality_data.action_mroot; digest_type base_digest = finality_data.base_digest; + // compute commitments used for proving finality violations level_3_commitments_t lvl3_commitments { .reversible_blocks_mroot = finality_data.reversible_blocks_mroot, .latest_qc_claim_block_num = finality_data.latest_qc_claim_block_num, @@ -275,19 +276,13 @@ namespace finality_proof { .base_digest = base_digest }; - - // compute commitments used for proving finality violations - //digest_type level_3_commitments_digest = fc::sha256::hash(lvl3_commitments); - + // compute commitments used for proving finalizer policy changes level_2_commitments_t lvl2_commitments { .last_pending_fin_pol_digest = last_pending_finalizer_policy_digest, .last_pending_fin_pol_start_timestamp = last_pending_finalizer_policy_start_timestamp, .l3_commitments_digest = fc::sha256::hash(lvl3_commitments) }; - // compute commitments used for proving finalizer policy changes - //digest_type level_2_commitments_digest = fc::sha256::hash(lvl2_commitments); - // during IF transition, finality_root is always set to an empty digest digest_type finality_root = digest_type(); diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index dd7b92b6df..9997dc4597 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -23,9 +23,9 @@ using mvo = mutable_variant_object; mvo prepare_proof( const finalizer_policy active_finalizer_policy, const ibc_block_data_t fake_qc_block, - const ibc_block_data_t fake_proving_block, + const qc_t fake_qc, const ibc_block_data_t real_qc_block, - const ibc_block_data_t real_proving_block){ + const qc_t real_qc){ return mvo() ("finalizer_policy", active_finalizer_policy) @@ -42,8 +42,8 @@ mvo prepare_proof( const finalizer_policy active_finalizer_policy, ("finality_mroot", fake_qc_block.finality_root) ) ("active_policy_qc", mvo() - ("signature", fake_proving_block.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(fake_proving_block.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("signature", fake_qc.active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) ) ) ("proof_2", mvo() @@ -59,8 +59,8 @@ mvo prepare_proof( const finalizer_policy active_finalizer_policy, ("finality_mroot", real_qc_block.finality_root) ) ("active_policy_qc", mvo() - ("signature", real_proving_block.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(real_proving_block.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("signature", real_qc.active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) ) ); @@ -285,22 +285,22 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //it can be demonstrated by verifying that a strong QC on a block of a given timestamp conflicts with another strong QC on a different block of the same timestamp mutable_variant_object valid_rule_1_proof = prepare_proof( fake_chain.active_finalizer_policy, fake_chain_block_7_result, - fake_chain_block_8_result, + fake_chain_block_8_result.qc_data.qc.value(), real_chain_block_7_result, - real_chain_block_8_result); + real_chain_block_8_result.qc_data.qc.value()); //we also prepare a few invalid proofs, which the contract must reject mutable_variant_object invalid_rule_1_proof_1 = prepare_proof( fake_chain.active_finalizer_policy, fake_chain_block_3_result, - fake_chain_block_4_result, + fake_chain_block_4_result.qc_data.qc.value(), real_chain_block_3_result, - real_chain_block_4_result); //same finality digest, not a violation + real_chain_block_4_result.qc_data.qc.value()); //same finality digest, not a violation proof mutable_variant_object invalid_rule_1_proof_2 = prepare_proof( fake_chain.active_finalizer_policy, fake_chain_block_3_result, - fake_chain_block_4_result, + fake_chain_block_4_result.qc_data.qc.value(), real_chain_block_4_result, - real_chain_block_5_result); //different timestamps + real_chain_block_5_result.qc_data.qc.value()); //different timestamps, not a violation proof BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); @@ -309,25 +309,35 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //we temporarilly disable a finalizer on the fake chain, which serves to set up a proof of violation of rule #2 fake_chain.vote_propagation = {1,0,0}; + //produce a block on a fake chain without propagating votes to all nodes auto fake_chain_block_9_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_9_result = real_chain.produce_block(); - //fake chain is no longer making progress. Real chain has a QC on #9, but fake chain doesn't + //restore vote propagation for fake chain. This leaves a one-block gap where no finality progress was achieved + fake_chain.vote_propagation = {1,1,0}; + auto fake_chain_block_10_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_10_result = real_chain.produce_block(); + //Real chain has a QC on #9, but fake chain doesn't BOOST_TEST(!fake_chain_block_10_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_10_result.qc_data.qc.has_value()); - + auto fake_chain_block_11_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_11_result = real_chain.produce_block(); - //last recorded QC on fake chain is over #10. #10 claims a QC over #8. Provide real block #9, which is a proof of violation of rule #2 + //Things are back to normal, and we have a QC on both chains + BOOST_TEST(fake_chain_block_11_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_11_result.qc_data.qc.has_value()); + + //Light client recorded the last QC on fake chain, which is over block #10. + //Block #10 claims a QC over block #8, skipping #9. We provide fake block #10 and its QC. + //We also provide the real block #9 and a QC over it, which is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof = prepare_proof( fake_chain.active_finalizer_policy, fake_chain_block_10_result, - fake_chain_block_11_result, + fake_chain_block_11_result.qc_data.qc.value(), real_chain_block_9_result, - real_chain_block_10_result); + real_chain_block_10_result.qc_data.qc.value()); BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof)); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index bb4486d180..0c74b3f2d5 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -160,7 +160,20 @@ { "name": "rule2", "base": "", - "fields": [] + "fields": [ + { + "name": "finalizer_policy", + "type": "finalizer_policy_input" + }, + { + "name": "proof_1", + "type": "finality_proof" + }, + { + "name": "proof_2", + "type": "finality_proof" + } + ] }, { "name": "rule3", diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index c55b90fb23..5ce3a463bd 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -20,7 +20,26 @@ ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, } -ACTION finality_violation::rule2(){ +ACTION finality_violation::rule2(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ + + check(proof_1.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); + check(proof_2.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); + + checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); + checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); + + block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; + block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; + block_timestamp last_claim_timestamp_1 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + block_timestamp last_claim_timestamp_2 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + + bool lessThanRule = last_claim_timestamp_2 < timestamp_1 && timestamp_1 < timestamp_2; + bool greaterThanRule = last_claim_timestamp_1 < timestamp_2 && timestamp_2 < timestamp_1; + + check(lessThanRule || greaterThanRule, "proofs must demonstrate a conflicting time range"); + + _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); + _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 657b567947..8a4a5d8309 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -14,7 +14,7 @@ CONTRACT finality_violation : public contract { using contract::contract; ACTION rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); - ACTION rule2(); + ACTION rule2(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); ACTION rule3(); }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 23821da0401f1e120369e3fb375b64a4a3d1ff65..94ebf53ad37431f49ca8473ecb8abc68b95a3959 100755 GIT binary patch delta 1496 zcmd6nTTC2P7{}*3v$qz}c|dnrshlngWvR5ZR0Ir_q0(|GEVT3zZgyD~mgTZ#cUkPh z0*gh_-lU(=##+mR60L1)a!BQ+7ksF1MUXUTG}!QhZGy(s#0En=GoS&DK06Qp`F;O6 z|1&dZzB50MklQ!ORg*~SooE(w;7L3r?lfGV7NU9u`8r+@lQdU8B2N5L%zzqP5}z_P zX!H7%K)x^ZZ7?Tq&0?gw*t4szb(V7_6j!z0w7T!zTfag+T zVFDvb8*wSI9~M|7=`MjqOx==*VOu6oU9+jFN49@UNjR6{g9Sdv+wA`4yybKYxY>JK zoErF?Ep49>$;Bx)lM!48V`$IK;d*LCH{mb2MzxN|S@$~JE|0?$dnnsPXvSyqe}fh@ z?jC_wys^89oc|hadrIJS#LR4_&Du(EB6DM!MMp*XL+~UM!D1-nJiN?$sw-@d7N~)r zsh%2?LKfsv7J1`MdRmakc zK3yFj*8U@|SbvKvmKsOn-Unurbp*?NI|M%7bUXfu?kUr1lx~oBB!QtsKboEm+d2cfcA1_(&A7ej+I*> z$i6Qh0d5@MUmDF9P~h3eJQh)M;Ozbch%m9@69O)F<=}Vlzg_+6|5*JG*qy^C0GhC} zvRl;+_^>i>g(|Aj{-PMXRu#vq+Da2Xs486X_f_YrUM%lNr9~wV8?6p#xc9!pjJq6% zRVw2;>@bQtepz34Py_RT|R; hx&%RBZ@hIA41zo|B}BbTu;AnQ6AhkogeeW~%O(b)&msWBM>PLL{?xEhbo$?&bkex4SV}H;=Z- zx=A$dZekwY)@-Y%lu9I8N|!ze3EY$xX`}eTK4^nc5ffUGw$Or5QbZ{BgFQ0|C8^Gb zAOG{8`#<+`=ggIhY$3xYts)BrQ57q&PTXNh-4L=9f}X-Lu}C^O!h-m%SOyLFP|UX; zl08Y|nf!QmI-#+BQl%znpfqu}lxKqh?y;W(n8IH+>g>iC;Tcx} zjN)y_hj4+o0lSMkdHyC3gKw~;#DQ_=HcEcsG?NdUk4rv{zqvXV)4aR*^TlKeqVQr5J8*wZ)id5t_-x| z#a)Nl@7K`v;-1rc=3qbn^T4N2en1oTORFNpJ6RDu$s)=@b%k}3tiVd^eX?HRvw_c+ z$Ir`;#0~3zal_`$xrYOqk!MB0s9C0HmcwVJGiR83Mscp;QtMbbPLl{;R(0uw0v zyu@ms8#=Ma_blmg(;4>_VS(Rf)uYzz5gxA102R6QFXhlx3ABE7q)=1> z7^$~IlBerGVbI98HT(p)(CFn0jc);jvA3z$3|0 z@y9+tMA6>=wi8#SA&!0M{wZjAs4ByhlXH3zA(HUdU;}) zZg~Q;Q&q&8R2Ok3brX)^M{fp6M@|j0pReOzr|vH=F5}EdE*!_(BX+W7Us0CDoPjuQ zNiR9rlcuraXVO>M^5n`ky!2f!gHaxPuj~na=j=3KX6!kOpl7ZL*}nqLkNqJjE Date: Thu, 1 Aug 2024 08:06:20 -0600 Subject: [PATCH 20/75] Added savanna contract interface for weak QCs --- .../test-contracts/savanna/common/savanna.hpp | 15 ++++++++++++--- .../finality_violation/finality_violation.abi | 10 +++++++--- .../finality_violation/finality_violation.cpp | 8 ++++---- .../finality_violation.wasm | Bin 40902 -> 41687 bytes unittests/test-contracts/savanna/ibc/ibc.abi | 10 +++++++--- unittests/test-contracts/savanna/ibc/ibc.cpp | 4 ++-- unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 74059 -> 74641 bytes 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index 9fc8c32766..f463171e72 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -12,11 +12,20 @@ using namespace eosio; namespace savanna { - struct quorum_certificate { + struct quorum_certificate_input { //representation of a bitset, where each bit represents the ordinal finalizer position according to canonical sorting rules of the finalizer policy std::vector finalizers; //string representation of a BLS signature std::string signature; + std::optional weak; + }; + + struct quorum_certificate : quorum_certificate_input { + bool weak = false; + + quorum_certificate(const quorum_certificate_input& base){ + if (base.weak.has_value()) weak = base.weak.value(); + } }; struct finalizer_authority_internal { @@ -509,10 +518,10 @@ namespace savanna { block_finality_data qc_block; //signature over finality_digest() of qc_block by active policy generation - quorum_certificate active_policy_qc; + quorum_certificate_input active_policy_qc; //signature over finality_digest() of qc_block by pending policy generation (required during transitions, prohibited otherwise) - std::optional pending_policy_qc; + std::optional pending_policy_qc; }; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 0c74b3f2d5..47e51e0b8f 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -55,11 +55,11 @@ }, { "name": "active_policy_qc", - "type": "quorum_certificate" + "type": "quorum_certificate_input" }, { "name": "pending_policy_qc", - "type": "quorum_certificate?" + "type": "quorum_certificate_input?" } ] }, @@ -126,7 +126,7 @@ ] }, { - "name": "quorum_certificate", + "name": "quorum_certificate_input", "base": "", "fields": [ { @@ -136,6 +136,10 @@ { "name": "signature", "type": "string" + }, + { + "name": "weak", + "type": "bool?" } ] }, diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 5ce3a463bd..326d5686bc 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -15,8 +15,8 @@ ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, check(digest_1 != digest_2, "finality digests must be different"); - _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); - _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); + _check_qc(quorum_certificate(proof_1.active_policy_qc), digest_1, finalizer_policy); + _check_qc(quorum_certificate(proof_2.active_policy_qc), digest_2, finalizer_policy); } @@ -38,8 +38,8 @@ ACTION finality_violation::rule2(const finalizer_policy_input finalizer_policy, check(lessThanRule || greaterThanRule, "proofs must demonstrate a conflicting time range"); - _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); - _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); + _check_qc(quorum_certificate(proof_1.active_policy_qc), digest_1, finalizer_policy); + _check_qc(quorum_certificate(proof_2.active_policy_qc), digest_2, finalizer_policy); } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 94ebf53ad37431f49ca8473ecb8abc68b95a3959..0749ccdd254d76fd026b7ab40c6f7e3470fed727 100755 GIT binary patch delta 2329 zcmds2Z)jXa5Z~E-my5al*+PidBjWZo>M2|kzr=cpA@52P3tAIujHHF8O`4o9O|n! z?(FQ$>~DU%Gq-e^fAu+^-z^lxwM+G3!1r_-vK0(rn>oZ$yxz`VQV)*bv@g&u2v*j(LuiEmqBz9;=0dq^$F9&pPd6<&j<>>eNAu|bEu9QI=)oatnC1m} zUxJir-s_fx&Lxe!UzMO9&-(iS#P0xzH2@SQ>3gh^Go^1fUkt%~@W`p$KLv9|_;su* zA$u6bR7j1&X2{wrY z8defvARTfiLl1mcG;#->d4^SDA^sNTwg_Q>s@nsgyJF;C<*|0;?{E`K(pffvq5y{y zfCgQh?heA1!s%j_;I*;&z$11R)Dk1N69cmY#aB^r; z0^eJBl67%o=;Xnpw%g$|Tw~H&I)A{@fLJ`pGQ405DTkP-?IBs!Q7=R^wbb({BNqq{ zSI~$E!D_t*dUAh+Gt~wK z^Yhf(TyN{HgXc}Y`y`k*UitxG+ypP*BjAMj@Rh9sPU>$?b-;wFpMetNJ;lK^ymcn= EE06{2F8}}l delta 1537 zcmdT@U2IfE6rMA4?{4XqI86}N4W#2`(W+ToeW(=>IyEhjkXqC#q8Jxye{O9RT?Na} ztuzHSSQ;m^2vz&wUx+46Mk9#{B}940#E=jkG(Hj27ZQ`k2je%hb^VL+*-3WhJKvsj z=A7@$z48seG{xf&kpIknl zD84;tQB0BSwS{2tPU^E~)-EjJUuEpc=AXH_y#8Xq^$0gp8z#(|2j&9djHfrgWo$=1 z6>VYDwG<7Q+V%x{&WOz)(V#iI`5Zr%F(;evx9@Iw{XbI3Vk|eXj0&{$;HtgSQKs}P zk2PDzOsTVr7R;BOUo`0pwXqWV`*e%~V);xC#vjyiP^G_8r$FsCr?*d6lWlvri_V!% zyQ|4&!ha-l)Zdahs*-H%k)LvVqCg4U4OZ@nLixA~Wp_>mvfI4*=y)xlFB0Zl_j~3- z*EE&wuE*Y{d-vrOYvjumX_EUL%zF5$gDX88c2IeEKyfYY`pFcTU&r$Fid}v55Z!!( zQ<$t~S`9=z&)@=y8q6;{^3-4&o>-MMv@Lb4j9b}#0>~XTm4ji%+^Oaq>f%U1F5bdh z6ctz&VGb$Yj(8l69A1q)kgrh&<5eyNlp8tz+4YyRyLQH0|}R zLQi>n#upG#+pVs9MgBHVYZXH#b`ui0M|MM)v7H_EyPZd<#N0@c)eZJpr~;qBKSbuA z7rfu$Bof@mG#+Dtv5H7cf8edf3Yh+s$B@fihvA*#C<9e6J#D6TT}#dqa=^lzvXz7V zS#-s`j_w}_<3`|m$Hp*V-=I^IE3^eQg;SH^@o_IBkni;OkSIVEOPp%l}&H1jpdH?V*?KQdNV|vkCOxo!syJX}jrm6Ohu$quHa zzvc#-Y2GUm>NGwVWbh2@lXS!S*|MZP&#z&pt0GT3^h8dqO1;~@QK2_0GVm)*jcJ(?6`79V0Z1z{C(dxlF~eWpWiAXg6e`lg zC0*&*xH<~A*20U0USm0XqYjYERW~3{6#D|iL;wNlLFkfUPFi-!HMdVLxM4mO?0(G+ zGGvqyRd?Kr`I$GiZZ9Ta^YQM4hG#vk3)c_tnz7qR=r_ybOg@l8%9 z;^y7a-5tevnCC=%8xM52#RC+VMt^dM+w#n-9egiwm1RF~%j_0q_P-RGuTK2ljH1Yi zIh#+HoIKVii>L*^YOcQ4`FK_8luo`si=y(^c%a@1pQ%ZYBLX6e*tl;V8k^13P*6Zz z^PISkvf1(puH0?PHS^2xkgmF+vYQh%*DAy3*?7>w8i=(@`Pl1s_c2f28 zpvGWYP`wlIrnU3}FPRgibDq#t=mK{|5nx>vfm-64luIJ;Ty@Q~PUZPG=IGR(-esOd zE+1M&tZJ|=>>(|>`J~8mG$mPSS6$~R($zwJTb`MEk@s#^1M=3)zidg3>WBGrg36gX zf@MTWSbU&0T~1$o<=Mh_yp7JA@HkiBBeslavGNaAZntv9%G$~|;MZ4xBe{TqQo!s` zQO)2BKksh)U01Erh(srP_PQ&cMmxSi^Rq7TbNP|u_=x9QaHDSNm65zUtcmjy~5 z)(q*R%n1e4cl(hxN|%_ot|?bU+|65%s~Nlfv}|uknp5}Qdz8-79GW&B=s+Qf{$lC~ zOWur`ed~wH#q|kZu)iYRoTVj{w|Wi5)6YtvgnCX(^ae3O_tDP}1DdFHC|shZ1aMYi z+O`5gR1(_}rV5D=CZh=3OF}n7&WULxn2FYssoZHhj1xkCT2Qv=H#omCEUjR)_ys&fncH#Z7MNFSMJ=`6tZ6A8vdFIH8ix_tf!b QIxIK-c|ex`zIS-fzi7*aQ~&?~ delta 1365 zcmYjRO=w(I6u#%)H+KeRrn%Khr)e}dv~Qxt8YHPT*7S}g^&pjn79O&g^zB6R0M zGeJZ|x-eY9L8l>#Q2bd;d0Di&DRoh8YCGzH1vj%OVg#jP7gkXG&YcN5i+9es@7(j9 z?>pzs;`wOxT-3ag*`Os!tqDI#qJc%fny{{^64w%D)~bqZs-cXL6yfEj~n- zNX(2%B)&ECLqx7F)7isi@}D2+Y(~vd42IE$RsHEPwLtJ&{?zQYJ6p;YZ2r}gXvuXb zF)w$EwpC33qkhHzJ$v$|HfGDzX;fYTnt3?yH%`B?0ky8_q%8XI&Nb8hFZ_0YbR)u! zjW;f2LEMR*!k11wMs+`PVsV>*QV`sHNd1=m<1&f0`Jflo5oOJJf6LNC^p$^dX(awS z2#74J&AxuLi4>bP7pTB5tTe0Xsipn2V_jm*%L?~V5Zuu*mPWYGBrgs+wxS~IGLXY@ zBfV$&**#BOWblgIgyLkrA@)nbzZCp|;9m~D4Sox_7Nvv3hkU~{a3Ini!+6xXay!v=V zrYlJS08k{zz>k<3jK^3LE(a6YXJBoF#&ZANiSZ|Z zZXd-xd?UC53S@~(a%RN)9I*HDG-#(NeX_=wFckQ;oMsP(g3qtjkJsimif(yqmM{lJ{tx9RlLP5Hzr0Sjim(S^ z8%&$$hop#;FxG|i=3lo&^le)G{Xr$aqj!(>(SZN)i|tuxgqeJwp1J;}8vKON6w)!W bQrC#kU{8Dh>DnuOy$(Wo^pNiQ^Wez82(J~< From 7ea485e258a4232e4ce00e1f47df180f3d7ffe3b Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 09:11:50 -0600 Subject: [PATCH 21/75] Completed savanna contract support for weak QCs --- .../test-contracts/savanna/common/savanna.hpp | 34 +++++++++++------- .../finality_violation/finality_violation.abi | 2 +- .../finality_violation/finality_violation.cpp | 8 ++--- .../finality_violation.wasm | Bin 41687 -> 41578 bytes unittests/test-contracts/savanna/ibc/ibc.abi | 2 +- unittests/test-contracts/savanna/ibc/ibc.cpp | 4 +-- unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 74641 -> 74754 bytes 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index f463171e72..8f1524f538 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -12,20 +12,22 @@ using namespace eosio; namespace savanna { + constexpr std::array weak_bls_sig_postfix = { 'W', 'E', 'A', 'K' }; + using weak_digest_t = std::array; + + inline std::string create_weak_digest(const checksum256& digest) { + weak_digest_t res; + std::memcpy(res.begin(), digest.data(), digest.size()); + std::memcpy(res.begin() + digest.size(), weak_bls_sig_postfix.data(), weak_bls_sig_postfix.size()); + return std::string(res.begin(), res.end()); + } + struct quorum_certificate_input { //representation of a bitset, where each bit represents the ordinal finalizer position according to canonical sorting rules of the finalizer policy std::vector finalizers; //string representation of a BLS signature std::string signature; - std::optional weak; - }; - - struct quorum_certificate : quorum_certificate_input { - bool weak = false; - - quorum_certificate(const quorum_certificate_input& base){ - if (base.weak.has_value()) weak = base.weak.value(); - } + std::optional is_weak; }; struct finalizer_authority_internal { @@ -139,7 +141,7 @@ namespace savanna { } //verify that the quorum certificate over the finality digest is valid - void _check_qc(const quorum_certificate& qc, const checksum256& finality_digest, const finalizer_policy_input finalizer_policy){ + void _check_qc(const quorum_certificate_input& qc, const checksum256& finality_digest, const finalizer_policy_input finalizer_policy){ auto fa_itr = finalizer_policy.finalizers.begin(); auto fa_end_itr = finalizer_policy.finalizers.end(); size_t finalizer_count = finalizer_policy.finalizers.size(); @@ -168,15 +170,20 @@ namespace savanna { //verify that we have enough vote weight to meet the quorum threshold of the target policy check(weight>=finalizer_policy.threshold, "insufficient signatures to reach quorum"); - std::array fd_data = finality_digest.extract_as_byte_array(); - std::string message(fd_data.begin(), fd_data.end()); + + std::string message; + + if (qc.is_weak.has_value() && qc.is_weak.value() == true) message = create_weak_digest(finality_digest); + else { + std::array fd_data = finality_digest.extract_as_byte_array(); + message = std::string(fd_data.begin(), fd_data.end()); + } std::string s_agg_pub_key = encode_g1_to_bls_public_key(agg_pub_key); //verify signature validity check(_verify(s_agg_pub_key, qc.signature, message), "signature verification failed"); } - struct authseq { name account; uint64_t sequence = 0; @@ -413,6 +420,7 @@ namespace savanna { } EOSLIB_SERIALIZE(block_finality_data_internal, (major_version)(minor_version)(active_finalizer_policy_generation)(resolved_last_pending_finalizer_policy_generation)(finality_mroot)(resolved_witness_hash)) + }; //used in "heavy" proofs, where verification of finality digest is performed diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 47e51e0b8f..e8e6b927c3 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -138,7 +138,7 @@ "type": "string" }, { - "name": "weak", + "name": "is_weak", "type": "bool?" } ] diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 326d5686bc..5ce3a463bd 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -15,8 +15,8 @@ ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, check(digest_1 != digest_2, "finality digests must be different"); - _check_qc(quorum_certificate(proof_1.active_policy_qc), digest_1, finalizer_policy); - _check_qc(quorum_certificate(proof_2.active_policy_qc), digest_2, finalizer_policy); + _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); + _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); } @@ -38,8 +38,8 @@ ACTION finality_violation::rule2(const finalizer_policy_input finalizer_policy, check(lessThanRule || greaterThanRule, "proofs must demonstrate a conflicting time range"); - _check_qc(quorum_certificate(proof_1.active_policy_qc), digest_1, finalizer_policy); - _check_qc(quorum_certificate(proof_2.active_policy_qc), digest_2, finalizer_policy); + _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); + _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 0749ccdd254d76fd026b7ab40c6f7e3470fed727..4433675268107d5065e4d7e60e36c245549b414a 100755 GIT binary patch delta 940 zcmb`FO-NKx6vxlK?@2RGGH<~0E1hT7sO6+2&8U+fcVs{^EP`fq6SNR)0!e8IE1l60 zC&ZfJ>Cf=lj z$P2px0$HGg8*+`WBGw?RGr=`AKOG3(E0&f(sFV>^nrpLFKpkw+t-9idV|4(ADuW&m zFhi9?#0wc|)$fI@v>Ik!h;B$%jxi4u8nP0S%m>AWbOW+FC^4s^KohLT90q($ThDA{ zz;g{-V?f#>+qhvAnIqGgR+>K0s8qM6*Sryn#qO!JtJy$j@>}%+i&6)p!17XNhR^rpagDfeXLwSrmYR@E8mPF@J0lptek@e~LKBB$D zBl+1JPBMRV53zIf1XZt$*3u>H!(ld#Kk(}x`T?$-ucp|M*l*H>@dPa|uO=3aE`Pt$ qm@5BmegKzK^g?peM3}SZa^&I;3!B7SQ*JdB5Uu!jYE5m~2JjP{iW+kO delta 1083 zcmeH`%WG3X6o+T#-ln%C!K~717r7{=u^L~o38`(9n-B;pwG{HeRRx7`n~fMBw9qt) z7^-=cgV7>Hx>2N3g)!KLEr?n~#FiqsD}nBGqY}iWXKutQxac3?=9}L+Gv_g9m`|_R z$JgvhMN$?&5ObDji%^xEY-x@!OXCi@z(c!EY-5jGdHHaP^2U)B8nCI7+U8{)xJcS6 z)opPWL`^h{jt-k{8?q9OX!Uo*M$KZ&O^8O%MXW@XTukAkz5-+St*{>Tb5HMc7OoQ= z!$QO-5mnYmFBucdQF>XGg)`M$MlcoAW|)rYL@pVvJBEb;@VReUmbE&8T7Nl;QA0gW( zN0C=2^TytBWHJ|{1wNnKPO;9LD`lA$U_RFgnd=oSZ_b}YR+C;noo{C-A5I-ZHl{A& z>GTah27v21R4q zUTjckP$@5Rk`TcdAfbu}Te-$SL%0|U_;G}O{(*xwkyw?IR3wrHXLbwLANtEb^FA{> z?=v&cJ9jM?q<1@|(0W<8a@eSoTU{_@O!3~EEbNOj5(_s#qsII~vMN5!@_I8 z@!-S)LNb-5262kXbc2%2mhqcXWPMODRdrZno(u#6=OmofBv8S4d%^e6hzyiq|6fXJ zimuspEN#~_1?-E+#VnBo#zssjMJ)=tvIW&Peg0B$DzC|%XfzkUKLPd`NsAvq%89E? z!660I-L5N{Zb54-PXLW+C-|JDsW50fv-GJ1Om13v>OYg8tEA-3m0bWseBgqI*RIOJ zCHQ#NeyK8|2y&y;;9=UZywNSDlal9TU1cid33jCc-KdB!uLK}XLIPG*f8fm#gL^hD0~|=!vRrqq34~Yg5YbO1rER4OKdf&=d+wA}}Qy z6YHFuFCLc1D&eVh$s{K6MS0EYY;OpI>IA>gW}d37Wp2{3IrK0?wkmhYeEPa4O55hYA2$Y)5&Oo&;x+-7;>GwGK4Ucdt~%hvYOHZIX;tdCQuE zylU-}5aoN-8c zF0pF!3z*nk-7A51P%vuOr33$_CN>p0{irxq%Mw4S$QJ{EILc4f&dP|2YP{;KKSsaU z7_Qk{7HOC0hH*3X@zL62NnQA8G!yuRK71#o$jK+JdG9gg8kY5RmPiX5GRoJd2)H~y z))%3@QJ29l)je*aV$-^e5nkJ6uS%H(+slul@yiHPDUj#fpig*Y7Xuf%&3=d>K&J~9J-A?hvwloBl6i#0(A2)k8I}2N6IjH_0d_p=}49t zJBbOak5(%fK7YChTK9&_5iScOBeYm%Ut&uu@C$s_H*Q9-k-GjkB-ovA?E za%TthbGRGf-Dk6K)(2!BqNZ`0U+lhf&JubeYhsX)-?I8(|_s}EW z>UkOM?p_a{d1wYN>6J}yH`?L8F%s^$oXuPMs?695w2S)vNc~QK1Y6`K*NLY7I?X`u zb^AKKmmF(C`>0Q2C{{`j1fyQQ@}|o~Bro|lZq7n9VW1T4vVjh?af1s94{ksW+Xg?f zD)O_E=`y&Xdp!v#e7P|dsJaD-Qm%%?=$hf^b@{IwA(m;U>B{r_>p(C*E} zrw_Ks@B;t#PaU&!@8dfer|(Pu*7@d9YZ9+vjQ)We7Q6%C4PF7jFyT*;fv#X7Fqxq?*th z1O-UDU13*nu>vz`B|X8N3OtL!zYDvPz*=B)IX_2g`y{F#E|~-uol<3)wk(+IhKz8I N3rZpUrV7)=zW_OHrJn!* delta 2009 zcmZ`(e{2(V6u)=h>s?`6xC{j4*KTdscG6~vU=ikECjr7xmY@=ufMYs_2O>n1@gr~-*JeOV5u zoO+rzu9*jM~^qqM_8Tv(bK7d3@b;BnqA*P+=(w5PbmgxzpUxgOROR}2w6nfqM&3=?b; z)Mj4ubDY&8S|==vJ<7sY38E<=2O(czAp(3qQSz-AQHv;|sZma@X52(czKyZx_`5(KO(K^@x)JPD#?D_I9a*nk?xPib!|Op-VNE8t60=54xbTgaQltwGWi2=KRY zQSRr&afy?_Nm^2~MM;P$uo&Qzq6NmBjgsc#rm?|Ajjw8E^Z2cl)|H@y1`jRPW~Q}$ z+AREjnzXwDyk#`K9wU&0JZjx4G_|Sz&p@KZ{9jjS9KO*6T3UhESX1t>cl@L z+D>EM*LE9n=g-8`@TL%`=qANbsHeK=4a~FfCE$P{T zvaRQ^bzej`_s+#Y`+FbFLdV8f9_{{-C1DzgF(&84*aSDmHlw@~bJC+N6N!$XZ|guh z2;JZ7qx`@Y%l9nGvjYJP{oBBKER}`_S(qDxtQWQpm2oz(+V~-q_v9<%wE!T}Uj& zkdtX$`^UmeMxX&Rn^BNnCIG9bb;wD3DyHfm3*bY*fB?JkD6>Hj`pjM% z1i-3bz4@F3aRILmWMjA&vjLNtsK1YyaK+uaPi8LtX&Kg{XWY!wO&R8+y2o_tf6DL- sUQX Date: Thu, 1 Aug 2024 09:27:24 -0600 Subject: [PATCH 22/75] Reordered proof generation to ensure only the most recent information collected by the light client is used --- unittests/svnn_finality_violation_tests.cpp | 27 +++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 9997dc4597..cec813e000 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -256,6 +256,19 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //verify the two chains are the same so far BOOST_TEST(fake_chain_block_4_result.finality_digest == real_chain_block_4_result.finality_digest); + //at this point, we can prepare some "invalid proofs" that the contract will reject + mutable_variant_object invalid_rule_1_proof_1 = prepare_proof( fake_chain.active_finalizer_policy, + fake_chain_block_3_result, + fake_chain_block_4_result.qc_data.qc.value(), + real_chain_block_3_result, + real_chain_block_4_result.qc_data.qc.value()); //same finality digest, not a violation proof + + mutable_variant_object invalid_rule_1_proof_2 = prepare_proof( fake_chain.active_finalizer_policy, + fake_chain_block_3_result, + fake_chain_block_4_result.qc_data.qc.value(), + real_chain_block_2_result, + real_chain_block_3_result.qc_data.qc.value()); //different timestamps, not a violation proof + //create a fork by pushing a transaction on the fake chain fake_chain.node0.push_action("eosio.token"_n, "transfer"_n, "user1"_n, user1_transfer); @@ -282,6 +295,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //block #8 on the real chain contains a strong QC over a different block #7 than what the light client recorded from the fake chain //this is a rule #1 finality violation proof + //it can be demonstrated by verifying that a strong QC on a block of a given timestamp conflicts with another strong QC on a different block of the same timestamp mutable_variant_object valid_rule_1_proof = prepare_proof( fake_chain.active_finalizer_policy, fake_chain_block_7_result, @@ -289,19 +303,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) real_chain_block_7_result, real_chain_block_8_result.qc_data.qc.value()); - //we also prepare a few invalid proofs, which the contract must reject - mutable_variant_object invalid_rule_1_proof_1 = prepare_proof( fake_chain.active_finalizer_policy, - fake_chain_block_3_result, - fake_chain_block_4_result.qc_data.qc.value(), - real_chain_block_3_result, - real_chain_block_4_result.qc_data.qc.value()); //same finality digest, not a violation proof - - mutable_variant_object invalid_rule_1_proof_2 = prepare_proof( fake_chain.active_finalizer_policy, - fake_chain_block_3_result, - fake_chain_block_4_result.qc_data.qc.value(), - real_chain_block_4_result, - real_chain_block_5_result.qc_data.qc.value()); //different timestamps, not a violation proof - BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_2)); From 891207dafc5c1caf800ae4c5ed4fd6063c281485 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 09:35:55 -0600 Subject: [PATCH 23/75] WIP --- unittests/finality_proof.hpp | 2 +- unittests/svnn_finality_violation_tests.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 4b295c38e3..fc21fd553e 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -21,8 +21,8 @@ namespace finality_proof { digest_type base_digest; digest_type active_finalizer_policy_digest; digest_type last_pending_finalizer_policy_digest; - block_timestamp_type last_pending_finalizer_policy_start_timestamp; digest_type last_proposed_finalizer_policy_digest; + block_timestamp_type last_pending_finalizer_policy_start_timestamp; digest_type finality_digest; level_3_commitments_t level_3_commitments; level_2_commitments_t level_2_commitments; diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index cec813e000..f3cd1de769 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -179,6 +179,9 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //store all policies sunset data std::vector policy_sunsets; + //current active finalizer policy + finalizer_policy active_finalizer_policy; + //observe a stream of blocks as they are received, and store minimal data required to construct finality violation proofs in the future ibc_block_data_t scan_block(const ibc_block_data_t& block){ From 19d1b5d923ed3607d121d9cb910c4290803385ad Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 09:40:07 -0600 Subject: [PATCH 24/75] Replaced policy digests and generations returned by process_result function with full policies --- unittests/finality_proof.hpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index fc21fd553e..4007dd6fe3 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -14,14 +14,11 @@ namespace finality_proof { qc_data_t qc_data; action_trace onblock_trace; finality_data_t finality_data; - uint32_t active_finalizer_policy_generation = 0; - uint32_t last_pending_finalizer_policy_generation = 0; - uint32_t last_proposed_finalizer_policy_generation = 0; + finalizer_policy active_finalizer_policy; + finalizer_policy last_pending_finalizer_policy; + finalizer_policy last_proposed_finalizer_policy; digest_type action_mroot; //this is the real action_mroot, as returned from finality_data digest_type base_digest; - digest_type active_finalizer_policy_digest; - digest_type last_pending_finalizer_policy_digest; - digest_type last_proposed_finalizer_policy_digest; block_timestamp_type last_pending_finalizer_policy_start_timestamp; digest_type finality_digest; level_3_commitments_t level_3_commitments; @@ -320,15 +317,12 @@ namespace finality_proof { .qc_data = qc_data, .onblock_trace = onblock_trace, .finality_data = finality_data, - .active_finalizer_policy_generation = active_finalizer_policy.generation, - .last_pending_finalizer_policy_generation = last_pending_finalizer_policy.generation, - .last_proposed_finalizer_policy_generation = last_proposed_finalizer_policy.generation, + .active_finalizer_policy = active_finalizer_policy, + .last_pending_finalizer_policy = last_pending_finalizer_policy, + .last_proposed_finalizer_policy = last_proposed_finalizer_policy, .action_mroot = action_mroot, .base_digest = base_digest, - .active_finalizer_policy_digest = active_finalizer_policy_digest, - .last_pending_finalizer_policy_digest = last_pending_finalizer_policy_digest, .last_pending_finalizer_policy_start_timestamp = last_pending_finalizer_policy_start_timestamp, - .last_proposed_finalizer_policy_digest = last_proposed_finalizer_policy_digest, .finality_digest = finality_digest, .level_3_commitments = lvl3_commitments, .level_2_commitments = lvl2_commitments, From 2f5a557a229fcc70a210474105e2fb2015705985 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 09:47:51 -0600 Subject: [PATCH 25/75] Modified tests to use light client data cache only --- unittests/svnn_finality_violation_tests.cpp | 26 +++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index f3cd1de769..9fa0528b2f 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -198,6 +198,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) reversible_blocks.push_back(block); + active_finalizer_policy = block.active_finalizer_policy; + return block; } @@ -260,15 +262,15 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_4_result.finality_digest == real_chain_block_4_result.finality_digest); //at this point, we can prepare some "invalid proofs" that the contract will reject - mutable_variant_object invalid_rule_1_proof_1 = prepare_proof( fake_chain.active_finalizer_policy, - fake_chain_block_3_result, - fake_chain_block_4_result.qc_data.qc.value(), + mutable_variant_object invalid_rule_1_proof_1 = prepare_proof( light_client_data.active_finalizer_policy, + light_client_data.high_qc_block, + light_client_data.high_qc, real_chain_block_3_result, real_chain_block_4_result.qc_data.qc.value()); //same finality digest, not a violation proof - mutable_variant_object invalid_rule_1_proof_2 = prepare_proof( fake_chain.active_finalizer_policy, - fake_chain_block_3_result, - fake_chain_block_4_result.qc_data.qc.value(), + mutable_variant_object invalid_rule_1_proof_2 = prepare_proof( light_client_data.active_finalizer_policy, + light_client_data.high_qc_block, + light_client_data.high_qc, real_chain_block_2_result, real_chain_block_3_result.qc_data.qc.value()); //different timestamps, not a violation proof @@ -300,9 +302,9 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //this is a rule #1 finality violation proof //it can be demonstrated by verifying that a strong QC on a block of a given timestamp conflicts with another strong QC on a different block of the same timestamp - mutable_variant_object valid_rule_1_proof = prepare_proof( fake_chain.active_finalizer_policy, - fake_chain_block_7_result, - fake_chain_block_8_result.qc_data.qc.value(), + mutable_variant_object valid_rule_1_proof = prepare_proof( light_client_data.active_finalizer_policy, + light_client_data.high_qc_block, + light_client_data.high_qc, real_chain_block_7_result, real_chain_block_8_result.qc_data.qc.value()); @@ -337,9 +339,9 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Light client recorded the last QC on fake chain, which is over block #10. //Block #10 claims a QC over block #8, skipping #9. We provide fake block #10 and its QC. //We also provide the real block #9 and a QC over it, which is a proof of violation of rule #2. - mutable_variant_object valid_rule_2_proof = prepare_proof( fake_chain.active_finalizer_policy, - fake_chain_block_10_result, - fake_chain_block_11_result.qc_data.qc.value(), + mutable_variant_object valid_rule_2_proof = prepare_proof( light_client_data.active_finalizer_policy, + light_client_data.high_qc_block, + light_client_data.high_qc, real_chain_block_9_result, real_chain_block_10_result.qc_data.qc.value()); From 67440e4d479d460e94f29d1654126a7a9cd58381 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 09:48:57 -0600 Subject: [PATCH 26/75] Removed leftover comments --- unittests/svnn_finality_violation_tests.cpp | 8 ----- unittests/svnn_ibc_tests.cpp | 33 --------------------- 2 files changed, 41 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 9fa0528b2f..a4c71eeb93 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -347,14 +347,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof)); -/* policy_indices[0] = 1; // update key used for node0 in policy, which will result in a new policy we will call B - - // take note of policy digest prior to changes - digest_type previous_policy_digest = fake_chain.active_finalizer_policy_digest; - - // change the finalizer policy by rotating the key of node0 - cluster.node0.finkeys.set_finalizer_policy(policy_indices); -*/ } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 20eb912514..6f8387a64e 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -18,39 +18,6 @@ using namespace eosio::testing; using mvo = mutable_variant_object; -/*std::string bitset_to_input_string(const boost::dynamic_bitset& bitset) { - static const char* hexchar = "0123456789abcdef"; - - boost::dynamic_bitset bs(bitset); - bs.resize((bs.size() + 7) & ~0x7); - assert(bs.size() % 8 == 0); - - std::string result; - result.resize(bs.size() / 4); - for (size_t i = 0; i < bs.size(); i += 4) { - size_t x = 0; - for (size_t j = 0; j < 4; ++j) - x += bs[i+j] << j; - auto slot = i / 4; - result[slot % 2 ? slot - 1 : slot + 1] = hexchar[x]; // flip the two hex digits for each byte - } - return result; -} - -std::string binary_to_hex(const std::string& bin) { - boost::dynamic_bitset bitset(bin.size()); - for (size_t i = 0; i < bin.size(); ++i) { - if (bin[i] == '1') { - bitset.set(bin.size() - 1 - i); - } - } - return bitset_to_input_string(bitset); -} - -auto active_finalizers_string = [](const finality_proof::ibc_block_data_t& bd) { - return bitset_to_input_string(bd.qc_data.qc.value().active_policy_sig.strong_votes.value()); -}; -*/ BOOST_AUTO_TEST_SUITE(svnn_ibc) BOOST_AUTO_TEST_CASE(ibc_test) { try { From 35599296b09b868ded7bf6a817ab7dfed831b169 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 12:07:16 -0600 Subject: [PATCH 27/75] Minor code reorganization plus added comments for clarity --- unittests/svnn_finality_violation_tests.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index a4c71eeb93..e461c1d647 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -172,7 +172,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) ibc_block_data_t high_qc_block; qc_t high_qc; - //store the last final block, as wall as the first QC over it. The last_final_qc and high_qc together + //store the last final block, as wall as the first QC over it. The last_final_qc and high_qc together constitute the 2-chains required for finality progress ibc_block_data_t last_final_block; qc_t last_final_qc; @@ -268,12 +268,16 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) real_chain_block_3_result, real_chain_block_4_result.qc_data.qc.value()); //same finality digest, not a violation proof + BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); + mutable_variant_object invalid_rule_1_proof_2 = prepare_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, real_chain_block_2_result, real_chain_block_3_result.qc_data.qc.value()); //different timestamps, not a violation proof + BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_2)); + //create a fork by pushing a transaction on the fake chain fake_chain.node0.push_action("eosio.token"_n, "transfer"_n, "user1"_n, user1_transfer); @@ -301,7 +305,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //this is a rule #1 finality violation proof - //it can be demonstrated by verifying that a strong QC on a block of a given timestamp conflicts with another strong QC on a different block of the same timestamp + //it can be proven by verifying that a strong QC on a block of a given timestamp conflicts with another strong QC on a different block with the same timestamp mutable_variant_object valid_rule_1_proof = prepare_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, @@ -309,10 +313,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) real_chain_block_8_result.qc_data.qc.value()); BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); - BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); - BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_2)); - //we temporarilly disable a finalizer on the fake chain, which serves to set up a proof of violation of rule #2 + //we temporarily disable a finalizer on the fake chain, which serves to set up a proof of violation of rule #2 fake_chain.vote_propagation = {1,0,0}; //produce a block on a fake chain without propagating votes to all nodes @@ -337,7 +339,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(real_chain_block_11_result.qc_data.qc.has_value()); //Light client recorded the last QC on fake chain, which is over block #10. - //Block #10 claims a QC over block #8, skipping #9. We provide fake block #10 and its QC. + //Block #10 claims a QC over block #8 (skipping #9). We provide fake block #10 and its QC. //We also provide the real block #9 and a QC over it, which is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof = prepare_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, From ec68dd5a376d3950d622a271bf09800363d34d42 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 12:12:58 -0600 Subject: [PATCH 28/75] Fixed incorrect assignement in savanna contract, recompiled contract --- .../finality_violation/finality_violation.cpp | 2 +- .../finality_violation.wasm | Bin 41578 -> 41583 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 5ce3a463bd..09c9dbb57c 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -30,7 +30,7 @@ ACTION finality_violation::rule2(const finalizer_policy_input finalizer_policy, block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; - block_timestamp last_claim_timestamp_1 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + block_timestamp last_claim_timestamp_1 = proof_1.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; block_timestamp last_claim_timestamp_2 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; bool lessThanRule = last_claim_timestamp_2 < timestamp_1 && timestamp_1 < timestamp_2; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 4433675268107d5065e4d7e60e36c245549b414a..2953607e39986cd874ccf82b635f1c961224a0f9 100755 GIT binary patch delta 80 zcmaELgz5berVU4!882-<$}H~6*t7YrM-!7Vv*QxREDa_GB{l^n7?Z;j$X);zR$%ii T Date: Thu, 1 Aug 2024 12:15:09 -0600 Subject: [PATCH 29/75] Corrected order and names of sub rules --- .../savanna/finality_violation/finality_violation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 09c9dbb57c..146c96b910 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -33,8 +33,8 @@ ACTION finality_violation::rule2(const finalizer_policy_input finalizer_policy, block_timestamp last_claim_timestamp_1 = proof_1.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; block_timestamp last_claim_timestamp_2 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - bool lessThanRule = last_claim_timestamp_2 < timestamp_1 && timestamp_1 < timestamp_2; - bool greaterThanRule = last_claim_timestamp_1 < timestamp_2 && timestamp_2 < timestamp_1; + bool lessThanRule = last_claim_timestamp_1 < timestamp_2 && timestamp_2 < timestamp_1; + bool greaterThanRule = last_claim_timestamp_2 < timestamp_1 && timestamp_1 < timestamp_2; check(lessThanRule || greaterThanRule, "proofs must demonstrate a conflicting time range"); From a99e6733a1ac9f7e833cea165250d521c0594374 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 1 Aug 2024 13:16:17 -0600 Subject: [PATCH 30/75] Desambiguated rule2 (now rule2a and rule2b) to differentiate between scenarios where the fake block's timestamp is lower vs higher than block with conflicting range --- unittests/svnn_finality_violation_tests.cpp | 156 ++++++++---- .../finality_violation/finality_violation.abi | 229 +++++++++++++++++- .../finality_violation/finality_violation.cpp | 34 ++- .../finality_violation/finality_violation.hpp | 6 +- .../finality_violation.wasm | Bin 41583 -> 64694 bytes 5 files changed, 374 insertions(+), 51 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index e461c1d647..ca1972d839 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -21,51 +21,125 @@ using namespace finality_proof; using mvo = mutable_variant_object; -mvo prepare_proof( const finalizer_policy active_finalizer_policy, +mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, const ibc_block_data_t fake_qc_block, const qc_t fake_qc, const ibc_block_data_t real_qc_block, const qc_t real_qc){ return mvo() - ("finalizer_policy", active_finalizer_policy) - ("proof_1", mvo() - ("qc_block", mvo() - ("major_version", 1) - ("minor_version", 0) - ("active_finalizer_policy_generation", 1) - ("pending_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) - ("last_pending_finalizer_policy", active_finalizer_policy) - ("level_3_commitments", fake_qc_block.level_3_commitments) - ("witness_hash", fake_qc_block.base_digest) - ("finality_mroot", fake_qc_block.finality_root) - ) - ("active_policy_qc", mvo() - ("signature", fake_qc.active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) - ) - ) - ("proof_2", mvo() - ("qc_block", mvo() - ("major_version", 1) - ("minor_version", 0) - ("active_finalizer_policy_generation", 1) - ("pending_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) - ("last_pending_finalizer_policy", active_finalizer_policy) - ("level_3_commitments", real_qc_block.level_3_commitments) - ("witness_hash", real_qc_block.base_digest) - ("finality_mroot", real_qc_block.finality_root) - ) - ("active_policy_qc", mvo() - ("signature", real_qc.active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) - ) - ); + ("finalizer_policy", active_finalizer_policy) + ("proof_1", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", fake_qc_block.level_3_commitments) + ("witness_hash", fake_qc_block.base_digest) + ("finality_mroot", fake_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", fake_qc.active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) + ) + ) + ("proof_2", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", real_qc_block.level_3_commitments) + ("witness_hash", real_qc_block.base_digest) + ("finality_mroot", real_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", real_qc.active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) + ) + ); } +mvo prepare_rule_2_lt_proof( const finalizer_policy active_finalizer_policy, + const ibc_block_data_t fake_qc_block, + const qc_t fake_qc, + const ibc_block_data_t real_qc_block, + const qc_t real_qc){ + return prepare_rule_1_proof(active_finalizer_policy, fake_qc_block, fake_qc, real_qc_block, real_qc); +} + +mvo prepare_rule_2_gt_proof( const finalizer_policy active_finalizer_policy, + const ibc_block_data_t fake_qc_block, + const qc_t fake_qc, + const ibc_block_data_t real_qc_block, + const qc_t real_qc, + const ibc_block_data_t real_target_block, + const uint32_t target_index, + const std::vector merkle_branches){ + + return mvo() + ("finalizer_policy", active_finalizer_policy) + ("proof_1", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", fake_qc_block.level_3_commitments) + ("witness_hash", fake_qc_block.base_digest) + ("finality_mroot", fake_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", fake_qc.active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) + ) + ) + ("proof_2", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", real_qc_block.level_3_commitments) + ("witness_hash", real_qc_block.base_digest) + ("finality_mroot", real_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", real_qc.active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) + ) + ) + ("target_block_proof_of_inclusion", mvo() + ("target_block_index", target_index) + ("final_block_index", target_index) + ("target", fc::variants{"extended_block_data", mvo() //target block #2 + ("major_version", 1) + ("minor_version", 0) + ("finality_digest", real_target_block.finality_digest) + ("timestamp", real_target_block.block->timestamp) + ("parent_timestamp", real_target_block.parent_timestamp) + ("dynamic_data", mvo() + ("block_num", real_target_block.block->block_num()) + ("action_proofs", fc::variants()) + ("action_mroot", real_target_block.action_mroot) + )} + ) + ("merkle_branches", merkle_branches) + ); + +} + + bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_name rule, const mvo proof){ bool last_action_failed = false; @@ -262,7 +336,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_4_result.finality_digest == real_chain_block_4_result.finality_digest); //at this point, we can prepare some "invalid proofs" that the contract will reject - mutable_variant_object invalid_rule_1_proof_1 = prepare_proof( light_client_data.active_finalizer_policy, + mutable_variant_object invalid_rule_1_proof_1 = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, real_chain_block_3_result, @@ -270,7 +344,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); - mutable_variant_object invalid_rule_1_proof_2 = prepare_proof( light_client_data.active_finalizer_policy, + mutable_variant_object invalid_rule_1_proof_2 = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, real_chain_block_2_result, @@ -306,7 +380,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //this is a rule #1 finality violation proof //it can be proven by verifying that a strong QC on a block of a given timestamp conflicts with another strong QC on a different block with the same timestamp - mutable_variant_object valid_rule_1_proof = prepare_proof( light_client_data.active_finalizer_policy, + mutable_variant_object valid_rule_1_proof = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, real_chain_block_7_result, @@ -341,13 +415,13 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Light client recorded the last QC on fake chain, which is over block #10. //Block #10 claims a QC over block #8 (skipping #9). We provide fake block #10 and its QC. //We also provide the real block #9 and a QC over it, which is a proof of violation of rule #2. - mutable_variant_object valid_rule_2_proof = prepare_proof( light_client_data.active_finalizer_policy, + mutable_variant_object valid_rule_2_proof = prepare_rule_2_lt_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, real_chain_block_9_result, real_chain_block_10_result.qc_data.qc.value()); - BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof)); + BOOST_CHECK(shouldPass(real_chain, "rule2a"_n, valid_rule_2_proof)); } FC_LOG_AND_RETHROW() } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index e8e6b927c3..5966d4a5c2 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -1,8 +1,89 @@ { "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", "version": "eosio::abi/1.2", - "types": [], + "types": [ + { + "new_type_name": "block_data_type", + "type": "variant_simple_block_data_extended_block_data" + } + ], "structs": [ + { + "name": "action", + "base": "action_base", + "fields": [ + { + "name": "data", + "type": "bytes" + }, + { + "name": "return_value", + "type": "bytes" + } + ] + }, + { + "name": "action_base", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "name", + "type": "name" + }, + { + "name": "authorization", + "type": "permission_level[]" + } + ] + }, + { + "name": "action_data", + "base": "", + "fields": [ + { + "name": "action", + "type": "action" + }, + { + "name": "receiver", + "type": "name" + }, + { + "name": "recv_sequence", + "type": "uint64" + }, + { + "name": "witness_hash", + "type": "checksum256" + } + ] + }, + { + "name": "action_proof_of_inclusion", + "base": "", + "fields": [ + { + "name": "target_block_index", + "type": "uint64" + }, + { + "name": "final_block_index", + "type": "uint64" + }, + { + "name": "target", + "type": "action_data" + }, + { + "name": "merkle_branches", + "type": "checksum256[]" + } + ] + }, { "name": "block_finality_data", "base": "", @@ -45,6 +126,68 @@ } ] }, + { + "name": "block_proof_of_inclusion", + "base": "", + "fields": [ + { + "name": "target_block_index", + "type": "uint64" + }, + { + "name": "final_block_index", + "type": "uint64" + }, + { + "name": "target", + "type": "block_data_type" + }, + { + "name": "merkle_branches", + "type": "checksum256[]" + } + ] + }, + { + "name": "dynamic_data_v0", + "base": "", + "fields": [ + { + "name": "block_num", + "type": "uint32" + }, + { + "name": "action_proofs", + "type": "action_proof_of_inclusion[]" + }, + { + "name": "action_mroot", + "type": "checksum256?" + } + ] + }, + { + "name": "extended_block_data", + "base": "", + "fields": [ + { + "name": "finality_data", + "type": "block_finality_data" + }, + { + "name": "timestamp", + "type": "block_timestamp_type" + }, + { + "name": "parent_timestamp", + "type": "block_timestamp_type" + }, + { + "name": "dynamic_data", + "type": "dynamic_data_v0" + } + ] + }, { "name": "finality_proof", "base": "", @@ -125,6 +268,20 @@ } ] }, + { + "name": "permission_level", + "base": "", + "fields": [ + { + "name": "actor", + "type": "name" + }, + { + "name": "permission", + "type": "name" + } + ] + }, { "name": "quorum_certificate_input", "base": "", @@ -162,7 +319,7 @@ ] }, { - "name": "rule2", + "name": "rule2a", "base": "", "fields": [ { @@ -179,10 +336,62 @@ } ] }, + { + "name": "rule2b", + "base": "", + "fields": [ + { + "name": "finalizer_policy", + "type": "finalizer_policy_input" + }, + { + "name": "proof_1", + "type": "finality_proof" + }, + { + "name": "proof_2", + "type": "finality_proof" + }, + { + "name": "proof_of_inclusion", + "type": "block_proof_of_inclusion" + } + ] + }, { "name": "rule3", "base": "", "fields": [] + }, + { + "name": "simple_block_data", + "base": "", + "fields": [ + { + "name": "major_version", + "type": "uint32" + }, + { + "name": "minor_version", + "type": "uint32" + }, + { + "name": "timestamp", + "type": "block_timestamp_type" + }, + { + "name": "parent_timestamp", + "type": "block_timestamp_type" + }, + { + "name": "finality_digest", + "type": "checksum256" + }, + { + "name": "dynamic_data", + "type": "dynamic_data_v0" + } + ] } ], "actions": [ @@ -192,8 +401,13 @@ "ricardian_contract": "" }, { - "name": "rule2", - "type": "rule2", + "name": "rule2a", + "type": "rule2a", + "ricardian_contract": "" + }, + { + "name": "rule2b", + "type": "rule2b", "ricardian_contract": "" }, { @@ -204,6 +418,11 @@ ], "tables": [], "ricardian_clauses": [], - "variants": [], + "variants": [ + { + "name": "variant_simple_block_data_extended_block_data", + "types": ["simple_block_data","extended_block_data"] + } + ], "action_results": [] } \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 146c96b910..87d7b21613 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -20,7 +20,7 @@ ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, } -ACTION finality_violation::rule2(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ +void check_rule_2_qcs( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ check(proof_1.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); check(proof_2.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); @@ -28,18 +28,44 @@ ACTION finality_violation::rule2(const finalizer_policy_input finalizer_policy, checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); + _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); + _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); +} + +//Proof_1 is presumably from a fake, hidden chain, while proof_2 is presumably from the real, discoverable chain +//The block referenced by the proof of inclusion must be proven as a final ancestor of proof_2, while also be proven to conflict with proof_1 +ACTION finality_violation::rule2a( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ + block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; block_timestamp last_claim_timestamp_1 = proof_1.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; block_timestamp last_claim_timestamp_2 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; bool lessThanRule = last_claim_timestamp_1 < timestamp_2 && timestamp_2 < timestamp_1; + + check(lessThanRule, "proofs must demonstrate a conflicting time range"); + + check_rule_2_qcs(finalizer_policy, proof_1, proof_2); + +} + +ACTION finality_violation::rule2b( const finalizer_policy_input finalizer_policy, + const finality_proof proof_1, + const finality_proof proof_2, + const block_proof_of_inclusion proof_of_inclusion){ + + block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; + block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; + block_timestamp last_claim_timestamp_1 = proof_1.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + block_timestamp last_claim_timestamp_2 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + bool greaterThanRule = last_claim_timestamp_2 < timestamp_1 && timestamp_1 < timestamp_2; + + check(greaterThanRule, "proofs must demonstrate a conflicting time range"); - check(lessThanRule || greaterThanRule, "proofs must demonstrate a conflicting time range"); + check_rule_2_qcs(finalizer_policy, proof_1, proof_2); - _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); - _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); + check(proof_of_inclusion.root() == proof_2.qc_block.finality_mroot, "invalid proof of inclusion"); } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 8a4a5d8309..54a90c1581 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -14,7 +14,11 @@ CONTRACT finality_violation : public contract { using contract::contract; ACTION rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); - ACTION rule2(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); + ACTION rule2a(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); + ACTION rule2b(const finalizer_policy_input finalizer_policy, + const finality_proof proof_1, + const finality_proof proof_2, + const block_proof_of_inclusion proof_of_inclusion); ACTION rule3(); }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 2953607e39986cd874ccf82b635f1c961224a0f9..c69ac9df5f3ab063e4f63340dd28807e10c2a18b 100755 GIT binary patch delta 23182 zcmcJX3$z_ondfU))p^{Udy|R=2_ZCf4nb}(U{DNcMM*_K5Fe<#M3h8{Cif;GkDG`+ znz~@nC?Z#jM(jont)tQL5$B?vp2eEEW3xua_7N5B=(yUrhP9ery68B|SwpJ}=lB11 zRh@Gpq3!M&G-to}{`U8M`+MzO_a7e$zxq#6H(0-OiwlCleaS6Z7X<5~JkNu5p?;Kg z`G>lDMx%Vso?u;r@}4~(`q+7S7;V_PySZhf=GoHg)KMl-r!4Wdds$V_(FTz zj!j#qcHX!-uwi5CckbMjp07*FUOS3cx{*t5 zU~_PmksEJZzjNc}sZGxhV$B-USWc&Y^*kdxHf`Im<)-sitqfdopL>l9XCHUtE_cy~ z-HZzsBuQ&wkSyfCC~CD9wp!14W*D`?`B4-uT;$wICj|@p{|j5s3Qvw4|3%?RtyU1a zlUhkLY_*bB*jgNh{45R^pR#aai##R$M?xkH8FZ3zEe#LhbpBiM<7nBXla000`tOqmzWbl^G<;3GCJj=TZ1dG~$5n4H zdmkL`y|XwcUN(DW{L4Rbj}~jsSdl--zV^g<8f3w$u$=~LgA2kS4ZBgeCJonySB(X0 z=YlNEE~1pb^Btd=ocjDb-qnpd;Tk$bRP~}O#~9p=BWl*VwR!5+x_4jMYn+<8cc$U> zV*wq5@i6W+m9%@UtTFYnu^??`ZX(O`SB%Bk@A$9Pjp^9To+KY#VeRQewr)GaT3K_d z*GgMy^YT->aVHJZIC+gGV^y2F+f=0{TouNdBX|1tsctKc$wj-f-!i4!KGAI&&<)Ma z*tE5y+sH>rL?gs3_9AzzOG~2?w!hoj zJ!b@2I@t@Cv01;}HoHvSt63`)W*0M0a6y!$E@Q_|$wb!Jo#k$#=dy4rd3HBS!_-+% z4Om1eS`{XK_$Vdqw?~Jz&&R!pU9w*dOQV5(0zIRW34YrABp+}dS0rgT9wurJmWQ#B z2O}*H7hT|IvE|bE0v30d0J%%f>M%(LdP1Ch<_IuP#_8QBWiYX%YMlM^d zMHf%hWdkDVmjw?%mFMp86?&i zbeqn62eincegv%qpzOMwLMt}dd%k~A)z0PkKKxfG6 zY#brgX*#U(VSt(ol2?g$4m&u5f#cyL9F)sMyH(-EJ`oqp1zLh_B(GlLi+S3r^L_^8 zjg3#6lgUdwOCEN;rs!(ZJ%V`Axe#iE?#qnxm0MGv0b9xTXXq8){c`>7wZ)_D^!yQo zR&T`Ed4SzV5b@9jneP_= zbJ-equvm8Hp2k0XH%R`t=sxGP;-hDu;{H$ZcV~VwyyttxPtQz?Tb{FX_E~4G4Z}}< zuXygc%ZfeAS8ME{%b#lOdBvk=?eU(Y`O{AP6e>)A?_9RvG8Hp+nq4aw{5O#h?Kbn(-(78lzt z_@?{%?2A|b*e$;ETU2WMQFhQxoD&4r_0De<&wb&l@BZ&wR6`N55MwG_r}@#FWT?in~JsB4%FXQ zvKRAv-rBQ@C2OB$xtG}Q>BU`Zzu}&oz5b=kBlq@V@1;+;TZ(tR?9=WY#j?vbv9k|e zc18Zip!219t|37<7ftpeNq(gI5WM9s!r!lDX9C|PZ$Jn~;_1s!FEESt43}ny#nI7t zxTf1cK9ae3Rd_EM>886$kz1?688X>SG=Vz2VM&?Qm>y#okv2RB`zrB}4W}Mf0@|2! z406>Fb5u>*QZOMGXDh4%mN#A9Z6Iu-HH>Ox+ZcsC<4o2yvRl;AGv{?kU+Z$6Ca!y| zt9rE94eFd3J83)~j!WI5qgRD13?{Zv61Bq0A$BOV9Mnz^Mt!{G2crrz51=N&o)B`e zPf8?KXaS$X$HBMqHAASC-u|%}zXZKpGLJcQ5UkUBwn=z(V z+XU>Cc^37gXW>v3Y*PHkHK*keWjs6|*3t0u;S6#fDILtGY&z6Y7k>)m27WLIQ$Lc?p!Ri3V9j`5*8_` zkhgdkdBuS>XL*z@Dp9uRIFyAY%8X$vd?iEpTJ&$g7krtQXZA$+N@{!oo5$B&yDGV`G8a%7A{Sl?Cb<`x9Neb zBSfCiaw{gYtL)2KLiY zzM?iL786v2kFt{R&0)efvO^W&8v;v)ma7a=tgwZq5dgddYJa2n&J}0nqS&*09QA{0 zB7MM8yhjsdhv+Ucw}!}!HWDF4VLI04zX(j(Q(457$Dx@Jb%w=q0a;`~$nVjMN-|?U zBJGUQEhya*rA2Ph2PHH>X^Iat#CAsM;%6@&cdg>sahnjaG<28pG+g z7bhFF@q6(q%qY;@I6OUbm{izDp;;16$+YvN+E40HmnWN0W7R%;trEsYS%s?%zZvD5 z+PpR_ES7lCe89p!Li*UO{|r49!5E}x9+w0qtqCe@Boo6lWuVbC9;9jPY06k=8do$m zl2%~Xw5+yP1CN~&@#V5}?uFI)r1!uP%gyH)45*^Mj9IL0zYr}ZL15NA^e z0khYDfq`*$ARlc0e13fUePw&@$L}BP!0=;xCM+t4aP}R|kc+ds>4(ZeFq*>V2q9)o zc7UCW6)z(~A``hp2Gk&gWE@~Sa@nu!cuFoF3(@7}`i)(^C?noDhn5yiW27rs6{$%? zG`TK~Da^#S#{SA8HpjvqT7i>(nUH|gijo+riBfb|d`_V{$`TqT!1dEkUMt)Rd+{Z? z9ZFlH1bI3xLr^k69*n&tItr`=+?dTJ({2eS?_*&pg9uLbvq$Qz_I#M&jM8JI*%NhB zHeE?GVlw;cJU!)Qu`@h1u>;6E0`TQboRP5fNr3%uDDkCTJQA)750Mk19w^g;q}f54 z#B$br)&}*AjUS;iYz$C`^U%bjCOQl@JwHQXx)_Or>=-?oU>8O`80z#%EU0v-ESa2Q zF)!?W50h!CtF#;#kNPfPgn$zMTbG<%vU*hdfU$XOX{)&Cs>^eXf8#BT>v8dxnY}Au z4hc`r{Y*H4_%QuB5D7H>sv}$pjuKwtI~*ZFZt?_D5?lj$;N0?i$=@x!Lr;;qRZLlhzje(~tKqkZQj?nwCG4Y# z_^mskZK&b5t^pa-4lwpQkEu|{m_iSGQb?GVf+su4Q3fkqx9sY*D?u&+)dDy8s+t66 zRTmW#tMYK46;~<#ZWCz=qVL2GOqAo*B7G`Ru1+9yFQyxm0*z*47{a9#WN;X{=&w9p+)7JIKW)V`%2zS;^G zYU`>ge zN67X$c^)NrxnwoD`h5-_rF67zlMPQr?zvaYD;!YM=UrV&9mlu(;W3@=Pq~_D$NEe= z-@|I}YSlT*ZW`O{6J$^Cx>_o@p1P^V6}KY1Ij) z{gnI-Lt%28;E?jsPqJ6PLN26PK;_<=0lpEs4CX zpe)yGU<7HpQq_UDvM^@rsoK_82lE(?hi&2Q9Nq456jN z_}`D$)+fWR2yK%Yzpt!e^Ak4Sed0z_)}wH=>FR*A*z~F&yUnvdd(&Szx21UU%F~{? z6>glHCk{Q@n#k@$q2D}v?rZ*&n*ZUogUt#!SaWg8>wdmuJ!e|6XKEF~Nv=l-;?T#* zn~OiX>Vxyh+JqFpdez70eVk_n=rEYDSik4w;_wjKMyy{2)U$DCFX|ej%XS%jFaV#kozUlJ(n~Jq!Iy`Kt z>7m1la2zlfx(x2hKtDvS(t~P6YaURFOZMoPk@VpNVQ|#vHCDv~h{kfNwnRl;MDj># z?r%@zBuB?4lFdF9&u76418H8W7|^)~YGaPi1+k)V$D5~R8B zeXJ_`BEcNr?2Csm^9NSKI?aL?xoRaW=E2VU>Y{4*F906K9-Qyf{bl+Dm{NWI3o`0B zS9bg@sme!H9sy}`3K0Fm=YF3TVvg!@#+*l}%5y3|*e|0vaGLt%Ih7yimlsn!NU_9! zHo9X~`1yWwn_@{l%^p)MsYkI-JpkL^nMk$_fXEz%|7rPVqHrQv0r+LT2?L#T210G3 zLnUTa9w4`heR8YRC$~xiC)%r*aG!!vSL%9WMq zlqj7M2@#Y(8KBS+pPvzuexW>(p~p(9v4+rE5|k|<9%;(JAiqbz;||h%+ICt2s*(&p zA`w{mz-BAkxm+F{plVv@>%nAVT+YdOcnDQQEObjZlsSbZP&%yr>qy0!h<40q4qc^K zibAs}{=IF)k!F!z9uFT(iUY5EZr%pj8be>jS!oO9YHI&;MCa{%C+o4o@s8{|5<*iZ zIHkZsmRp!k$Tds=H*tw_3;QiZA$!@bwpnw!gY%ioHs`=Bj~>4If;=)jfgxS~X;N|_ zue!A9g^rGbb-DvS@w+j&YdQ;A$H0Wz@1}{Xpbn~9CU7|9w_(3mJ}r?e@`N`nmH(D2 z2KNNT{v^GTNt{0O+(L}}wv1qdf>9#uuLKC7KtAj^Md{w+p&)+eo#}E3x#zy+C@ll~ zp1T^6FZMW+T%>&;w`;wigIToYTDj{EL|bgz5=MQ2+%CR-)f@8}yd=TI&P2v~*_jR@ zK_Oc@9~|{tAVmDs0C7WC!V_0i@bHN4-eyCC+no#mz)ayKqgBw$mwwG*Qq0|!I~Uft_>fW zVTM(Xi@288Z9H3RMLHaygATj~Vvb+Su2nsB3(KG!O+Mk4%pSYx1J3O#-u2dZ+V5$_ z$!}X*=HC9cm33}W@uRmbrF83C7Z>w4US8Jk+PKE*PAR^#vE_Ep{_V!8n2P_hWl8bY zE!SD@N`8~Ar_gxz)@$N$<~y^u?O3H1|7zzHzvoV!O=R!&Q!lsQ(~G~JI^A+-6wO^< zbXU!OW!Kl8`;+2bZ~w9TceB^s@_=)X&W`=bZzA{3Vr0)sy1sDFCHzkBImf-L_~M>* z$?v}DeKzz8e&2ZOity2} zIOY0R6q~O9-#l(z9!(Quoer^{QLNb8{OZm{ToH>24o1UwrrtHi=OYHDXBnJ%;apPF zqP5VLhnHR&0_5eYU}Ui*0PhWS_*`#7_&9GKl67HP!z;Or4oX;VzcfO7toe*P8eZ9n5Z z-df=X`O5_&c=e8G2A~zJxgh7KI+{VY)SpoWmqeYcR9J$;*$Pzja&fQnV3)TyNZU{9hZ zRQ5wFTr*e=zg0JZFpV^zUln7y3|RN;Ic=W2n->X`4%rQ_cA>jF+5oPc0Lg)xbGAce z_mkCp;>CwaHUFH-*lmF4?_TE|QiQO^c=!;Z;{!B{-w3N%x?kM|K(3u7do5O~GX3YA zzkq#+piq5Tr%Kawnnf*jaAI}YkX^`jOgYz0E2^fIzt@zOO*zcmALjQ&SjVUqt_j8A zgi9s(V5@2PpyY8vPIkzu3$OBw!2_DjWGzRnf8n*)`3a6cWqBQOitU)yH(o-9-ccNP z<7IEHp`=Bo*(ea>9GIF3#j-ycjT>l{mXB)gFR#7qj<@1Oc(lqMz(c9>2m96VLRqUs z!Cs{zxl(B7I4O`9lq|)5Dun=BLx&(aCchw!#Jx_go2S;=*1lfLzl5hNu}8ZB9M`jz zY=ysw7@16)++$&UG|Is!YgG&lHio;!1F?<4m|62k>{*Y07Mt|2h(?isK#*1_KJosBk5?iZ96Z$8eSm>S~O3E`<>olv_+G7oY|wZ2k;q|?kXdygGWfVeI-AgE?vA7C#tOnBfXYaYeFwF)VJvZLTwBBZR4~Fqdjx$B+^Bn z68kkVnADZEHHnb__+TSxIdJ7fZ+==%yqchS>H*N~b579gX9ks~`|4;PSwyzFrX`5% zW2|0QqzCxpOm3P3!j5+>$ai*)YTXy@y#}7MO%cN0bFB^iAmLi}>h2TT!LggqS~S zsny+@fd)9Qu7^wN0ap#w!+8Do6E_VsY#SIbW%YozU$^S^Y0n%{b5zY8A@O2(bT!}4 zglh&2=BRHle_w8htY}?lVGm^HT$)*_X9&Fr`3Ji78mT|YzhpeatF=!&4B=YaetucM zYI~x)tOotW!^7g}Z6_@;8_jMFkYL>K55@2Gh1~R>Gvu_jYi9x9B6_b|Qamty!=Q`c zK2v;o&uL-2t2n$z9}(bo{( zlJNV$`-6yw47^Gj?od8tD97s|L*0%OtA`BQDVN6subZOdUN^yWI&mg7LG1ZrL5{2* zs>^!pv=Fd7yz6WtPELJ!&}E^p_xb^zj2p^nrBifYO1TzbcDX5B)#K%>!P|A&1m{Zm zeVutMNoa-_NF828+Z#!~1dNsur}Jlm^DZ8k)aj3zr)CTLxGmz##))3r$jtT)J8kS6 z^g}+eGdo=mCOK?D*X{;=j(+VdwH660-$E6k>HgP_xi#SmF;l;`YeFdQw!<~dV!R1H z`Etz^2~}J(Po|t=?>1Vm(Cm)3Yz_}gmS-v5qyyPI|1LYV=O+1>F%74-6L#W5%Cw0V z4AS;?0*WSmCsCB(X@VHi>Dw_Efnyk~CO7%zcbou}ZlPm1)pJQC!HLETLa@W2#uNdv z%)JV{wC!yQ%|c5F)NUa6>oZ6o#0EkcS;B2iD~!QOt8jfS1iF|68tC~xDqc}Qc$3Ju zI$I!69HJl~KU@&p2`VD^XIap>r{PBQC`e=#K6${#6QrqBRshuY7_MGXROnO`%s;Hr z>u-I|NChLUWE)Y3UW*uB%J+VmVH-m_9RbtxsG4{AslB#gyfw95`_+RE;TZ!Ri+i4$ zj2ilU*5<+-)s!QXQ)oc+;^b})(#RF3@)fK`T5K>D;q@7#p3VqW*fs-1ve$oX6#ypb zRyyI@fPp-sMS+B|f+TO+icHB$UEg5 zJDN7SZR{Y!*F29ev@VOMquYB-tUZLxWI48)gH`Jgr=9J<+}aHg;z1FqT9F=n5;~QR z7=UdaEWy5~6BzOMd&d72%AV{mpG33Ellgar+tJ zhurL6-u`LlCpxIx;f4E_@gdx*earn-@$GJN@%Q^)7~bnJ7b9kRCzWLslYSfqS*%;=( zj(m)0MzMiq2%eDhtq?R)po$CW_*Y>eJ)NwgLWIHl*EVy+k;3W@r@r=)VTp)Q0z)Wa zY9%XKf%}*jPeDoq&o&Z$b+anu3vOjmK14^;zB8O#5uSqgeTYlk@!!kYRM* zkG75rk>A2Z@^|U4xD^57@}5Ewaw=;bht8v5*B^RiB-ULiHZyJNzm8S`RazO;rbp#t z*lRY^qIxc){mIZ^!Sn4^xJ{za&UkpCpOI}HhV@I04d{w$ahwNKu6)cPove7;kkSV6 zR7k7)uZk_$Mop#ny#iyA7l$YvidI=kTx*CViw%)yNOax4Zd4A(ytwOuW+ni`$RuGr#nwS z8D3{!D)Lrgt(O-j0_P-_=y#=0#9Wjx&y--035uiiQHr;1=`^X`bCU`kN0j16C^Gj8 zpWmzeV?IBv{G*n)+esw0Ja#TJ3D5mSUvOf5z+YOKK!q+l8KOR^Ks-ts3`)+({9Oq; z++UMreMxFN@b4pQfm-3&q-0*kcErr*1gquQ!w)YER+C8Oupmh#X6>;*o?^v`rhd#m zQB(;8Xfz%VmaM>E!35X?oMByQSmO#VNM#<-Zw{duM*IMcUg5)MBt2Zh7?keKloSX&a6yoLlk<)?HS>?zgSWKYs@WgaPqgZ$K(z6fS z1#ToM?qyMC7joY8w&6T?xBFmq-}nc1AFZ}miVJorapAY8H&`cz&Sb^P;@F)R3>!+L z?`~i}#}XaWt}n%SZ44T1N6EkUZxY+p;w+~VdJ!C~Em$XWDTlQU`wlan3*cQ2XdA@3 zELX;c=L8qQ{m6T1oN+I7PzEBMF4l%VGK3To16-Z@HQiI`H&`RIKslDmz>=omXo0$2 zhl10Ry5R7AsceL6n3v}RQf)1%wq}|)&~1CM^rYJO>q1g(=y|EOCi>X=534r58I@|& zb)1d$strBIPP$Ip)Vi4LQf)!4+EBY*wUz7h0TdWuYpR}HJum)2WY@JRFFC)U5>arg8o>-jxnjUHyJ5}0Qazsxb>&GxYh^r&v*F|il zCM=O!sg|EMc3%gQ`awmHdN?iNCFgQgXi4WD+B z53$h@+dwuIq|}QReKx`e!&USy#{?Pp#IJe$*F4QC2xb?G&Evl2(e2sb$2`sBKIUoG zAM-SaVqncW2FBOt1i8|P!vS$iQScdr~!A_izIY{qUoT|3j>H# z$uvm_FViH+kZJG)KUfla^xasIed)4Le}jO9t(f_^#(E`p7i( z-ybm?{Q2i%`29i!U(p=8?}IJL66rnB%p@il&wj~AOWf!Y6semKND5{E)FMfa%uT_y z4MHk}eHvquSJvPD02$4&ss4T#6O_t_L(5m9VqV}klKWns!WP>)AzhvZn^-pg63W$R zhovf9x2s%LF+kp@rf#1)JUujf(tv<5k-ZgVv~}C6unO-Ua2$qEFJRCd#0Yu;@>Jp- zybx)Yx{=!8xJ*0y(+kuS(KfxQzj{Vn{k^VT&&N8<+>8hVb%*jammL(*9&Rrq*=4)F z)2-Vp-~xZLfRQiJWcMka&K}Hyf6{qdrjndRbj^~yem^D`4y!L#kV?*y#s4jYC<9S^ ztFveyRG3(xFHEoFsn<_cwC4|+rTl;iw$tN|Qm9~FN~py0e7rQ_Z(S%Waia6>O(f5O zwN&}6Ga)6|?9ExV*KaiNn!-I{-q>U3%j5{&jSiVaofpyZXpTZRX&oB}iWp=aSzt@zG5V{H4hvK#;3bZg*A+-!s)hkGlSLq@r+)a;h{v5@RVt|N*!$2eV6kxR;>ANpz3Pg3 z(GBRyGDMTl1GL`BeMsrJ(&EU6&mZ;)!XJjk9k-svRrvQ)eiMkr;>9F(FF_Ksy7*O z9iDz19S4REj09x_Isn59;-K^Wkl=4QAN7Jvk+XPLWzIzKk__UUIm;|l$Pp^d1dG4} z)B>{=U@+#eYay`3l?M!~r<{7p8Q5Pwh3!n_0fer$;$8Y1s23rifkLt;;tw$}if#xO>T{dX)*DvWrFW@+)|^t4>@%>KUZb)EKo(eSn#wr$;amfHqf zPqpo8*O~`+Qi0F@t_gycyH+?9<;SiYwrzpeslbbX?9Pw8xurdJzPD(0=|^AG7@gX% zW9$0O>4qIUZr(9_)yL0uOEzuYy?*nio6>DNZoc`gDgWQJ^(~us?c8+p*4cM|;$GJd zvQK|6==@n+oO=IwamD@rHGj+cty^!NQvcnXZrYG;*s^Wv9qC5?%5kt|*Ul+&yEmlk zr_#+E)>FE9D}}dAF`Hi?IBxV{TROFoaXU8bym|BP4Lj4Rn+NA!zj?EtC@W{$nc8w? zclDmyxFOxSe#?e*%Z443n>VCvXe!WuE&jFnH^RSpK~NmM|CD0cU#va7+WPMGJ2tJ~ UI>pX!+Hgw{WI-oh^PC{~zxB)wtN;K2 delta 2191 zcmcIkdvHuw7(d^+clT~=a29#k(p-5|LNZAdjTGG*LgF1oJxa17$R;7OE1}pMru9tH ziEoTIX+>(ZRh_F;JtnDFy~U$bjMjh5Q1oG{VNB1tS+DA}fA#L{_x--#_qgYL=R0@z zpVHmKv=ecAi+~UUA3=ByAvsjnNsgrJI_Rb%)t&s+pC{@d<;^Iy7UdOr^L&H~L*Yiz z%+kDOOSp22=QbO*;*$IsK5up*G1FSQz23YMU$%F;yIYT*L=xHdXIyeK^ITMqN$#i1x|+5%Oe6{X;4CrKe8#PRG03YGXnAw*FKgoFZsqOj1= zFo#3oSXhc8gopzES3r_2P|PWd1O%+1k`7vVJ+bQ~U2C`~w5gOfR5WpB$y`b-?&9LY zxkL>L2BLx8+An=z=HQ_%&Tpr=jIbD%#`?1XEQ1YVL+m?Hm7^0UTGGnJQ?sq{psvy^ z*`*Q{*pvXh^F=xjm&?({7xKMVVL#SH#ba4qsx?cMG@A$g-D6-RUW==Qx3Qp$PH&#W z+g)6+6cf7op#c{rrP8yF=uTGgS`x$X_!4?Ym}o>Kq*3~?5mUNHsJ-lYFvZoAT|O7!YJcZzu^F*utRz!j5oHY*8ohw!2?Tp z<05kVa5*uzGv?iHjjalmfaT5zGf@Pt$v6Su8@ZWr669m$kO$zw#Y5|$027Ag!)9DP zY;eYSVz01+MUNs{y;Kk>M-#qos=cu8f8Nc1Z3C}p$FZ-geIH4y1y3v`kyc(f0BxBZ z*O;_w&79Ses5Y*7M+2NqaJ;X(Uh0B_ub6i)$-{b^fO&AwoNF-2xL&juARC9z>)1Aj z7piX|4(Ph#(Kb*Ia~t}+Z-hH_!Sn7KOTdku1-G1&BV|<*Yp{6O!J%1@n~J^(HsUYd&h*9wbo%-W)wnDYukLCgmpJW*vD%j;IQQikZ2BSM33Oc! zz*sDs9VyuLv(tq7X|^`5)TxqK+D-)xSeby~CY&Q+2NTW}P&MIuynW0QP3#8&s5m@U z5J{u?l@omil`!3@a>b{V`LMjSgLuiN(zncal}bpL8=ujrmx@eRD4VFO7Zo-x_O_FJA#L4TCG@o3bO;Rip)wwKC}`YKPM+J8+9t zgyY7_%m6P}xlFD-PO4I1x-qrNCv##LI`RI>u_hY98?Qi-abx2yfc?gX%?~A*forxT z@zCR2(z$G1n*cMhPi+pzHFb%2x;D&N?Bth=pP@|%Inq*~DTB;0-;a-Z_$-Xy>gVB~ zZY|(4tZu29I+)Aquj1(<2~+NLrqvhGeP=AD9|!|4PCD=*`0(z5aB-qR2jjtKym4?f zg&I6t9|g0G-|Oq3Im3zL8b_k-kU6Tu4n@OJ^d5?99)u5I5=I|Rh51I#;T0gbpO0h; zm2xy4cBB8OIm zHpY}+-?SJz{^$xojNcxt0mPtQ&Kev}r(KQVSWqYEK_0T-#x Ac>n+a From 772eeb2c1bd91ec3eedc75a5bf76b42ef87546a1 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Tue, 6 Aug 2024 04:47:11 -0600 Subject: [PATCH 31/75] misc --- unittests/svnn_finality_violation_tests.cpp | 32 +++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index ca1972d839..b90503312a 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -366,7 +366,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto real_chain_block_6_result = real_chain.produce_block(); //qc over block #6 makes block #5 final. Since these blocks are different, this is a finality violation. - //moving forward, any auto fake_chain_block_7_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_7_result = real_chain.produce_block(); @@ -412,16 +411,39 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_11_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_11_result.qc_data.qc.has_value()); - //Light client recorded the last QC on fake chain, which is over block #10. + //Light client recorded the last QC on fake chain, which was delivered via block #11, and is over block #10. //Block #10 claims a QC over block #8 (skipping #9). We provide fake block #10 and its QC. - //We also provide the real block #9 and a QC over it, which is a proof of violation of rule #2. - mutable_variant_object valid_rule_2_proof = prepare_rule_2_lt_proof( light_client_data.active_finalizer_policy, + //We also provide the real block #9 and a QC over it delivered via block #10, which is a proof of violation of rule #2. + mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_lt_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, real_chain_block_9_result, real_chain_block_10_result.qc_data.qc.value()); - BOOST_CHECK(shouldPass(real_chain, "rule2a"_n, valid_rule_2_proof)); + BOOST_CHECK(shouldPass(real_chain, "rule2a"_n, valid_rule_2_proof_1)); + + real_chain.vote_propagation = {1,0,0}; + + auto fake_chain_block_12_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_12_result = real_chain.produce_block(); + + BOOST_TEST(fake_chain_block_12_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_12_result.qc_data.qc.has_value()); + + real_chain.vote_propagation = {1,0,1}; + + auto fake_chain_block_13_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_13_result = real_chain.produce_block(); + + BOOST_TEST(fake_chain_block_13_result.qc_data.qc.has_value()); + BOOST_TEST(!real_chain_block_13_result.qc_data.qc.has_value()); + +/* mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_gt_proof( light_client_data.active_finalizer_policy, + light_client_data.high_qc_block, + light_client_data.high_qc, + real_chain_block_9_result, + real_chain_block_10_result.qc_data.qc.value());*/ + } FC_LOG_AND_RETHROW() } From 792462dfdb6f602fd6b56c39a5d009663359bf0b Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Tue, 6 Aug 2024 15:42:44 -0600 Subject: [PATCH 32/75] Refactored light client data cache to properly evaluate finality rules, refactored contract to have a single rule2 implementation --- unittests/svnn_finality_violation_tests.cpp | 92 ++++++++++++------ .../finality_violation/finality_violation.abi | 29 +----- .../finality_violation/finality_violation.cpp | 31 ++++-- .../finality_violation/finality_violation.hpp | 4 +- .../finality_violation.wasm | Bin 64694 -> 63331 bytes 5 files changed, 90 insertions(+), 66 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index b90503312a..4639a0fda0 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -66,15 +66,7 @@ mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, } -mvo prepare_rule_2_lt_proof( const finalizer_policy active_finalizer_policy, - const ibc_block_data_t fake_qc_block, - const qc_t fake_qc, - const ibc_block_data_t real_qc_block, - const qc_t real_qc){ - return prepare_rule_1_proof(active_finalizer_policy, fake_qc_block, fake_qc, real_qc_block, real_qc); -} - -mvo prepare_rule_2_gt_proof( const finalizer_policy active_finalizer_policy, +mvo prepare_rule_2_proof( const finalizer_policy active_finalizer_policy, const ibc_block_data_t fake_qc_block, const qc_t fake_qc, const ibc_block_data_t real_qc_block, @@ -257,28 +249,50 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) finalizer_policy active_finalizer_policy; //observe a stream of blocks as they are received, and store minimal data required to construct finality violation proofs in the future - ibc_block_data_t scan_block(const ibc_block_data_t& block){ + ibc_block_data_t scan_block(const ibc_block_data_t& block_result){ - if (reversible_blocks.size()>=2){ - last_final_block = high_qc_block; - last_final_qc = high_qc; - reversible_blocks.erase(reversible_blocks.begin()); - } + if (block_result.qc_data.qc.has_value()) { + + auto high_qc_block_itr = std::find_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto& r) { + return r.block->block_num() == block_result.qc_data.qc.value().to_qc_claim().block_num; + }); + + if ( high_qc_block_itr != reversible_blocks.end() + && high_qc_block_itr->qc_data.qc.has_value()) { - if (reversible_blocks.size()>=1){ - high_qc_block = reversible_blocks[reversible_blocks.size()-1]; - if (block.qc_data.qc.has_value()) high_qc = block.qc_data.qc.value(); + auto last_final_block_itr = std::find_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto& r) { + return r.block->block_num() == high_qc_block_itr->qc_data.qc.value().to_qc_claim().block_num; + }); + + if ( last_final_block_itr != reversible_blocks.end() + && block_result.qc_data.qc.value().to_qc_claim().is_strong_qc) { + + //new strong QC + last_final_block = *last_final_block_itr; + last_final_qc = high_qc_block_itr->qc_data.qc.value(); + + reversible_blocks.erase(std::remove_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto &r){ + return r.block->block_num() <= last_final_block.block->block_num(); + }), reversible_blocks.end()); + + + } + + high_qc_block = *high_qc_block_itr; + high_qc = block_result.qc_data.qc.value(); + + } } - reversible_blocks.push_back(block); + reversible_blocks.push_back(block_result); - active_finalizer_policy = block.active_finalizer_policy; + active_finalizer_policy = block_result.active_finalizer_policy; - return block; + return block_result; } }; - + data_cache light_client_data; //setup a "fake" chain and a "real" chain @@ -316,6 +330,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //produce a few blocks on the fake chain auto fake_chain_genesis_block_result = light_client_data.scan_block(fake_chain.produce_block()) ; + auto fake_chain_block_1_result = light_client_data.scan_block(fake_chain.produce_block()); auto fake_chain_block_2_result = light_client_data.scan_block(fake_chain.produce_block()); auto fake_chain_block_3_result = light_client_data.scan_block(fake_chain.produce_block()); @@ -323,6 +338,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_REQUIRE(fake_chain_block_4_result.qc_data.qc.has_value()); + BOOST_TEST(light_client_data.reversible_blocks.size() == 2u); + //produce a few blocks on the real chain auto real_chain_genesis_block_result = real_chain.produce_block(); auto real_chain_block_1_result = real_chain.produce_block(); @@ -358,6 +375,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_5_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_5_result = real_chain.produce_block(); + BOOST_TEST(light_client_data.reversible_blocks.size() == 2u); + //verify the chains have diverged BOOST_TEST(fake_chain_block_5_result.finality_digest != real_chain_block_5_result.finality_digest); @@ -410,17 +429,30 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Things are back to normal, and we have a QC on both chains BOOST_TEST(fake_chain_block_11_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_11_result.qc_data.qc.has_value()); - - //Light client recorded the last QC on fake chain, which was delivered via block #11, and is over block #10. + + BOOST_TEST(light_client_data.reversible_blocks.size() == 4u); + + auto fake_chain_block_12_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_12_result = real_chain.produce_block(); + + BOOST_TEST(fake_chain_block_12_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_12_result.qc_data.qc.has_value()); + + BOOST_TEST(light_client_data.reversible_blocks.size() == 2u); + +/* + //Light client recorded the last QC on fake chain, which was delivered via block #11, and is over block #10. //Block #10 claims a QC over block #8 (skipping #9). We provide fake block #10 and its QC. - //We also provide the real block #9 and a QC over it delivered via block #10, which is a proof of violation of rule #2. - mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_lt_proof( light_client_data.active_finalizer_policy, + //We also provide the real block #9 and a QC over it delivered via block #10, as well as a proof of inclusion of which is a proof of violation of rule #2. + mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, real_chain_block_9_result, real_chain_block_10_result.qc_data.qc.value()); - BOOST_CHECK(shouldPass(real_chain, "rule2a"_n, valid_rule_2_proof_1)); + //less than rule + BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_1)); + real_chain.vote_propagation = {1,0,0}; @@ -438,12 +470,14 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_13_result.qc_data.qc.has_value()); BOOST_TEST(!real_chain_block_13_result.qc_data.qc.has_value()); -/* mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_gt_proof( light_client_data.active_finalizer_policy, + mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_gt_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, real_chain_block_9_result, - real_chain_block_10_result.qc_data.qc.value());*/ + real_chain_block_10_result.qc_data.qc.value()); + //greater than rule + BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2));*/ } FC_LOG_AND_RETHROW() } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 5966d4a5c2..fd8a62c753 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -319,25 +319,7 @@ ] }, { - "name": "rule2a", - "base": "", - "fields": [ - { - "name": "finalizer_policy", - "type": "finalizer_policy_input" - }, - { - "name": "proof_1", - "type": "finality_proof" - }, - { - "name": "proof_2", - "type": "finality_proof" - } - ] - }, - { - "name": "rule2b", + "name": "rule2", "base": "", "fields": [ { @@ -401,13 +383,8 @@ "ricardian_contract": "" }, { - "name": "rule2a", - "type": "rule2a", - "ricardian_contract": "" - }, - { - "name": "rule2b", - "type": "rule2b", + "name": "rule2", + "type": "rule2", "ricardian_contract": "" }, { diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 87d7b21613..52c97f73ec 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -30,11 +30,12 @@ void check_rule_2_qcs( const finalizer_policy_input finalizer_policy, const fina _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); + } //Proof_1 is presumably from a fake, hidden chain, while proof_2 is presumably from the real, discoverable chain //The block referenced by the proof of inclusion must be proven as a final ancestor of proof_2, while also be proven to conflict with proof_1 -ACTION finality_violation::rule2a( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ +/*ACTION finality_violation::rule2a( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; @@ -47,26 +48,38 @@ ACTION finality_violation::rule2a( const finalizer_policy_input finalizer_policy check_rule_2_qcs(finalizer_policy, proof_1, proof_2); -} +}*/ -ACTION finality_violation::rule2b( const finalizer_policy_input finalizer_policy, - const finality_proof proof_1, - const finality_proof proof_2, +ACTION finality_violation::rule2( const finalizer_policy_input finalizer_policy, + const finality_proof proof_1, + const finality_proof proof_2, const block_proof_of_inclusion proof_of_inclusion){ block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; - block_timestamp last_claim_timestamp_1 = proof_1.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - block_timestamp last_claim_timestamp_2 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + block_timestamp last_claim_timestamp_1 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - bool greaterThanRule = last_claim_timestamp_2 < timestamp_1 && timestamp_1 < timestamp_2; + bool time_range_conflict = last_claim_timestamp_1 < timestamp_2 && timestamp_2 < timestamp_1; - check(greaterThanRule, "proofs must demonstrate a conflicting time range"); + check(time_range_conflict, "proofs must demonstrate a conflicting time range"); check_rule_2_qcs(finalizer_policy, proof_1, proof_2); check(proof_of_inclusion.root() == proof_2.qc_block.finality_mroot, "invalid proof of inclusion"); + check(std::holds_alternative(proof_of_inclusion.target), "must provide extended block data object"); + + extended_block_data target = std::get(proof_of_inclusion.target); + + block_timestamp target_timestamp = target.timestamp; + + check(timestamp_1 == target_timestamp, "timestamp of proof_1 block of the target block of the proof of inclusion must be the same"); + + auto finality_digest_claimed = block_finality_data_internal(proof_1.qc_block).finality_digest(); + auto finality_digest_actual = block_finality_data_internal(target.finality_data).finality_digest() ; + + check(finality_digest_claimed != finality_digest_actual, "finality digests must be different for a finality violation proof to be valid"); + } ACTION finality_violation::rule3(){ diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 54a90c1581..5ae50a7f85 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -14,8 +14,8 @@ CONTRACT finality_violation : public contract { using contract::contract; ACTION rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); - ACTION rule2a(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); - ACTION rule2b(const finalizer_policy_input finalizer_policy, + //ACTION rule2a(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); + ACTION rule2(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2, const block_proof_of_inclusion proof_of_inclusion); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index c69ac9df5f3ab063e4f63340dd28807e10c2a18b..29104cc79e6717d3b54c397b91258451c420062f 100755 GIT binary patch delta 3570 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 TqeXCRvl*%1L@`@xydeA+X3IUo From 00820b24be7e0297be30dabf5115179200fc82bf Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 7 Aug 2024 10:26:23 -0600 Subject: [PATCH 33/75] Rule 2 WIP --- libraries/chain/finality/finality_core.cpp | 20 +- unittests/svnn_finality_violation_tests.cpp | 184 +++++++++++----- .../test-contracts/savanna/common/savanna.hpp | 55 +++++ .../finality_violation/finality_violation.abi | 203 ++---------------- .../finality_violation/finality_violation.cpp | 64 +++--- .../finality_violation/finality_violation.hpp | 8 +- .../finality_violation.wasm | Bin 63331 -> 49027 bytes 7 files changed, 255 insertions(+), 279 deletions(-) diff --git a/libraries/chain/finality/finality_core.cpp b/libraries/chain/finality/finality_core.cpp index 2a8228b992..a83407f9aa 100644 --- a/libraries/chain/finality/finality_core.cpp +++ b/libraries/chain/finality/finality_core.cpp @@ -158,6 +158,8 @@ digest_type finality_core::get_reversible_blocks_mroot() const { return {}; } + std::cout << "\n*** get_reversible_blocks_mroot *** \n\n"; + // Build a merkle tree of a sequence of records including block number, // block timestamp, finality digest, and the timestamp of the parent block. std::vector block_ref_digests; @@ -170,10 +172,24 @@ digest_type finality_core::get_reversible_blocks_mroot() const { .parent_timestamp = refs[i-1].timestamp }; - block_ref_digests.emplace_back(fc::sha256::hash(data)); + std::cout << "\ndata.block_num " << data.block_num << "\n"; + std::cout << "data.timestamp " << data.timestamp << "\n"; + std::cout << "data.finality_digest " << data.finality_digest << "\n"; + std::cout << "data.parent_timestamp " << data.parent_timestamp << "\n\n"; + + digest_type hash = fc::sha256::hash(data); + + std::cout << "leaf hash " << hash << "\n\n"; + + block_ref_digests.emplace_back(hash); } - return calculate_merkle(block_ref_digests); + digest_type root = calculate_merkle(block_ref_digests); + + std::cout << "root " << root << "\n\n"; + + return root; + } /** diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 4639a0fda0..d35bf75faa 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -67,67 +67,51 @@ mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, } mvo prepare_rule_2_proof( const finalizer_policy active_finalizer_policy, - const ibc_block_data_t fake_qc_block, - const qc_t fake_qc, - const ibc_block_data_t real_qc_block, - const qc_t real_qc, - const ibc_block_data_t real_target_block, + const ibc_block_data_t high_qc_block, + const qc_t high_qc, + const ibc_block_data_t low_qc_block, + const qc_t low_qc, + const ibc_block_data_t target_block, const uint32_t target_index, - const std::vector merkle_branches){ + const std::vector digests){ return mvo() ("finalizer_policy", active_finalizer_policy) - ("proof_1", mvo() + ("high_proof", mvo() ("qc_block", mvo() ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) ("pending_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy_start_timestamp", high_qc_block.last_pending_finalizer_policy_start_timestamp) ("last_pending_finalizer_policy", active_finalizer_policy) - ("level_3_commitments", fake_qc_block.level_3_commitments) - ("witness_hash", fake_qc_block.base_digest) - ("finality_mroot", fake_qc_block.finality_root) + ("level_3_commitments", high_qc_block.level_3_commitments) + ("witness_hash", high_qc_block.base_digest) + ("finality_mroot", high_qc_block.finality_root) ) ("active_policy_qc", mvo() - ("signature", fake_qc.active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) + ("signature", high_qc.active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(high_qc.active_policy_sig.strong_votes.value())) ) ) - ("proof_2", mvo() + ("low_proof", mvo() ("qc_block", mvo() ("major_version", 1) ("minor_version", 0) ("active_finalizer_policy_generation", 1) ("pending_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy_start_timestamp", low_qc_block.last_pending_finalizer_policy_start_timestamp) ("last_pending_finalizer_policy", active_finalizer_policy) - ("level_3_commitments", real_qc_block.level_3_commitments) - ("witness_hash", real_qc_block.base_digest) - ("finality_mroot", real_qc_block.finality_root) + ("level_3_commitments", low_qc_block.level_3_commitments) + ("witness_hash", low_qc_block.base_digest) + ("finality_mroot", low_qc_block.finality_root) ) ("active_policy_qc", mvo() - ("signature", real_qc.active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) + ("signature", low_qc.active_policy_sig.sig.to_string()) + ("finalizers", finality_proof::finalizers_string(low_qc.active_policy_sig.strong_votes.value())) ) ) - ("target_block_proof_of_inclusion", mvo() - ("target_block_index", target_index) - ("final_block_index", target_index) - ("target", fc::variants{"extended_block_data", mvo() //target block #2 - ("major_version", 1) - ("minor_version", 0) - ("finality_digest", real_target_block.finality_digest) - ("timestamp", real_target_block.block->timestamp) - ("parent_timestamp", real_target_block.parent_timestamp) - ("dynamic_data", mvo() - ("block_num", real_target_block.block->block_num()) - ("action_proofs", fc::variants()) - ("action_mroot", real_target_block.action_mroot) - )} - ) - ("merkle_branches", merkle_branches) - ); + ("reversible_blocks_digests", digests); } @@ -152,9 +136,53 @@ bool shouldFail(const finality_proof::proof_test_cluster& chain, const account_n } +digest_type compute_block_ref_digest(const ibc_block_data_t b){ + + block_ref_digest_data data = { + .block_num = b.block->block_num(), + .timestamp = b.block->timestamp, + .finality_digest = b.finality_digest, + .parent_timestamp = b.parent_timestamp + }; + + digest_type digest = fc::sha256::hash(data); + + return digest; + +} + BOOST_AUTO_TEST_SUITE(svnn_finality_violation) + BOOST_AUTO_TEST_CASE(contract_get_merkle_root) { try { + + return; + + //verify that get_merkle_root function from savanna contract produces the same results than calculate_merkle + std::vector digests = { digest_type{"cb7ea678fe3a84fcff103f9cf08b97e5dd0b01bee54635f49255050f34bdbf34"}, + digest_type{"ac2607856dea36137a1d6d0dd79b980f1ac7b104c59dc1e5fdd5f1568c7169c3"}, + digest_type{"a1af721189bdf59121043169b891a9e8fa545611be35eb95139bb1f2f409fcb3"}, + digest_type{"1afa2d6299822185c1f5332445f1fe304e12bf51a6221abdf75fcda6cdc38f56"}, + digest_type{"2dafbbd44d65e07ba48652cd5d83857765d19f93075ae2a9eb7749d2cdf1341d"} }; + + digest_type root = calculate_merkle(digests); + + tester c; + + c.create_accounts( {account_name("violation")} ); + + c.produce_block(); + + c.set_code( "violation"_n, test_contracts::finality_violation_wasm() ); + c.set_abi( "violation"_n, test_contracts::finality_violation_abi() ); + + c.push_action("violation"_n, "testmroot"_n, "eosio"_n, mvo()("root", root)("reversible_blocks_digests", digests)); + + + } FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE(cluster_vote_propagation_tests) { try { + + return; finality_proof::proof_test_cluster cluster_1; finality_proof::proof_test_cluster cluster_2; @@ -282,6 +310,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) high_qc = block_result.qc_data.qc.value(); } + } reversible_blocks.push_back(block_result); @@ -289,6 +318,30 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) active_finalizer_policy = block_result.active_finalizer_policy; return block_result; + + } + + std::vector get_reversible_blocks_digests(){ + + std::vector block_ref_digests; + + for (int i = 0 ; i < reversible_blocks.size() - 1; i++){ + + ibc_block_data_t b = reversible_blocks[i]; + + std::cout << "b.block->block_num() " << b.block->block_num() << "\n"; + std::cout << "b.block->timestamp " << b.block->timestamp << "\n"; + std::cout << "b.finality_digest " << b.finality_digest << "\n"; + std::cout << "b.parent_timestamp " << b.parent_timestamp << "\n"; + + digest_type digest = compute_block_ref_digest(b); + + block_ref_digests.push_back(digest); + + } + + return block_ref_digests; + } }; @@ -329,7 +382,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //produce a few blocks on the fake chain auto fake_chain_genesis_block_result = light_client_data.scan_block(fake_chain.produce_block()) ; - auto fake_chain_block_1_result = light_client_data.scan_block(fake_chain.produce_block()); auto fake_chain_block_2_result = light_client_data.scan_block(fake_chain.produce_block()); @@ -338,7 +390,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_REQUIRE(fake_chain_block_4_result.qc_data.qc.has_value()); - BOOST_TEST(light_client_data.reversible_blocks.size() == 2u); + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); //produce a few blocks on the real chain auto real_chain_genesis_block_result = real_chain.produce_block(); @@ -375,7 +427,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_5_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_5_result = real_chain.produce_block(); - BOOST_TEST(light_client_data.reversible_blocks.size() == 2u); + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); //verify the chains have diverged BOOST_TEST(fake_chain_block_5_result.finality_digest != real_chain_block_5_result.finality_digest); @@ -406,22 +458,34 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); - //we temporarily disable a finalizer on the fake chain, which serves to set up a proof of violation of rule #2 + //we temporarily disable a finalizer on the fake chain, which allow us to set up a proof of violation of rule #2 fake_chain.vote_propagation = {1,0,0}; //produce a block on a fake chain without propagating votes to all nodes auto fake_chain_block_9_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_9_result = real_chain.produce_block(); + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + //restore vote propagation for fake chain. This leaves a one-block gap where no finality progress was achieved fake_chain.vote_propagation = {1,1,0}; auto fake_chain_block_10_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_10_result = real_chain.produce_block(); - //Real chain has a QC on #9, but fake chain doesn't + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 2u); + + //get the reversible blocks for block #10 (should be only one digest, block ref data of #9) + std::vector reversible_blocks_digests = light_client_data.get_reversible_blocks_digests(); + + BOOST_TEST(reversible_blocks_digests.size() == 2u); + BOOST_TEST(reversible_blocks_digests[0] == compute_block_ref_digest(fake_chain_block_8_result)); + BOOST_TEST(reversible_blocks_digests[1] == compute_block_ref_digest(fake_chain_block_9_result)); + + //Real chain has a QC on #9 carried by #10, but fake chain doesn't BOOST_TEST(!fake_chain_block_10_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_10_result.qc_data.qc.has_value()); + auto fake_chain_block_11_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_11_result = real_chain.produce_block(); @@ -430,7 +494,21 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_11_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_11_result.qc_data.qc.has_value()); - BOOST_TEST(light_client_data.reversible_blocks.size() == 4u); + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 3u); + + //Light client recorded the last QC on fake chain, which was delivered via block #11, and is over block #10. + //Block #10 claims a QC over block #8 (skipping #9). We provide fake block #10 and its QC. + //We also provide the real block #9 and a QC over it delivered via block #10, as well as a proof of inclusion of which is a proof of violation of rule #2. + mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, + fake_chain_block_10_result, + fake_chain_block_11_result.qc_data.qc.value(), + real_chain_block_9_result, + real_chain_block_10_result.qc_data.qc.value(), + real_chain_block_10_result, + 2, + reversible_blocks_digests); + + real_chain.node0.push_action("violation"_n, "rule2"_n, "violation"_n, valid_rule_2_proof_1); auto fake_chain_block_12_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_12_result = real_chain.produce_block(); @@ -438,22 +516,14 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_12_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_12_result.qc_data.qc.has_value()); - BOOST_TEST(light_client_data.reversible_blocks.size() == 2u); - -/* - //Light client recorded the last QC on fake chain, which was delivered via block #11, and is over block #10. - //Block #10 claims a QC over block #8 (skipping #9). We provide fake block #10 and its QC. - //We also provide the real block #9 and a QC over it delivered via block #10, as well as a proof of inclusion of which is a proof of violation of rule #2. - mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, - light_client_data.high_qc_block, - light_client_data.high_qc, - real_chain_block_9_result, - real_chain_block_10_result.qc_data.qc.value()); - - //less than rule - BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_1)); + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + + //BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_1)); +/* */ + +/* real_chain.vote_propagation = {1,0,0}; auto fake_chain_block_12_result = light_client_data.scan_block(fake_chain.produce_block()); diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index 8f1524f538..b5e335f2d7 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -184,6 +184,31 @@ namespace savanna { check(_verify(s_agg_pub_key, qc.signature, message), "signature verification failed"); } + checksum256 get_merkle_root(std::vector leaves) { + std::vector> tree; + + tree.push_back(leaves); + + std::vector current_level = leaves; + while (current_level.size() > 1) { + std::vector next_level; + for (size_t i = 0; i < current_level.size(); i += 2) { + checksum256 left = current_level[i]; + if (i + 1 < current_level.size()) { + checksum256 right = current_level[i + 1]; + next_level.push_back(hash_pair(std::make_pair(left, right))); + } else { + next_level.push_back(left); + } + } + tree.insert(tree.begin(), next_level); // Prepend to build the tree upwards + current_level = next_level; + } + + return current_level.front(); + + } + struct authseq { name account; uint64_t sequence = 0; @@ -291,6 +316,36 @@ namespace savanna { checksum256 l3_commitments_digest{}; }; + struct block_ref_data { + uint32_t block_num{0}; + block_timestamp_type timestamp; + checksum256 finality_digest; + block_timestamp_type parent_timestamp; + + checksum256 digest() const { + auto result = eosio::pack(*this); + checksum256 hash = sha256(result.data(), result.size()); + return hash; + }; + + }; + + struct reversible_proof_of_inclusion { + uint64_t target_block_index = 0; + uint64_t final_block_index = 0; + + block_ref_data target; + + std::vector merkle_branches; + + //returns the merkle root obtained by hashing target.digest() with merkle_branches + checksum256 root() const { + checksum256 digest = target.digest(); + checksum256 root = _compute_root(merkle_branches, digest, target_block_index, final_block_index); + return root; + }; + }; + struct dynamic_data_v0 { //block_num is always present uint32_t block_num = 0; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index fd8a62c753..6167e949a1 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -1,89 +1,8 @@ { "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", "version": "eosio::abi/1.2", - "types": [ - { - "new_type_name": "block_data_type", - "type": "variant_simple_block_data_extended_block_data" - } - ], + "types": [], "structs": [ - { - "name": "action", - "base": "action_base", - "fields": [ - { - "name": "data", - "type": "bytes" - }, - { - "name": "return_value", - "type": "bytes" - } - ] - }, - { - "name": "action_base", - "base": "", - "fields": [ - { - "name": "account", - "type": "name" - }, - { - "name": "name", - "type": "name" - }, - { - "name": "authorization", - "type": "permission_level[]" - } - ] - }, - { - "name": "action_data", - "base": "", - "fields": [ - { - "name": "action", - "type": "action" - }, - { - "name": "receiver", - "type": "name" - }, - { - "name": "recv_sequence", - "type": "uint64" - }, - { - "name": "witness_hash", - "type": "checksum256" - } - ] - }, - { - "name": "action_proof_of_inclusion", - "base": "", - "fields": [ - { - "name": "target_block_index", - "type": "uint64" - }, - { - "name": "final_block_index", - "type": "uint64" - }, - { - "name": "target", - "type": "action_data" - }, - { - "name": "merkle_branches", - "type": "checksum256[]" - } - ] - }, { "name": "block_finality_data", "base": "", @@ -126,68 +45,6 @@ } ] }, - { - "name": "block_proof_of_inclusion", - "base": "", - "fields": [ - { - "name": "target_block_index", - "type": "uint64" - }, - { - "name": "final_block_index", - "type": "uint64" - }, - { - "name": "target", - "type": "block_data_type" - }, - { - "name": "merkle_branches", - "type": "checksum256[]" - } - ] - }, - { - "name": "dynamic_data_v0", - "base": "", - "fields": [ - { - "name": "block_num", - "type": "uint32" - }, - { - "name": "action_proofs", - "type": "action_proof_of_inclusion[]" - }, - { - "name": "action_mroot", - "type": "checksum256?" - } - ] - }, - { - "name": "extended_block_data", - "base": "", - "fields": [ - { - "name": "finality_data", - "type": "block_finality_data" - }, - { - "name": "timestamp", - "type": "block_timestamp_type" - }, - { - "name": "parent_timestamp", - "type": "block_timestamp_type" - }, - { - "name": "dynamic_data", - "type": "dynamic_data_v0" - } - ] - }, { "name": "finality_proof", "base": "", @@ -268,20 +125,6 @@ } ] }, - { - "name": "permission_level", - "base": "", - "fields": [ - { - "name": "actor", - "type": "name" - }, - { - "name": "permission", - "type": "name" - } - ] - }, { "name": "quorum_certificate_input", "base": "", @@ -327,16 +170,16 @@ "type": "finalizer_policy_input" }, { - "name": "proof_1", + "name": "high_proof", "type": "finality_proof" }, { - "name": "proof_2", + "name": "low_proof", "type": "finality_proof" }, { - "name": "proof_of_inclusion", - "type": "block_proof_of_inclusion" + "name": "reversible_blocks_digests", + "type": "checksum256[]" } ] }, @@ -346,32 +189,16 @@ "fields": [] }, { - "name": "simple_block_data", + "name": "testmroot", "base": "", "fields": [ { - "name": "major_version", - "type": "uint32" - }, - { - "name": "minor_version", - "type": "uint32" - }, - { - "name": "timestamp", - "type": "block_timestamp_type" - }, - { - "name": "parent_timestamp", - "type": "block_timestamp_type" - }, - { - "name": "finality_digest", + "name": "root", "type": "checksum256" }, { - "name": "dynamic_data", - "type": "dynamic_data_v0" + "name": "reversible_blocks_digests", + "type": "checksum256[]" } ] } @@ -391,15 +218,15 @@ "name": "rule3", "type": "rule3", "ricardian_contract": "" + }, + { + "name": "testmroot", + "type": "testmroot", + "ricardian_contract": "" } ], "tables": [], "ricardian_clauses": [], - "variants": [ - { - "name": "variant_simple_block_data_extended_block_data", - "types": ["simple_block_data","extended_block_data"] - } - ], + "variants": [], "action_results": [] } \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 52c97f73ec..cdf6914558 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -1,38 +1,35 @@ #include "finality_violation.hpp" -ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ +void check_qcs( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ check(proof_1.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); check(proof_2.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); - block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; - block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; - - check(timestamp_1 == timestamp_2, "proofs must be over blocks that have the same timestamp"); - checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); - check(digest_1 != digest_2, "finality digests must be different"); - _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); } -void check_rule_2_qcs( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ +ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ + + check_qcs(finalizer_policy, proof_1, proof_2); + + block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; + block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; + + check(timestamp_1 == timestamp_2, "proofs must be over blocks that have the same timestamp"); - check(proof_1.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); - check(proof_2.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); - checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); - _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); - _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); + check(digest_1 != digest_2, "finality digests must be different"); } + //Proof_1 is presumably from a fake, hidden chain, while proof_2 is presumably from the real, discoverable chain //The block referenced by the proof of inclusion must be proven as a final ancestor of proof_2, while also be proven to conflict with proof_1 /*ACTION finality_violation::rule2a( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ @@ -50,38 +47,47 @@ void check_rule_2_qcs( const finalizer_policy_input finalizer_policy, const fina }*/ -ACTION finality_violation::rule2( const finalizer_policy_input finalizer_policy, - const finality_proof proof_1, - const finality_proof proof_2, - const block_proof_of_inclusion proof_of_inclusion){ +ACTION finality_violation::rule2( const finalizer_policy_input finalizer_policy, + const finality_proof high_proof, + const finality_proof low_proof, + const std::vector reversible_blocks_digests){ - block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; - block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; - block_timestamp last_claim_timestamp_1 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + print("check QC\n"); + check_qcs(finalizer_policy, high_proof, low_proof); + + block_timestamp high_proof_timestamp = high_proof.qc_block.level_3_commitments.value().timestamp; + block_timestamp low_proof_timestamp = low_proof.qc_block.level_3_commitments.value().timestamp; + block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - bool time_range_conflict = last_claim_timestamp_1 < timestamp_2 && timestamp_2 < timestamp_1; + print("check range conflict\n"); + bool time_range_conflict = high_proof_last_claim_timestamp < low_proof_timestamp && low_proof_timestamp < high_proof_timestamp; check(time_range_conflict, "proofs must demonstrate a conflicting time range"); - check_rule_2_qcs(finalizer_policy, proof_1, proof_2); - - check(proof_of_inclusion.root() == proof_2.qc_block.finality_mroot, "invalid proof of inclusion"); + print("check merkle root\n"); + check(get_merkle_root(reversible_blocks_digests) == high_proof.qc_block.level_3_commitments->reversible_blocks_mroot, "reversible_blocks_digests merkle root does not match reversible_blocks_mroot"); - check(std::holds_alternative(proof_of_inclusion.target), "must provide extended block data object"); +/* check(std::holds_alternative(proof_of_inclusion.target), "must provide extended block data object"); extended_block_data target = std::get(proof_of_inclusion.target); block_timestamp target_timestamp = target.timestamp; - check(timestamp_1 == target_timestamp, "timestamp of proof_1 block of the target block of the proof of inclusion must be the same"); + check(high_proof_timestamp == target_timestamp, "timestamp of high_proof block of the target block of the proof of inclusion must be the same"); - auto finality_digest_claimed = block_finality_data_internal(proof_1.qc_block).finality_digest(); + auto finality_digest_claimed = block_finality_data_internal(high_proof.qc_block).finality_digest(); auto finality_digest_actual = block_finality_data_internal(target.finality_data).finality_digest() ; - check(finality_digest_claimed != finality_digest_actual, "finality digests must be different for a finality violation proof to be valid"); + check(finality_digest_claimed != finality_digest_actual, "finality digests must be different for a finality violation proof to be valid");*/ } ACTION finality_violation::rule3(){ + +} + +ACTION finality_violation::testmroot(const checksum256 root, const std::vector reversible_blocks_digests){ + checksum256 c_root = get_merkle_root(reversible_blocks_digests); + check(c_root == root, "invalid root"); } \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 5ae50a7f85..4687ffc78c 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -16,9 +16,11 @@ CONTRACT finality_violation : public contract { ACTION rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); //ACTION rule2a(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); ACTION rule2(const finalizer_policy_input finalizer_policy, - const finality_proof proof_1, - const finality_proof proof_2, - const block_proof_of_inclusion proof_of_inclusion); + const finality_proof high_proof, + const finality_proof low_proof, + const std::vector reversible_blocks_digests); ACTION rule3(); + ACTION testmroot(const checksum256 root, const std::vector reversible_blocks_digests); + }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 29104cc79e6717d3b54c397b91258451c420062f..bfd575286290b4a5f173a0e6460ff2c297f06167 100755 GIT binary patch delta 14494 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
9jvE2O9Z9A5l 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 z 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 Date: Wed, 7 Aug 2024 12:02:48 -0600 Subject: [PATCH 34/75] Completed rule 2 --- libraries/chain/finality/finality_core.cpp | 20 +-- unittests/finality_proof.hpp | 60 +++++-- unittests/svnn_finality_violation_tests.cpp | 157 ++++++++++++++---- .../finality_violation/finality_violation.cpp | 18 +- .../finality_violation.wasm | Bin 49027 -> 49449 bytes 5 files changed, 174 insertions(+), 81 deletions(-) diff --git a/libraries/chain/finality/finality_core.cpp b/libraries/chain/finality/finality_core.cpp index a83407f9aa..2a8228b992 100644 --- a/libraries/chain/finality/finality_core.cpp +++ b/libraries/chain/finality/finality_core.cpp @@ -158,8 +158,6 @@ digest_type finality_core::get_reversible_blocks_mroot() const { return {}; } - std::cout << "\n*** get_reversible_blocks_mroot *** \n\n"; - // Build a merkle tree of a sequence of records including block number, // block timestamp, finality digest, and the timestamp of the parent block. std::vector block_ref_digests; @@ -172,24 +170,10 @@ digest_type finality_core::get_reversible_blocks_mroot() const { .parent_timestamp = refs[i-1].timestamp }; - std::cout << "\ndata.block_num " << data.block_num << "\n"; - std::cout << "data.timestamp " << data.timestamp << "\n"; - std::cout << "data.finality_digest " << data.finality_digest << "\n"; - std::cout << "data.parent_timestamp " << data.parent_timestamp << "\n\n"; - - digest_type hash = fc::sha256::hash(data); - - std::cout << "leaf hash " << hash << "\n\n"; - - block_ref_digests.emplace_back(hash); + block_ref_digests.emplace_back(fc::sha256::hash(data)); } - digest_type root = calculate_merkle(block_ref_digests); - - std::cout << "root " << root << "\n\n"; - - return root; - + return calculate_merkle(block_ref_digests); } /** diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 4007dd6fe3..8ad6aa72d9 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -10,22 +10,50 @@ namespace finality_proof { // data relevant to IBC struct ibc_block_data_t { - signed_block_ptr block; - qc_data_t qc_data; - action_trace onblock_trace; - finality_data_t finality_data; - finalizer_policy active_finalizer_policy; - finalizer_policy last_pending_finalizer_policy; - finalizer_policy last_proposed_finalizer_policy; - digest_type action_mroot; //this is the real action_mroot, as returned from finality_data - digest_type base_digest; - block_timestamp_type last_pending_finalizer_policy_start_timestamp; - digest_type finality_digest; - level_3_commitments_t level_3_commitments; - level_2_commitments_t level_2_commitments; - digest_type finality_leaf; - digest_type finality_root; - block_timestamp_type parent_timestamp; + signed_block_ptr block = nullptr; + qc_data_t qc_data{}; + action_trace onblock_trace{}; + finality_data_t finality_data{}; + finalizer_policy active_finalizer_policy{}; + finalizer_policy last_pending_finalizer_policy{}; + finalizer_policy last_proposed_finalizer_policy{}; + digest_type action_mroot{}; //this is the real action_mroot, as returned from finality_data + digest_type base_digest{}; + block_timestamp_type last_pending_finalizer_policy_start_timestamp{}; + digest_type finality_digest{}; + level_3_commitments_t level_3_commitments{}; + level_2_commitments_t level_2_commitments{}; + digest_type finality_leaf{}; + digest_type finality_root{}; + block_timestamp_type parent_timestamp{}; + + digest_type level_3_commitments_digest() const { + return fc::sha256::hash(level_3_commitments); + } + + digest_type level_2_commitments_digest() const { + return fc::sha256::hash(level_2_commitments); + } + + }; + + struct ibc_block_data_tt { + signed_block block{}; + qc_data_t qc_data{}; + action_trace onblock_trace{}; + finality_data_t finality_data{}; + finalizer_policy active_finalizer_policy{}; + finalizer_policy last_pending_finalizer_policy{}; + finalizer_policy last_proposed_finalizer_policy{}; + digest_type action_mroot{}; //this is the real action_mroot, as returned from finality_data + digest_type base_digest{}; + block_timestamp_type last_pending_finalizer_policy_start_timestamp{}; + digest_type finality_digest{}; + level_3_commitments_t level_3_commitments{}; + level_2_commitments_t level_2_commitments{}; + digest_type finality_leaf{}; + digest_type finality_root{}; + block_timestamp_type parent_timestamp{}; digest_type level_3_commitments_digest() const { return fc::sha256::hash(level_3_commitments); diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index d35bf75faa..7d055a8bfb 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -21,10 +21,39 @@ using namespace finality_proof; using mvo = mutable_variant_object; +struct minimal_block_data { + + uint32_t block_num{0}; + block_timestamp_type timestamp{}; + block_timestamp_type parent_timestamp{}; + digest_type finality_digest{}; + block_timestamp_type last_pending_finalizer_policy_start_timestamp{}; + level_3_commitments_t level_3_commitments{}; + digest_type base_digest{}; + digest_type finality_root{}; + qc_data_t qc_data{}; + +}; + +minimal_block_data get_minimal_block_data(const ibc_block_data_t block_result){ + + return minimal_block_data { + .block_num = block_result.block->block_num(), + .timestamp = block_result.block->timestamp, + .parent_timestamp = block_result.parent_timestamp, + .finality_digest = block_result.finality_digest, + .last_pending_finalizer_policy_start_timestamp = block_result.last_pending_finalizer_policy_start_timestamp, + .level_3_commitments = block_result.level_3_commitments, + .base_digest = block_result.base_digest, + .finality_root = block_result.finality_root, + .qc_data = block_result.qc_data + }; +} + mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, - const ibc_block_data_t fake_qc_block, + const minimal_block_data fake_qc_block, const qc_t fake_qc, - const ibc_block_data_t real_qc_block, + const minimal_block_data real_qc_block, const qc_t real_qc){ return mvo() @@ -67,12 +96,10 @@ mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, } mvo prepare_rule_2_proof( const finalizer_policy active_finalizer_policy, - const ibc_block_data_t high_qc_block, + const minimal_block_data high_qc_block, const qc_t high_qc, - const ibc_block_data_t low_qc_block, + const minimal_block_data low_qc_block, const qc_t low_qc, - const ibc_block_data_t target_block, - const uint32_t target_index, const std::vector digests){ return mvo() @@ -151,6 +178,21 @@ digest_type compute_block_ref_digest(const ibc_block_data_t b){ } +digest_type compute_block_ref_digest(const minimal_block_data b){ + + block_ref_digest_data data = { + .block_num = b.block_num, + .timestamp = b.timestamp, + .finality_digest = b.finality_digest, + .parent_timestamp = b.parent_timestamp + }; + + digest_type digest = fc::sha256::hash(data); + + return digest; + +} + BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_AUTO_TEST_CASE(contract_get_merkle_root) { try { @@ -260,14 +302,14 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //It only operates in optimistic mode, which is sufficient for finality violation proofs testing purposes //store the reversible blocks - std::vector reversible_blocks; + std::vector reversible_blocks; //store the last block over which we have a QC, as well as said QC - ibc_block_data_t high_qc_block; + minimal_block_data high_qc_block; qc_t high_qc; //store the last final block, as wall as the first QC over it. The last_final_qc and high_qc together constitute the 2-chains required for finality progress - ibc_block_data_t last_final_block; + minimal_block_data last_final_block; qc_t last_final_qc; //store all policies sunset data @@ -279,17 +321,29 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //observe a stream of blocks as they are received, and store minimal data required to construct finality violation proofs in the future ibc_block_data_t scan_block(const ibc_block_data_t& block_result){ + minimal_block_data block_data{ + .block_num = block_result.block->block_num(), + .timestamp = block_result.block->timestamp, + .parent_timestamp = block_result.parent_timestamp, + .finality_digest = block_result.finality_digest, + .last_pending_finalizer_policy_start_timestamp = block_result.last_pending_finalizer_policy_start_timestamp, + .level_3_commitments = block_result.level_3_commitments, + .base_digest = block_result.base_digest, + .finality_root = block_result.finality_root, + .qc_data = block_result.qc_data + }; + if (block_result.qc_data.qc.has_value()) { auto high_qc_block_itr = std::find_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto& r) { - return r.block->block_num() == block_result.qc_data.qc.value().to_qc_claim().block_num; + return r.block_num == block_result.qc_data.qc.value().to_qc_claim().block_num; }); if ( high_qc_block_itr != reversible_blocks.end() && high_qc_block_itr->qc_data.qc.has_value()) { auto last_final_block_itr = std::find_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto& r) { - return r.block->block_num() == high_qc_block_itr->qc_data.qc.value().to_qc_claim().block_num; + return r.block_num == high_qc_block_itr->qc_data.qc.value().to_qc_claim().block_num; }); if ( last_final_block_itr != reversible_blocks.end() @@ -300,7 +354,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) last_final_qc = high_qc_block_itr->qc_data.qc.value(); reversible_blocks.erase(std::remove_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto &r){ - return r.block->block_num() <= last_final_block.block->block_num(); + return r.block_num <= last_final_block.block_num; }), reversible_blocks.end()); @@ -313,7 +367,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) } - reversible_blocks.push_back(block_result); + reversible_blocks.push_back(block_data); active_finalizer_policy = block_result.active_finalizer_policy; @@ -327,10 +381,10 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) for (int i = 0 ; i < reversible_blocks.size() - 1; i++){ - ibc_block_data_t b = reversible_blocks[i]; + minimal_block_data b = reversible_blocks[i]; - std::cout << "b.block->block_num() " << b.block->block_num() << "\n"; - std::cout << "b.block->timestamp " << b.block->timestamp << "\n"; + std::cout << "b.block_num " << b.block_num << "\n"; + std::cout << "b.timestamp " << b.timestamp << "\n"; std::cout << "b.finality_digest " << b.finality_digest << "\n"; std::cout << "b.parent_timestamp " << b.parent_timestamp << "\n"; @@ -408,16 +462,16 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) mutable_variant_object invalid_rule_1_proof_1 = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, - real_chain_block_3_result, - real_chain_block_4_result.qc_data.qc.value()); //same finality digest, not a violation proof + get_minimal_block_data(real_chain_block_3_result), + get_minimal_block_data(real_chain_block_4_result).qc_data.qc.value()); //same finality digest, not a violation proof BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); mutable_variant_object invalid_rule_1_proof_2 = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, - real_chain_block_2_result, - real_chain_block_3_result.qc_data.qc.value()); //different timestamps, not a violation proof + get_minimal_block_data(real_chain_block_2_result), + get_minimal_block_data(real_chain_block_3_result).qc_data.qc.value()); //different timestamps, not a violation proof BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_2)); @@ -453,8 +507,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) mutable_variant_object valid_rule_1_proof = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, - real_chain_block_7_result, - real_chain_block_8_result.qc_data.qc.value()); + get_minimal_block_data(real_chain_block_7_result), + get_minimal_block_data(real_chain_block_8_result).qc_data.qc.value()); BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); @@ -485,7 +539,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Real chain has a QC on #9 carried by #10, but fake chain doesn't BOOST_TEST(!fake_chain_block_10_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_10_result.qc_data.qc.has_value()); - auto fake_chain_block_11_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_11_result = real_chain.produce_block(); @@ -496,16 +549,16 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 3u); - //Light client recorded the last QC on fake chain, which was delivered via block #11, and is over block #10. - //Block #10 claims a QC over block #8 (skipping #9). We provide fake block #10 and its QC. - //We also provide the real block #9 and a QC over it delivered via block #10, as well as a proof of inclusion of which is a proof of violation of rule #2. + //Light client recorded the last QC (over block #10) on the fake chain, which was delivered via block #11. + //Block #10 claims a QC over block #8. We provide fake block #10 and its QC, as well as the digests of the reversible blocks the light client recorded (block #8 and block #9). + //We also provide the real block #9 and a QC over it delivered via block #10. + //Since there is a time range conflict, and the real block #9 finality digest doesn't appear in the list of the digests committed to by the fake block QC, + //this is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, - fake_chain_block_10_result, - fake_chain_block_11_result.qc_data.qc.value(), - real_chain_block_9_result, - real_chain_block_10_result.qc_data.qc.value(), - real_chain_block_10_result, - 2, + light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], + light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), + get_minimal_block_data(real_chain_block_9_result), + get_minimal_block_data(real_chain_block_10_result).qc_data.qc.value(), reversible_blocks_digests); real_chain.node0.push_action("violation"_n, "rule2"_n, "violation"_n, valid_rule_2_proof_1); @@ -518,7 +571,45 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); - //BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_1)); + //we can now test the other possibility for rule #2, where the high proof is actually from the real chain + + //produce a block on the real chain without propagating votes to all nodes + real_chain.vote_propagation = {1,0,0}; + + auto fake_chain_block_13_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_13_result = real_chain.produce_block(); + + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + + //restore vote propagation on the real chain + real_chain.vote_propagation = {1,1,0}; + + auto fake_chain_block_14_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_14_result = real_chain.produce_block(); + + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + + std::vector block_ref_digests = { compute_block_ref_digest(real_chain_block_12_result), compute_block_ref_digest(real_chain_block_13_result)}; + + //Fake chain has a QC on #13 carried by #14, but real chain doesn't + BOOST_TEST(fake_chain_block_14_result.qc_data.qc.has_value()); + BOOST_TEST(!real_chain_block_14_result.qc_data.qc.has_value()); + + auto fake_chain_block_15_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_15_result = real_chain.produce_block(); + + //Things are back to normal, and we have a QC on both chains + BOOST_TEST(fake_chain_block_15_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_15_result.qc_data.qc.has_value()); + + mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, + get_minimal_block_data(real_chain_block_14_result), + get_minimal_block_data(real_chain_block_15_result).qc_data.qc.value(), + get_minimal_block_data(fake_chain_block_13_result), + get_minimal_block_data(fake_chain_block_14_result).qc_data.qc.value(), + block_ref_digests); + + BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2)); /* */ diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index cdf6914558..b2afc7e7ca 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -52,33 +52,23 @@ ACTION finality_violation::rule2( const finalizer_policy_input finalizer_polic const finality_proof low_proof, const std::vector reversible_blocks_digests){ - print("check QC\n"); check_qcs(finalizer_policy, high_proof, low_proof); block_timestamp high_proof_timestamp = high_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp low_proof_timestamp = low_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - print("check range conflict\n"); bool time_range_conflict = high_proof_last_claim_timestamp < low_proof_timestamp && low_proof_timestamp < high_proof_timestamp; check(time_range_conflict, "proofs must demonstrate a conflicting time range"); - print("check merkle root\n"); check(get_merkle_root(reversible_blocks_digests) == high_proof.qc_block.level_3_commitments->reversible_blocks_mroot, "reversible_blocks_digests merkle root does not match reversible_blocks_mroot"); -/* check(std::holds_alternative(proof_of_inclusion.target), "must provide extended block data object"); + checksum256 computed_digest = block_finality_data_internal(low_proof.qc_block).finality_digest(); - extended_block_data target = std::get(proof_of_inclusion.target); - - block_timestamp target_timestamp = target.timestamp; - - check(high_proof_timestamp == target_timestamp, "timestamp of high_proof block of the target block of the proof of inclusion must be the same"); - - auto finality_digest_claimed = block_finality_data_internal(high_proof.qc_block).finality_digest(); - auto finality_digest_actual = block_finality_data_internal(target.finality_data).finality_digest() ; - - check(finality_digest_claimed != finality_digest_actual, "finality digests must be different for a finality violation proof to be valid");*/ + auto f_itr = std::find(reversible_blocks_digests.begin(), reversible_blocks_digests.end(), computed_digest); + + check(f_itr==reversible_blocks_digests.end(), "finality digest of low block exists in reversible_blocks_digests vector"); } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index bfd575286290b4a5f173a0e6460ff2c297f06167..0e8c553e58535562567ec7815bf3bac7918d3f58 100755 GIT binary patch delta 6321 zcmc&&X>?UpmcHlQHzd3SZU`hVj~Cv3FChseAt6A*km)i6!jv!w$drT($jdwc31|`} zXr#mn90r*riXft3?TdPuRX~Q3WsI9H|#c0afl-;c<>J5wM zrJj$N*vp5r5b-P@9}}vU*K8iLzN)dPqOPdAwoDn?PuP%FR9#Z13>CL|>OI3W%^s#` zVff?RZV$8Dhlew6XG1w>VIx#^_x~djM4-R39!5p@kv*+Z`UpVC2r@U5E zv@zNYZI+g&&DG{<3mm^-imqr`nn&DG-Ol^0Gur8|`_ykuMK?| z^9{r93{7+BisN0zENw8%E9$g2Av`f`4VxI~3R}mbX?>VOQxwNgoJ?mr*SY4^ra0b& zjCg98Q^bV7&XUFN!xxeA5xY2>B;JUa!ZO6|h)EceBhxSzM~+U+FtWVX5QYtPi*EBL zDT*dZx?*H$8AP@k&L+dTNy`w=MJ7S&AEj)tGu4@)+maQ<$f@^QO?j&+FBUtuLGL@x z>5NSi!LFQeL$@R=n!)O4hcexgz@1vWSncw_s%@@^u_qtZfN|5P8jSxsYC201x;uk; z#Y(paj%ju;4^MO%h;urBVV+}{!{~lX(Ne^9cN83U$DP>EIVXC6^P=SJk(@oz#Q9eA z0B6ML0nWE2=OxM6HkvpOjvnCr!ywMi`K0~Nl5>%VIPdcqu<*1eizSL1o|IAX4##H3 z+jy>_BrE3^;K-p0EWc#Y;zXLh6kHGM@sK;KAB5aoZ*x#Qwgfyg1Hbe>$JkZT5L3Yx zi;rW*u_eM1yNfLqdt#@sB=M`*Jl^@92p=;wC~3Cb^psdOW*#%du`#-Hx~>}S%x?tw zF}bPuq4WBCiZ)d&PK*}+FfK~{;ypzaBziYp_aDwjm5zhEthHfTfD>+Uv1otV)ay>pGxsRoN z5KAXmdL`=y1e+oEUD7(sHATx2tCR2a9rLhQH{Jz>&y1Zx4!ALPkORh!`#(59vs_}@ z|L%i(|6k5u!v5mnTgESD4dPehzq5{V89yrGo$*QHo0Q8Dqg)ht)o)yb^NmYD0M6zX zzf1kCT`nE7iR-CvCs~YC?QT}hO|qtloamf{qs95OEOw9hDlLZo+*whsY^dZ3yo>u( z(#dr-o$tj6r3G0v>|XJDRvB9*VzM*YYEhg$!nMW}=w^cio?%kp@$CDc`Bruw#_SyL zhBYIS&JF-Ynh@kXA_R{Sxzp-ak*FRnF`iG4>e}HAi%s~4^6D8D{x~Pj z4NXB__)#@l&|s+fU4oD)6-%Uoj7@q}pOv2H zu}!6xExuUjgz#M(fpT~e9q0Q+6IiNfD~!UJSCGn}{E`A4_Ojt6o$pcH8Sc&**$x`?#Z|P{1 z_VT4!7_Tn{zL!SGS+*QKB4=4haDmQ@cJ2pGmo0mdeH*acbDL27!-`b4NsM3VWZ7cQ z%0;ku|H>}5S;XIaGI@&*1j8lFhaLbfVLF3L`bc!yavhf@(c$dx@6BRwh$*Y)vvSd~ zssZNSS~UrNNm}j3n7=yNRSMTyjGIUoRV($`v~@_EG8ZCMCc0J|P(5lbMe6I-G)Am3 zoJC#^0mMH@w^00nT{HHzYZ6ltHT=S6xbYR*IRV*CF(aHyk_0=U0dyGn)*3>CsUvHx zCjkz(0Ix8nU(XZ0in{PLx`KL<*0DA&lC;sbT|uU*y+I~Ee!iCQao5(MRIgiaMf|jK z@xi(`;fn)B6Go-d9u&~ADh{JTg_Ek7)32q9e<`W~*OKCU;Hy)`G>AfV{vLvnYRt6vYUEL6~yheK?S)8CfexSve?461bLMTuQ`_+s>U;n91SCQ@3ep_ zztJtpx-CDHp`|y@zbkJVD>>;3*R6!-%Urx)7H_)cGH7(Z59AZawl={nSGX8a9y1?A zJAQq@$uKLSURz-v2kzDRShaA6ZXK%zLG2ZV;I*S;hOz`^fT)3pnny&4Iz?M~MB5*v zF{V2%bDxcX(WhOW7&TC4g*3MlSSNGa@if|dvw|Gk9t79Hpak9#l+Iu7J;C|jXAhoq zUm--u9&;*%d}AO*GP=!7SCcwm1+B=0O%UIfCj8|{p&%ttpkxV3l5vtAk*RN!)xc-Z zqjQlBNHRFUivx^RFL7@$JzwVDA@uCwUWZE9u#qzzJAl0);?{@sdC`WPT~3r>T9jMq zK|4AMLlF-^1VzMW1AIcb!$F9KRs{UquX_WIIwG%Zt8vpVi@H{h{4%4MDSll(hmyS_ zpREuj6%jaWbH)7G-wL2<(+^gz57J9rEo~t?K)n_8YXt(6k zu{EE#*x+JOB4)$mY-8ZuhF2K#i>i&$;gtaKr6E*$t|4iunp6djY+8!W|@3gm$rZ>wt9f4v)UUedD(}?k9;WFUKk^;9f}H?FI^5o zT1^sb6-6}@X;rTsBVMWT^i6;4o6aLjRqYIV^wb(@4>1?jyAik|nYNwL+$rZBa_*M% z9dfSA`NJZn&KvR*h=GV+RBIQ7bkzZ-a)|=jq@t@5I~&{)e>LDAHBi7S8fC!s zO=d(d%YXxcy#X)WBm?f)GI+VCKj721jAk`r)s_mF`137)J?z7qleJRWs%Zo&Y@(Hl zg663ZeYE)=RzbLS;Bh?!UA+jQ-U-fHwl#yjBp%yZ$7;n7TV>$$w{@@!fp@o^WH4~k14nT4 zne9KWYg}%#n2IE$1e1wH7MX>mH?b_5&&%nX2K6Sx{6<|pvN0Cw@LP)#Yg=QOQ#{z} z!q=N47^B4Bw5}v9gtfJ@3!O7f_SH;I&lOq4;HIJCN+k+PM_WE`+S8xzE=EG! z+)d3u@Sa55V$ktUIBn10f$-xV9gfq*)$(Ra7N-dqcs`xGC0<|6OkZn=~tJDXk{F04IUigARtRQV*@PZ$*{C#epIj zYwruitA}Q@t9uS7vu$Gh;bQ37eKbY9emK%~KXpk1;j>m6{W4#G{Q(huq!sK(j#Od{ zI@&2k=VSb*qe-ko40$pE2{!S`I@TsmJed^?3oXVrB7JaV=3--_TP2%SM0)L zNvvHwaO@kLx3PN+o%i5V^frj56rX?wH>Gf0?`cK&({b!UG41I!fWv{O+rjQUUWBpv zc=AXHai9}zseDt`QWVFJOsqW@4?}mHtA>*bpP5N1IW-U~UU^1dBvJK~h3iBi+bAke zOk#_golN9NC-&YuIVe-dYf7p%Hnr$w8#k0UHtDq$dR6TfeSKAJ zX{BD?ys@#VQQuf2>Mw?if{Q`no6Ad^Y8#Y|HJc$-rZ?2qHU$n`)R|i`PQIr&UQ?Tt h^(Bqv>67&Gn$p^`@-n@tr7rOP&t71b8{-t^KLHjd8HNA= delta 6017 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 From f408f9f600e4c34ab8e360bc74ed172e46aa1a14 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 7 Aug 2024 12:11:07 -0600 Subject: [PATCH 35/75] Clean up --- unittests/finality_proof.hpp | 28 ------------ unittests/svnn_finality_violation_tests.cpp | 45 ++++--------------- .../finality_violation/finality_violation.cpp | 18 -------- 3 files changed, 8 insertions(+), 83 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 8ad6aa72d9..b5ab2c8706 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -37,34 +37,6 @@ namespace finality_proof { }; - struct ibc_block_data_tt { - signed_block block{}; - qc_data_t qc_data{}; - action_trace onblock_trace{}; - finality_data_t finality_data{}; - finalizer_policy active_finalizer_policy{}; - finalizer_policy last_pending_finalizer_policy{}; - finalizer_policy last_proposed_finalizer_policy{}; - digest_type action_mroot{}; //this is the real action_mroot, as returned from finality_data - digest_type base_digest{}; - block_timestamp_type last_pending_finalizer_policy_start_timestamp{}; - digest_type finality_digest{}; - level_3_commitments_t level_3_commitments{}; - level_2_commitments_t level_2_commitments{}; - digest_type finality_leaf{}; - digest_type finality_root{}; - block_timestamp_type parent_timestamp{}; - - digest_type level_3_commitments_digest() const { - return fc::sha256::hash(level_3_commitments); - } - - digest_type level_2_commitments_digest() const { - return fc::sha256::hash(level_2_commitments); - } - - }; - static std::string bitset_to_input_string(const boost::dynamic_bitset& bitset) { static const char* hexchar = "0123456789abcdef"; diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 7d055a8bfb..c041d25f13 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -383,11 +383,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) minimal_block_data b = reversible_blocks[i]; - std::cout << "b.block_num " << b.block_num << "\n"; - std::cout << "b.timestamp " << b.timestamp << "\n"; - std::cout << "b.finality_digest " << b.finality_digest << "\n"; - std::cout << "b.parent_timestamp " << b.parent_timestamp << "\n"; - digest_type digest = compute_block_ref_digest(b); block_ref_digests.push_back(digest); @@ -589,19 +584,24 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + //compute reversible digests on block #12 and #13 on the real chain std::vector block_ref_digests = { compute_block_ref_digest(real_chain_block_12_result), compute_block_ref_digest(real_chain_block_13_result)}; //Fake chain has a QC on #13 carried by #14, but real chain doesn't BOOST_TEST(fake_chain_block_14_result.qc_data.qc.has_value()); BOOST_TEST(!real_chain_block_14_result.qc_data.qc.has_value()); - auto fake_chain_block_15_result = light_client_data.scan_block(fake_chain.produce_block()); + //fake chain stops producing, but real chain continues auto real_chain_block_15_result = real_chain.produce_block(); - //Things are back to normal, and we have a QC on both chains - BOOST_TEST(fake_chain_block_15_result.qc_data.qc.has_value()); + //Things are back to normal on the real chain, and we have a QC BOOST_TEST(real_chain_block_15_result.qc_data.qc.has_value()); + //We discovered a QC (over block #14) on the real chain, which was delivered via block #15. + //Last QC recorded on the fake chain was over block #13, and was delivered by block #14 + //We provide the real block #14 and its QC, the reversible digests at that time (block #12 and #13), as well as the fake block #13 and its QC. + //Since there is a time range conflict, and the fake block #13 finality digest doesn't appear in the list of the digests committed to by the real block QC, + //this is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, get_minimal_block_data(real_chain_block_14_result), get_minimal_block_data(real_chain_block_15_result).qc_data.qc.value(), @@ -611,35 +611,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2)); -/* */ - - -/* - real_chain.vote_propagation = {1,0,0}; - - auto fake_chain_block_12_result = light_client_data.scan_block(fake_chain.produce_block()); - auto real_chain_block_12_result = real_chain.produce_block(); - - BOOST_TEST(fake_chain_block_12_result.qc_data.qc.has_value()); - BOOST_TEST(real_chain_block_12_result.qc_data.qc.has_value()); - - real_chain.vote_propagation = {1,0,1}; - - auto fake_chain_block_13_result = light_client_data.scan_block(fake_chain.produce_block()); - auto real_chain_block_13_result = real_chain.produce_block(); - - BOOST_TEST(fake_chain_block_13_result.qc_data.qc.has_value()); - BOOST_TEST(!real_chain_block_13_result.qc_data.qc.has_value()); - - mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_gt_proof( light_client_data.active_finalizer_policy, - light_client_data.high_qc_block, - light_client_data.high_qc, - real_chain_block_9_result, - real_chain_block_10_result.qc_data.qc.value()); - - //greater than rule - BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2));*/ - } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index b2afc7e7ca..8f753e9e9b 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -29,24 +29,6 @@ ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, } - -//Proof_1 is presumably from a fake, hidden chain, while proof_2 is presumably from the real, discoverable chain -//The block referenced by the proof of inclusion must be proven as a final ancestor of proof_2, while also be proven to conflict with proof_1 -/*ACTION finality_violation::rule2a( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ - - block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; - block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; - block_timestamp last_claim_timestamp_1 = proof_1.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - block_timestamp last_claim_timestamp_2 = proof_2.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - - bool lessThanRule = last_claim_timestamp_1 < timestamp_2 && timestamp_2 < timestamp_1; - - check(lessThanRule, "proofs must demonstrate a conflicting time range"); - - check_rule_2_qcs(finalizer_policy, proof_1, proof_2); - -}*/ - ACTION finality_violation::rule2( const finalizer_policy_input finalizer_policy, const finality_proof high_proof, const finality_proof low_proof, From 81bf80bd06ce3c5d2f8396276fe42aaa559a1c57 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 04:19:41 -0600 Subject: [PATCH 36/75] Renamed minimal_block_data to finality_block_data --- unittests/svnn_finality_violation_tests.cpp | 50 ++++++++++----------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index c041d25f13..963e7fe248 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -21,7 +21,7 @@ using namespace finality_proof; using mvo = mutable_variant_object; -struct minimal_block_data { +struct finality_block_data { uint32_t block_num{0}; block_timestamp_type timestamp{}; @@ -35,9 +35,9 @@ struct minimal_block_data { }; -minimal_block_data get_minimal_block_data(const ibc_block_data_t block_result){ +finality_block_data get_finality_block_data(const ibc_block_data_t block_result){ - return minimal_block_data { + return finality_block_data { .block_num = block_result.block->block_num(), .timestamp = block_result.block->timestamp, .parent_timestamp = block_result.parent_timestamp, @@ -51,9 +51,9 @@ minimal_block_data get_minimal_block_data(const ibc_block_data_t block_result){ } mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, - const minimal_block_data fake_qc_block, + const finality_block_data fake_qc_block, const qc_t fake_qc, - const minimal_block_data real_qc_block, + const finality_block_data real_qc_block, const qc_t real_qc){ return mvo() @@ -96,9 +96,9 @@ mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, } mvo prepare_rule_2_proof( const finalizer_policy active_finalizer_policy, - const minimal_block_data high_qc_block, + const finality_block_data high_qc_block, const qc_t high_qc, - const minimal_block_data low_qc_block, + const finality_block_data low_qc_block, const qc_t low_qc, const std::vector digests){ @@ -178,7 +178,7 @@ digest_type compute_block_ref_digest(const ibc_block_data_t b){ } -digest_type compute_block_ref_digest(const minimal_block_data b){ +digest_type compute_block_ref_digest(const finality_block_data b){ block_ref_digest_data data = { .block_num = b.block_num, @@ -302,14 +302,14 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //It only operates in optimistic mode, which is sufficient for finality violation proofs testing purposes //store the reversible blocks - std::vector reversible_blocks; + std::vector reversible_blocks; //store the last block over which we have a QC, as well as said QC - minimal_block_data high_qc_block; + finality_block_data high_qc_block; qc_t high_qc; //store the last final block, as wall as the first QC over it. The last_final_qc and high_qc together constitute the 2-chains required for finality progress - minimal_block_data last_final_block; + finality_block_data last_final_block; qc_t last_final_qc; //store all policies sunset data @@ -321,7 +321,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //observe a stream of blocks as they are received, and store minimal data required to construct finality violation proofs in the future ibc_block_data_t scan_block(const ibc_block_data_t& block_result){ - minimal_block_data block_data{ + finality_block_data block_data{ .block_num = block_result.block->block_num(), .timestamp = block_result.block->timestamp, .parent_timestamp = block_result.parent_timestamp, @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) for (int i = 0 ; i < reversible_blocks.size() - 1; i++){ - minimal_block_data b = reversible_blocks[i]; + finality_block_data b = reversible_blocks[i]; digest_type digest = compute_block_ref_digest(b); @@ -457,16 +457,16 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) mutable_variant_object invalid_rule_1_proof_1 = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, - get_minimal_block_data(real_chain_block_3_result), - get_minimal_block_data(real_chain_block_4_result).qc_data.qc.value()); //same finality digest, not a violation proof + get_finality_block_data(real_chain_block_3_result), + get_finality_block_data(real_chain_block_4_result).qc_data.qc.value()); //same finality digest, not a violation proof BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_1)); mutable_variant_object invalid_rule_1_proof_2 = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, - get_minimal_block_data(real_chain_block_2_result), - get_minimal_block_data(real_chain_block_3_result).qc_data.qc.value()); //different timestamps, not a violation proof + get_finality_block_data(real_chain_block_2_result), + get_finality_block_data(real_chain_block_3_result).qc_data.qc.value()); //different timestamps, not a violation proof BOOST_CHECK(shouldFail(real_chain, "rule1"_n, invalid_rule_1_proof_2)); @@ -502,8 +502,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) mutable_variant_object valid_rule_1_proof = prepare_rule_1_proof( light_client_data.active_finalizer_policy, light_client_data.high_qc_block, light_client_data.high_qc, - get_minimal_block_data(real_chain_block_7_result), - get_minimal_block_data(real_chain_block_8_result).qc_data.qc.value()); + get_finality_block_data(real_chain_block_7_result), + get_finality_block_data(real_chain_block_8_result).qc_data.qc.value()); BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); @@ -552,8 +552,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), - get_minimal_block_data(real_chain_block_9_result), - get_minimal_block_data(real_chain_block_10_result).qc_data.qc.value(), + get_finality_block_data(real_chain_block_9_result), + get_finality_block_data(real_chain_block_10_result).qc_data.qc.value(), reversible_blocks_digests); real_chain.node0.push_action("violation"_n, "rule2"_n, "violation"_n, valid_rule_2_proof_1); @@ -603,10 +603,10 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Since there is a time range conflict, and the fake block #13 finality digest doesn't appear in the list of the digests committed to by the real block QC, //this is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, - get_minimal_block_data(real_chain_block_14_result), - get_minimal_block_data(real_chain_block_15_result).qc_data.qc.value(), - get_minimal_block_data(fake_chain_block_13_result), - get_minimal_block_data(fake_chain_block_14_result).qc_data.qc.value(), + get_finality_block_data(real_chain_block_14_result), + get_finality_block_data(real_chain_block_15_result).qc_data.qc.value(), + get_finality_block_data(fake_chain_block_13_result), + get_finality_block_data(fake_chain_block_14_result).qc_data.qc.value(), block_ref_digests); BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2)); From 18deabb65790eb53a2ded92ae1592d437286f16c Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 04:56:15 -0600 Subject: [PATCH 37/75] Adjusted order of events to more closely reflect the light client behavior --- unittests/svnn_finality_violation_tests.cpp | 31 +++++++++++++------ .../finality_violation/finality_violation.cpp | 8 +++-- .../finality_violation/finality_violation.hpp | 12 ++++--- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 963e7fe248..965ce4b376 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -524,13 +524,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 2u); - //get the reversible blocks for block #10 (should be only one digest, block ref data of #9) - std::vector reversible_blocks_digests = light_client_data.get_reversible_blocks_digests(); - - BOOST_TEST(reversible_blocks_digests.size() == 2u); - BOOST_TEST(reversible_blocks_digests[0] == compute_block_ref_digest(fake_chain_block_8_result)); - BOOST_TEST(reversible_blocks_digests[1] == compute_block_ref_digest(fake_chain_block_9_result)); - //Real chain has a QC on #9 carried by #10, but fake chain doesn't BOOST_TEST(!fake_chain_block_10_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_10_result.qc_data.qc.has_value()); @@ -544,10 +537,20 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 3u); + //get the reversible blocks for block #10 (should be two digests, block ref data of #8 and #9) + std::vector reversible_blocks_digests = light_client_data.get_reversible_blocks_digests(); + + //since we're preparing the proof for block #10, we must discard block #11 + reversible_blocks_digests.pop_back(); + + BOOST_TEST(reversible_blocks_digests.size() == 2u); + BOOST_TEST(reversible_blocks_digests[0] == compute_block_ref_digest(fake_chain_block_8_result)); + BOOST_TEST(reversible_blocks_digests[1] == compute_block_ref_digest(fake_chain_block_9_result)); + //Light client recorded the last QC (over block #10) on the fake chain, which was delivered via block #11. - //Block #10 claims a QC over block #8. We provide fake block #10 and its QC, as well as the digests of the reversible blocks the light client recorded (block #8 and block #9). - //We also provide the real block #9 and a QC over it delivered via block #10. - //Since there is a time range conflict, and the real block #9 finality digest doesn't appear in the list of the digests committed to by the fake block QC, + //Block #10 claims a QC over block #8. We provide fake block #10 and the QC over it, as well as the digests of the reversible blocks the light client recorded (block #8 and block #9). + //We also provide the real block #9 and a QC over it, delivered via block #10. + //Since there is a time range conflict, and since the real block #9 finality digest doesn't appear in the list of reversible blocks digests committed to by the fake block QC, //this is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], @@ -611,6 +614,14 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2)); + //Fake chain resume production, catches up with real chain + auto fake_chain_block_15_result = light_client_data.scan_block(fake_chain.produce_block()); + + //Caught up + auto fake_chain_block_16_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_16_result = real_chain.produce_block(); + + } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 8f753e9e9b..b400edf90f 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -49,13 +49,17 @@ ACTION finality_violation::rule2( const finalizer_policy_input finalizer_polic checksum256 computed_digest = block_finality_data_internal(low_proof.qc_block).finality_digest(); auto f_itr = std::find(reversible_blocks_digests.begin(), reversible_blocks_digests.end(), computed_digest); - + check(f_itr==reversible_blocks_digests.end(), "finality digest of low block exists in reversible_blocks_digests vector"); } -ACTION finality_violation::rule3(){ +ACTION finality_violation::rule3( const finalizer_policy_input finalizer_policy, + const finality_proof high_proof, + const finality_proof low_proof, + const std::vector reversible_blocks_digests){ + check_qcs(finalizer_policy, high_proof, low_proof); } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 4687ffc78c..c36c98c807 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -13,14 +13,16 @@ CONTRACT finality_violation : public contract { public: using contract::contract; - ACTION rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); - //ACTION rule2a(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); - ACTION rule2(const finalizer_policy_input finalizer_policy, + ACTION rule1( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); + ACTION rule2( const finalizer_policy_input finalizer_policy, + const finality_proof high_proof, + const finality_proof low_proof, + const std::vector reversible_blocks_digests); + ACTION rule3( const finalizer_policy_input finalizer_policy, const finality_proof high_proof, const finality_proof low_proof, const std::vector reversible_blocks_digests); - ACTION rule3(); ACTION testmroot(const checksum256 root, const std::vector reversible_blocks_digests); - + }; \ No newline at end of file From 9c474277652146ba1e67b71eee492e5b14ccba93 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 04:58:58 -0600 Subject: [PATCH 38/75] Obtain reference to fake chain blocks via the list of reversible blocks stored by light client for better encapsulation --- unittests/svnn_finality_violation_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 965ce4b376..b11ea1e8f5 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -608,8 +608,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, get_finality_block_data(real_chain_block_14_result), get_finality_block_data(real_chain_block_15_result).qc_data.qc.value(), - get_finality_block_data(fake_chain_block_13_result), - get_finality_block_data(fake_chain_block_14_result).qc_data.qc.value(), + light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], + light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), block_ref_digests); BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2)); From 26e809b497f13030c9de438223d43d85134aaa4f Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 05:51:42 -0600 Subject: [PATCH 39/75] Added rule #3 to smart contract --- .../finality_violation/finality_violation.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index b400edf90f..69b17b098c 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -61,6 +61,21 @@ ACTION finality_violation::rule3( const finalizer_policy_input finalizer_polic check_qcs(finalizer_policy, high_proof, low_proof); + block_timestamp low_proof_timestamp = low_proof.qc_block.level_3_commitments.value().timestamp; + block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + + bool time_range_conflict = low_proof_timestamp < high_proof_last_claim_timestamp; + + check(time_range_conflict, "low proof timestamp must be less than high proof last claimed QC timestamp"); + + check(get_merkle_root(reversible_blocks_digests) == high_proof.qc_block.level_3_commitments->reversible_blocks_mroot, "reversible_blocks_digests merkle root does not match reversible_blocks_mroot"); + + checksum256 computed_digest = block_finality_data_internal(low_proof.qc_block).finality_digest(); + + auto f_itr = std::find(reversible_blocks_digests.begin(), reversible_blocks_digests.end(), computed_digest); + + check(f_itr==reversible_blocks_digests.end(), "finality digest of low block exists in reversible_blocks_digests vector"); + } ACTION finality_violation::testmroot(const checksum256 root, const std::vector reversible_blocks_digests){ From b1761b2beea1e7ca7e7950df64dfea964ed1a9f6 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 06:15:24 -0600 Subject: [PATCH 40/75] Added proof of rule #3 finality violation test --- unittests/svnn_finality_violation_tests.cpp | 56 ++++++++++++++++++- .../finality_violation/finality_violation.cpp | 4 +- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index b11ea1e8f5..49102d9b2a 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -95,7 +95,7 @@ mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, } -mvo prepare_rule_2_proof( const finalizer_policy active_finalizer_policy, +mvo prepare_rule_2_3_proof( const finalizer_policy active_finalizer_policy, const finality_block_data high_qc_block, const qc_t high_qc, const finality_block_data low_qc_block, @@ -552,7 +552,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //We also provide the real block #9 and a QC over it, delivered via block #10. //Since there is a time range conflict, and since the real block #9 finality digest doesn't appear in the list of reversible blocks digests committed to by the fake block QC, //this is a proof of violation of rule #2. - mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, + mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), get_finality_block_data(real_chain_block_9_result), @@ -605,7 +605,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //We provide the real block #14 and its QC, the reversible digests at that time (block #12 and #13), as well as the fake block #13 and its QC. //Since there is a time range conflict, and the fake block #13 finality digest doesn't appear in the list of the digests committed to by the real block QC, //this is a proof of violation of rule #2. - mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_proof( light_client_data.active_finalizer_policy, + mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, get_finality_block_data(real_chain_block_14_result), get_finality_block_data(real_chain_block_15_result).qc_data.qc.value(), light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], @@ -621,6 +621,56 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_16_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_16_result = real_chain.produce_block(); + BOOST_TEST(fake_chain_block_16_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_16_result.qc_data.qc.has_value()); + + //We once again disable vote propagation on the real chain + real_chain.vote_propagation = {1,0,0}; + + //Produce a few more blocks on both chains + auto fake_chain_block_17_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_17_result = real_chain.produce_block(); + + BOOST_TEST(fake_chain_block_17_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_17_result.qc_data.qc.has_value()); + + auto fake_chain_block_18_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_18_result = real_chain.produce_block(); + + BOOST_TEST(fake_chain_block_18_result.qc_data.qc.has_value()); + BOOST_TEST(!real_chain_block_18_result.qc_data.qc.has_value()); + + //restore vote propagation on the real chain + real_chain.vote_propagation = {1,1,0}; + + auto fake_chain_block_19_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_19_result = real_chain.produce_block(); + + BOOST_TEST(fake_chain_block_19_result.qc_data.qc.has_value()); + BOOST_TEST(!real_chain_block_19_result.qc_data.qc.has_value()); + + //At this point, we can verify that the time range of the last fake chain block is fully encapsulated within the time range of the last real chain block + BOOST_TEST(real_chain_block_19_result.level_3_commitments.latest_qc_claim_block_num == real_chain_block_16_result.block->block_num()); + BOOST_TEST(fake_chain_block_18_result.level_3_commitments.latest_qc_claim_block_num == fake_chain_block_17_result.block->block_num()); + + //stop producing on fake chain, produce one more block on the real chain + auto real_chain_block_20_result = real_chain.produce_block(); + + BOOST_TEST(real_chain_block_20_result.qc_data.qc.has_value()); + + //We can now produce a proof of finality violation demonstrating that finalizers were locked on #18 on the fake chain, while also voting on a conflicting block #19 + //on the real chain which is not a descendant of #18, where the time range committed to by #18 is fully encapsulated within the time range committed to by #19 + mutable_variant_object valid_rule_3_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, + get_finality_block_data(real_chain_block_19_result), + get_finality_block_data(real_chain_block_20_result).qc_data.qc.value(), + light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], + light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), + block_ref_digests); + + BOOST_CHECK(shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1)); + + + } FC_LOG_AND_RETHROW() } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 69b17b098c..3a27368f87 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -62,9 +62,11 @@ ACTION finality_violation::rule3( const finalizer_policy_input finalizer_polic check_qcs(finalizer_policy, high_proof, low_proof); block_timestamp low_proof_timestamp = low_proof.qc_block.level_3_commitments.value().timestamp; + block_timestamp high_proof_timestamp = high_proof.qc_block.level_3_commitments.value().timestamp; + block_timestamp low_proof_last_claim_timestamp = low_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - bool time_range_conflict = low_proof_timestamp < high_proof_last_claim_timestamp; + bool time_range_conflict = high_proof_timestamp > low_proof_timestamp && high_proof_last_claim_timestamp < low_proof_timestamp; // check(time_range_conflict, "low proof timestamp must be less than high proof last claimed QC timestamp"); From e1c8e4192835d784c941989f41bb2270e25dc30e Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 06:26:35 -0600 Subject: [PATCH 41/75] Added comments to smart contract, recompiled --- unittests/svnn_finality_violation_tests.cpp | 9 +++++ .../finality_violation/finality_violation.abi | 19 +++++++++- .../finality_violation/finality_violation.cpp | 33 ++++++++++++++++-- .../finality_violation.wasm | Bin 49449 -> 52605 bytes 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 49102d9b2a..b4cd2e25d5 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -669,6 +669,15 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_CHECK(shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1)); + //Resume production on fake chain + BOOST_TEST(fake_chain_block_20_result.qc_data.qc.has_value()); + + //Caught up + auto fake_chain_block_20_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_20_result = real_chain.produce_block(); + + BOOST_TEST(fake_chain_block_20_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_20_result.qc_data.qc.has_value()); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 6167e949a1..e2369d0b3b 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -186,7 +186,24 @@ { "name": "rule3", "base": "", - "fields": [] + "fields": [ + { + "name": "finalizer_policy", + "type": "finalizer_policy_input" + }, + { + "name": "high_proof", + "type": "finality_proof" + }, + { + "name": "low_proof", + "type": "finality_proof" + }, + { + "name": "reversible_blocks_digests", + "type": "checksum256[]" + } + ] }, { "name": "testmroot", diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 3a27368f87..553f493883 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -1,85 +1,114 @@ #include "finality_violation.hpp" +//Verify QCs presented as proof void check_qcs( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ + //Verify we have our level 3 commitments for both proofs check(proof_1.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); check(proof_2.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); + //Compute finality digests for both proofs checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); + //Verify QC signatures over the finality digests _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); } +//Rule #1 : Do not vote on different blocks with the same timestamp ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ + //Verify QCs check_qcs(finalizer_policy, proof_1, proof_2); + //Compare timestamps block_timestamp timestamp_1 = proof_1.qc_block.level_3_commitments.value().timestamp; block_timestamp timestamp_2 = proof_2.qc_block.level_3_commitments.value().timestamp; check(timestamp_1 == timestamp_2, "proofs must be over blocks that have the same timestamp"); + //Verify that finality digests are different checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); check(digest_1 != digest_2, "finality digests must be different"); + //Proof of rule #1 finality violation + } +//Rule #2 : Do not vote on a block that conflicts with the time interval of a strong vote ACTION finality_violation::rule2( const finalizer_policy_input finalizer_policy, const finality_proof high_proof, const finality_proof low_proof, const std::vector reversible_blocks_digests){ + //Verify QCs check_qcs(finalizer_policy, high_proof, low_proof); + //Compare timestamps block_timestamp high_proof_timestamp = high_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp low_proof_timestamp = low_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; + //A time range conflict has occured if the low proof timestamp is contained within the high proof time range bool time_range_conflict = high_proof_last_claim_timestamp < low_proof_timestamp && low_proof_timestamp < high_proof_timestamp; check(time_range_conflict, "proofs must demonstrate a conflicting time range"); + //Compute the merkle root of the reversible digests, and verify that it matches the commitment of the high proof check(get_merkle_root(reversible_blocks_digests) == high_proof.qc_block.level_3_commitments->reversible_blocks_mroot, "reversible_blocks_digests merkle root does not match reversible_blocks_mroot"); + //Compute the finality digest of the low proof checksum256 computed_digest = block_finality_data_internal(low_proof.qc_block).finality_digest(); + //Verify that the computed digest for the low proof doesn't appear in the list of reversible block digests committed to by the high proof auto f_itr = std::find(reversible_blocks_digests.begin(), reversible_blocks_digests.end(), computed_digest); check(f_itr==reversible_blocks_digests.end(), "finality digest of low block exists in reversible_blocks_digests vector"); + //Proof of rule #2 finality violation + } +//Rule #3 : Do not vote on a block that conflicts with another block on which you are locked ACTION finality_violation::rule3( const finalizer_policy_input finalizer_policy, const finality_proof high_proof, const finality_proof low_proof, const std::vector reversible_blocks_digests){ + //Verify QCs check_qcs(finalizer_policy, high_proof, low_proof); + //Compare timestamps block_timestamp low_proof_timestamp = low_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp high_proof_timestamp = high_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp low_proof_last_claim_timestamp = low_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - bool time_range_conflict = high_proof_timestamp > low_proof_timestamp && high_proof_last_claim_timestamp < low_proof_timestamp; // + //If the high proof timestamp is higher than the low proof timestamp, but the high proof last QC claim timestamp is lower than the low proof last QC claim, the lock was violated + bool lock_violation = high_proof_timestamp > low_proof_timestamp && high_proof_last_claim_timestamp < low_proof_timestamp; - check(time_range_conflict, "low proof timestamp must be less than high proof last claimed QC timestamp"); + check(lock_violation, "proofs must demonstrate a lock violation"); + //Compute the merkle root of the reversible digests, and verify that it matches the commitment of the high proof check(get_merkle_root(reversible_blocks_digests) == high_proof.qc_block.level_3_commitments->reversible_blocks_mroot, "reversible_blocks_digests merkle root does not match reversible_blocks_mroot"); + //Compute the finality digest of the low proof checksum256 computed_digest = block_finality_data_internal(low_proof.qc_block).finality_digest(); + //Verify that the computed digest for the low proof doesn't appear in the list of reversible block digests committed to by the high proof auto f_itr = std::find(reversible_blocks_digests.begin(), reversible_blocks_digests.end(), computed_digest); check(f_itr==reversible_blocks_digests.end(), "finality digest of low block exists in reversible_blocks_digests vector"); + //Proof of rule #3 finality violation + } +//For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation ACTION finality_violation::testmroot(const checksum256 root, const std::vector reversible_blocks_digests){ checksum256 c_root = get_merkle_root(reversible_blocks_digests); check(c_root == root, "invalid root"); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 0e8c553e58535562567ec7815bf3bac7918d3f58..e0f013f81f812ef421954b92250804d53227bf75 100755 GIT binary patch delta 1570 zcmcIjZ%|ZK6o2Qw-GwA{n`pEJv2x!o?y_t^hJe5# z2o-Tm8(Cl-4K)O}xnyI1Ku79>;}oSaYU50fGdUT~G<^utF*&M;hhZ`{cj^nN_&ZBo|<*tUk~-Aw|_oYTE=3$Yx#p40GXm*_vt?fnP2Z%^r61mvoOLu;w56>x+5;0;o5uFpjItb*$|i z`Yg-!d(TNmD>{?oamr%EtYo+T_+6$ut&%T)JdeR?EY2|F{?r_ba4A(ph_l=`LgTDA z?@s$P?6JXz>p_$ob?P zjpWY}n(AYkv7A3)|6G`_JMK06^>@ss@-ha;&^VNg2b~Fo#+>O;kAFCq5_Y(f@p+eE z9j^TF{({R)=x^7vB=airvYNo8NGU8v0TqnRAn<}9_6BiW5Vr)e62v?4ctxV6gWQNz zAJmLH2r^Y#Ac^4>xIj_bbB$w1kc6){r+ZvH@JISHQ-eJ09}8ep&l;dq8-=3 ziBXNyu!ZZI+5tS+*<2fx@o4eph0!i==3NwnlioPmJ>*@0i7ka;zO^NnP=zr2EwVWK zzK5jRWf8+s-c=Y<3vr@L3^_4L{86l8@D_G{a0#|?`<`JS15f<$rVMXmoG()xd(pR2 zsNKHBP>ZL08^y-lmx0AS^JBN0C=Y6~NFj7ddb=7?6l@ea)6}yV>M+n#Cmb?+HKBaH zJ45b8LRIvoffsxFQs`H|>2t9Lz-<3}vDAY9iMl%6epmeIu!GOr|0RP)e)hm2AlLcB z<#^?zmGmEHhRwLT-x6$1D<18C88p1zzXIBL&Y=z<`s>4@Ym7&#NwyxDfL7e_aT(#< zqtr(@_{pF5;M3SPIFIcD^gH7s3-~F9? z&b#N{a~_`O`|J3jNS-+CXyEr?A#YtbJHv&0IC*vkFY_kh&Rt^18Qzjzq{;LuUytwG zs~(Bu3s#AfKs>fA5B*nmC~{BV&jWXF=B{oor+LDr&E&`L=+T5OfJO{4$>761rW!Ik zg%`{_G`~)9q6Hm^vAARwF+H(Db7p!)=}b9JcRrR$;9WeC{0fV{ovcL1njaZmw;D-1 zQ~bdw6S~uOLj|2kD-_@cO-N4%a(|i@JQXUy1U|qZW;y5%lot|c zz>Kj(?D9ovpVe_PCpVzYxfN=#!enB19l7DRijEdbEkskOol17|Hf`1SWZlXTJ4R-weF#nYEj!12W2#|sUE1_ zGYD`6KleJoj+WY7_Pg5Jli;V?x_q)L3wTcgXC(1Fv5JvKAzQ~0E3ry~>=kl!oFs_G zcoG5O5@FJMo9rkrF_JBux)!4h;S#?ErItaK^y?N6`0=^ZKS3E<&Md)E`uN#qfa5sc zTCHL%zR;Gr%=~TJ{$(3c-yXwO6RnZBw>@WBr|ni%i$Wc0!AUovQ((fwa|Y}^SD>;y zhR~tsLqsPn>UdFrMmo{?wDQZv3r>cmU1qftyK;gi4xw9_DD3Dm(yVS5f!A^Ty<1R4 z-+6xon1k-^eZa#TxZAf)*;eJtQp|)e72d=--)qVuZy*h;`}NV)26jp8@k135jG7Wz zo2#}I+w(zxDxARE{WVI!OP6el`QlPTFg#Z=odYSM^*j$AsNpd_5YOIce!u}9EWCU+ z+6({Bz0$6_!L8t>&kTM{pq4Iucu{%T`GIJBHSj#@hO^i+ECkr#4x81cvf$0(JSfIw zx|7|_5!wPwo*z+KAj9uwIO6}FVe?0Y3@5L$robOQSy;obp?`EeX#za&iwWgd4x!AD zI2NLPn{{*&iNJ23eyx)Y5v?1WRHp6GIimu}tH*{Gyb@psrwaiaA-`{+hp+t^qAM Date: Thu, 8 Aug 2024 06:56:33 -0600 Subject: [PATCH 42/75] Fixed a few bugs, added additional rule #3 test where fake chain is ahead of real chain --- unittests/svnn_finality_violation_tests.cpp | 114 +++++++++++++++----- 1 file changed, 90 insertions(+), 24 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index b4cd2e25d5..034f3c6d4e 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -145,19 +145,24 @@ mvo prepare_rule_2_3_proof( const finalizer_policy active_finalizer_policy, bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_name rule, const mvo proof){ - bool last_action_failed = false; try {chain.node0.push_action("violation"_n, rule, "violation"_n, proof);} - catch (const eosio_assert_message_exception& e){last_action_failed = true;} + catch (const eosio_assert_message_exception& e){ + std::rethrow_exception(std::current_exception()); + } - return !last_action_failed; + return true; } bool shouldFail(const finality_proof::proof_test_cluster& chain, const account_name rule, const mvo proof){ bool last_action_failed = false; - try {chain.node0.push_action("violation"_n, rule, "violation"_n, proof);} - catch (const eosio_assert_message_exception& e){last_action_failed = true;} + try { + chain.node0.push_action("violation"_n, rule, "violation"_n, proof); + } + catch (const eosio_assert_message_exception& e){ + last_action_failed = true; + } return last_action_failed; @@ -538,14 +543,14 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 3u); //get the reversible blocks for block #10 (should be two digests, block ref data of #8 and #9) - std::vector reversible_blocks_digests = light_client_data.get_reversible_blocks_digests(); + std::vector block_10_reversible_blocks_digests = light_client_data.get_reversible_blocks_digests(); //since we're preparing the proof for block #10, we must discard block #11 - reversible_blocks_digests.pop_back(); + block_10_reversible_blocks_digests.pop_back(); - BOOST_TEST(reversible_blocks_digests.size() == 2u); - BOOST_TEST(reversible_blocks_digests[0] == compute_block_ref_digest(fake_chain_block_8_result)); - BOOST_TEST(reversible_blocks_digests[1] == compute_block_ref_digest(fake_chain_block_9_result)); + BOOST_TEST(block_10_reversible_blocks_digests.size() == 2u); + BOOST_TEST(block_10_reversible_blocks_digests[0] == compute_block_ref_digest(fake_chain_block_8_result)); + BOOST_TEST(block_10_reversible_blocks_digests[1] == compute_block_ref_digest(fake_chain_block_9_result)); //Light client recorded the last QC (over block #10) on the fake chain, which was delivered via block #11. //Block #10 claims a QC over block #8. We provide fake block #10 and the QC over it, as well as the digests of the reversible blocks the light client recorded (block #8 and block #9). @@ -557,7 +562,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), get_finality_block_data(real_chain_block_9_result), get_finality_block_data(real_chain_block_10_result).qc_data.qc.value(), - reversible_blocks_digests); + block_10_reversible_blocks_digests); real_chain.node0.push_action("violation"_n, "rule2"_n, "violation"_n, valid_rule_2_proof_1); @@ -580,7 +585,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); //restore vote propagation on the real chain - real_chain.vote_propagation = {1,1,0}; + real_chain.vote_propagation = {1,0,1}; auto fake_chain_block_14_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_14_result = real_chain.produce_block(); @@ -588,7 +593,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); //compute reversible digests on block #12 and #13 on the real chain - std::vector block_ref_digests = { compute_block_ref_digest(real_chain_block_12_result), compute_block_ref_digest(real_chain_block_13_result)}; + std::vector block_14_reversible_blocks_digests = { compute_block_ref_digest(real_chain_block_12_result), compute_block_ref_digest(real_chain_block_13_result)}; //Fake chain has a QC on #13 carried by #14, but real chain doesn't BOOST_TEST(fake_chain_block_14_result.qc_data.qc.has_value()); @@ -610,7 +615,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) get_finality_block_data(real_chain_block_15_result).qc_data.qc.value(), light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), - block_ref_digests); + block_14_reversible_blocks_digests); BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2)); @@ -641,19 +646,22 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(!real_chain_block_18_result.qc_data.qc.has_value()); //restore vote propagation on the real chain - real_chain.vote_propagation = {1,1,0}; + real_chain.vote_propagation = {1,0,1}; auto fake_chain_block_19_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_19_result = real_chain.produce_block(); + //compute reversible digests on block #16, #17 and #18 on the real chain + std::vector block_19_reversible_blocks_digests = { compute_block_ref_digest(real_chain_block_16_result), compute_block_ref_digest(real_chain_block_17_result), compute_block_ref_digest(real_chain_block_18_result)}; + BOOST_TEST(fake_chain_block_19_result.qc_data.qc.has_value()); BOOST_TEST(!real_chain_block_19_result.qc_data.qc.has_value()); - //At this point, we can verify that the time range of the last fake chain block is fully encapsulated within the time range of the last real chain block - BOOST_TEST(real_chain_block_19_result.level_3_commitments.latest_qc_claim_block_num == real_chain_block_16_result.block->block_num()); + //At this point, we can verify that the time range of the last fake chain block on which we have a QC is fully encapsulated within the time range of the last real chain block BOOST_TEST(fake_chain_block_18_result.level_3_commitments.latest_qc_claim_block_num == fake_chain_block_17_result.block->block_num()); - - //stop producing on fake chain, produce one more block on the real chain + BOOST_TEST(real_chain_block_19_result.level_3_commitments.latest_qc_claim_block_num == real_chain_block_16_result.block->block_num()); + + //stop producing on fake chain, produce one more block on the real chain to get a QC on previous block auto real_chain_block_20_result = real_chain.produce_block(); BOOST_TEST(real_chain_block_20_result.qc_data.qc.has_value()); @@ -665,20 +673,78 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) get_finality_block_data(real_chain_block_20_result).qc_data.qc.value(), light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), - block_ref_digests); + block_19_reversible_blocks_digests); BOOST_CHECK(shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1)); //Resume production on fake chain + auto fake_chain_block_20_result = light_client_data.scan_block(fake_chain.produce_block()); BOOST_TEST(fake_chain_block_20_result.qc_data.qc.has_value()); //Caught up - auto fake_chain_block_20_result = light_client_data.scan_block(fake_chain.produce_block()); - auto real_chain_block_20_result = real_chain.produce_block(); + auto fake_chain_block_21_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_21_result = real_chain.produce_block(); - BOOST_TEST(fake_chain_block_20_result.qc_data.qc.has_value()); - BOOST_TEST(real_chain_block_20_result.qc_data.qc.has_value()); + BOOST_TEST(fake_chain_block_21_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_21_result.qc_data.qc.has_value()); + + //We once again disable vote propagation on the fake chain + fake_chain.vote_propagation = {1,0,0}; + + //Produce a few more blocks on both chains + auto fake_chain_block_22_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_22_result = real_chain.produce_block(); + + BOOST_TEST(fake_chain_block_22_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_22_result.qc_data.qc.has_value()); + + auto fake_chain_block_23_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_23_result = real_chain.produce_block(); + + BOOST_TEST(!fake_chain_block_23_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_23_result.qc_data.qc.has_value()); + + //restore vote propagation on the fake chain + fake_chain.vote_propagation = {1,1,0}; + + auto fake_chain_block_24_result = light_client_data.scan_block(fake_chain.produce_block()); + auto real_chain_block_24_result = real_chain.produce_block(); + + BOOST_TEST(!fake_chain_block_24_result.qc_data.qc.has_value()); + BOOST_TEST(real_chain_block_24_result.qc_data.qc.has_value()); + + //At this point, we can verify that the time range of the last real chain block on which we have a QC is fully encapsulated within the time range of the last fake chain block + BOOST_TEST(fake_chain_block_24_result.level_3_commitments.latest_qc_claim_block_num == fake_chain_block_21_result.block->block_num()); + BOOST_TEST(real_chain_block_23_result.level_3_commitments.latest_qc_claim_block_num == real_chain_block_22_result.block->block_num()); + + //stop producing on real chain, produce one more block on the fake chain to get a QC on previous block + auto fake_chain_block_25_result = light_client_data.scan_block(fake_chain.produce_block()); + + BOOST_TEST(fake_chain_block_25_result.qc_data.qc.has_value()); + + BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 4u); + + //get the reversible blocks for block #24 (should be three digests, block ref data of #21, #22 and #23) + std::vector block_24_reversible_blocks_digests = light_client_data.get_reversible_blocks_digests(); + + //since we're preparing the proof for block #24, we must discard block #25 + block_24_reversible_blocks_digests.pop_back(); + + BOOST_TEST(block_24_reversible_blocks_digests.size() == 3u); + BOOST_TEST(block_24_reversible_blocks_digests[0] == compute_block_ref_digest(fake_chain_block_21_result)); + BOOST_TEST(block_24_reversible_blocks_digests[1] == compute_block_ref_digest(fake_chain_block_22_result)); + BOOST_TEST(block_24_reversible_blocks_digests[2] == compute_block_ref_digest(fake_chain_block_23_result)); + + //We can now produce a proof of finality violation demonstrating that finalizers were locked on #23 on the real chain, while also voting on a conflicting block #24 + //on the fake chain which is not a descendant of #23, where the time range committed to by #23 is fully encapsulated within the time range committed to by #24 + mutable_variant_object valid_rule_3_proof_2 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, + light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], + light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), + get_finality_block_data(real_chain_block_23_result), + get_finality_block_data(real_chain_block_24_result).qc_data.qc.value(), + block_24_reversible_blocks_digests); + BOOST_CHECK(shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_2)); } FC_LOG_AND_RETHROW() } From 411adc2f12d0bf453686a7ca827bb725af5c23a1 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 07:03:25 -0600 Subject: [PATCH 43/75] Updated finality violation contract actions to return guilty vs innocent finalizers upon successful proof verifcation --- .../finality_violation/finality_violation.abi | 29 +++++++++++++++++- .../finality_violation/finality_violation.cpp | 27 ++++++++++++++-- .../finality_violation/finality_violation.hpp | 11 +++++-- .../finality_violation.wasm | Bin 52605 -> 55591 bytes 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index e2369d0b3b..afad8c464e 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -125,6 +125,20 @@ } ] }, + { + "name": "pair_string_string", + "base": "", + "fields": [ + { + "name": "first", + "type": "string" + }, + { + "name": "second", + "type": "string" + } + ] + }, { "name": "quorum_certificate_input", "base": "", @@ -245,5 +259,18 @@ "tables": [], "ricardian_clauses": [], "variants": [], - "action_results": [] + "action_results": [ + { + "name": "rule1", + "result_type": "pair_string_string" + }, + { + "name": "rule2", + "result_type": "pair_string_string" + }, + { + "name": "rule3", + "result_type": "pair_string_string" + } + ] } \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 553f493883..d4cb637466 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -18,7 +18,7 @@ void check_qcs( const finalizer_policy_input finalizer_policy, const finality_pr } //Rule #1 : Do not vote on different blocks with the same timestamp -ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ +std::pair finality_violation::rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ //Verify QCs check_qcs(finalizer_policy, proof_1, proof_2); @@ -37,10 +37,17 @@ ACTION finality_violation::rule1(const finalizer_policy_input finalizer_policy, //Proof of rule #1 finality violation + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), proof_1.active_policy_qc.finalizers); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), proof_2.active_policy_qc.finalizers); + + auto result = bitset::compare(proof_1_bitset, proof_2_bitset); + + return {result.first.to_string(), result.second.to_string()}; + } //Rule #2 : Do not vote on a block that conflicts with the time interval of a strong vote -ACTION finality_violation::rule2( const finalizer_policy_input finalizer_policy, +std::pair finality_violation::rule2( const finalizer_policy_input finalizer_policy, const finality_proof high_proof, const finality_proof low_proof, const std::vector reversible_blocks_digests){ @@ -71,10 +78,17 @@ ACTION finality_violation::rule2( const finalizer_policy_input finalizer_polic //Proof of rule #2 finality violation + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.finalizers); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.finalizers); + + auto result = bitset::compare(proof_1_bitset, proof_2_bitset); + + return {result.first.to_string(), result.second.to_string()}; + } //Rule #3 : Do not vote on a block that conflicts with another block on which you are locked -ACTION finality_violation::rule3( const finalizer_policy_input finalizer_policy, +std::pair finality_violation::rule3( const finalizer_policy_input finalizer_policy, const finality_proof high_proof, const finality_proof low_proof, const std::vector reversible_blocks_digests){ @@ -106,6 +120,13 @@ ACTION finality_violation::rule3( const finalizer_policy_input finalizer_polic //Proof of rule #3 finality violation + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.finalizers); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.finalizers); + + auto result = bitset::compare(proof_1_bitset, proof_2_bitset); + + return {result.first.to_string(), result.second.to_string()}; + } //For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index c36c98c807..bfa1b7f29a 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -13,12 +13,17 @@ CONTRACT finality_violation : public contract { public: using contract::contract; - ACTION rule1( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); - ACTION rule2( const finalizer_policy_input finalizer_policy, + [[eosio::action]] + std::pair rule1( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); + + [[eosio::action]] + std::pair rule2( const finalizer_policy_input finalizer_policy, const finality_proof high_proof, const finality_proof low_proof, const std::vector reversible_blocks_digests); - ACTION rule3( const finalizer_policy_input finalizer_policy, + + [[eosio::action]] + std::pair rule3( const finalizer_policy_input finalizer_policy, const finality_proof high_proof, const finality_proof low_proof, const std::vector reversible_blocks_digests); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index e0f013f81f812ef421954b92250804d53227bf75..686f09e65f3f83b118beb6d6dd6ebdba6048431b 100755 GIT binary patch delta 14378 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 From a62ab3f9a426078f2307b890318be2b95dcc27a3 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 07:22:24 -0600 Subject: [PATCH 44/75] Added bitset tests to ibc tests to cover the exact blame and exoneration of finalizers in finality violation tests --- unittests/svnn_finality_violation_tests.cpp | 11 ++++++++++- unittests/svnn_ibc_tests.cpp | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 034f3c6d4e..3cd5417982 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -145,7 +145,16 @@ mvo prepare_rule_2_3_proof( const finalizer_policy active_finalizer_policy, bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_name rule, const mvo proof){ - try {chain.node0.push_action("violation"_n, rule, "violation"_n, proof);} + try { + + action_trace trace = chain.node0.push_action("violation"_n, rule, "violation"_n, proof)->action_traces[0]; + + std::pair blame = fc::raw::unpack>(trace.return_value); + + BOOST_TEST(blame.first == "30"); + BOOST_TEST(blame.second == "c0"); + + } catch (const eosio_assert_message_exception& e){ std::rethrow_exception(std::current_exception()); } diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 6f8387a64e..5a7029537b 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -656,6 +656,8 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) std::string bitset_4 = finality_proof::binary_to_hex("11011000100001"); std::string bitset_5 = finality_proof::binary_to_hex("111111111111111111111"); std::string bitset_6 = finality_proof::binary_to_hex("000000111111111111111"); + std::string bitset_7 = finality_proof::binary_to_hex("0011"); + std::string bitset_8 = finality_proof::binary_to_hex("1100"); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() ("bitset_string", "00") @@ -693,6 +695,18 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ("finalizers_count", 21) ); + chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() + ("bitset_string", "30") + ("bitset_vector", bitset_7) + ("finalizers_count", 4) + ); + + chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() + ("bitset_string", "c0") + ("bitset_vector", bitset_8) + ("finalizers_count", 4) + ); + } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() From fa8115437efc8146f51d872dc614927977e56eee Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 8 Aug 2024 07:31:44 -0600 Subject: [PATCH 45/75] Removed unused return statements, added comments about bitset verification --- unittests/svnn_finality_violation_tests.cpp | 9 +++------ .../savanna/finality_violation/finality_violation.cpp | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 3cd5417982..a69ef09f24 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -151,8 +151,9 @@ bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_n std::pair blame = fc::raw::unpack>(trace.return_value); - BOOST_TEST(blame.first == "30"); - BOOST_TEST(blame.second == "c0"); + //finalizers 0 and 1 are guilty, while finalizer 2 and 3 are innocent, see bitset tests in svnn_ibc_tests + BOOST_TEST(blame.first == "30"); //0011 (reverse order) + BOOST_TEST(blame.second == "c0"); //1100 (reverse order) } catch (const eosio_assert_message_exception& e){ @@ -211,8 +212,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_AUTO_TEST_CASE(contract_get_merkle_root) { try { - return; - //verify that get_merkle_root function from savanna contract produces the same results than calculate_merkle std::vector digests = { digest_type{"cb7ea678fe3a84fcff103f9cf08b97e5dd0b01bee54635f49255050f34bdbf34"}, digest_type{"ac2607856dea36137a1d6d0dd79b980f1ac7b104c59dc1e5fdd5f1568c7169c3"}, @@ -238,8 +237,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_AUTO_TEST_CASE(cluster_vote_propagation_tests) { try { - return; - finality_proof::proof_test_cluster cluster_1; finality_proof::proof_test_cluster cluster_2; finality_proof::proof_test_cluster cluster_3; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index d4cb637466..0787b67f51 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -43,7 +43,7 @@ std::pair finality_violation::rule1(const finalizer_po auto result = bitset::compare(proof_1_bitset, proof_2_bitset); return {result.first.to_string(), result.second.to_string()}; - + } //Rule #2 : Do not vote on a block that conflicts with the time interval of a strong vote From b10e275e88aa3140469e8b8a968328fb8ce804cc Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 06:53:05 -0600 Subject: [PATCH 46/75] Converted some static functions to inline functions in finality_proof --- unittests/finality_proof.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index a295166c24..659ac1000e 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -37,7 +37,7 @@ namespace finality_proof { }; - static std::string bitset_to_input_string(const boost::dynamic_bitset& bitset) { + inline std::string bitset_to_input_string(const boost::dynamic_bitset& bitset) { static const char* hexchar = "0123456789abcdef"; boost::dynamic_bitset bs(bitset); @@ -56,7 +56,7 @@ namespace finality_proof { return result; } - static std::string binary_to_hex(const std::string& bin) { + inline std::string binary_to_hex(const std::string& bin) { boost::dynamic_bitset bitset(bin.size()); for (size_t i = 0; i < bin.size(); ++i) { if (bin[i] == '1') { @@ -66,7 +66,7 @@ namespace finality_proof { return bitset_to_input_string(bitset); } - static auto finalizers_string = [](const vote_bitset_t finalizers) { + inline auto finalizers_string = [](const vote_bitset_t finalizers) { return bitset_to_input_string(finalizers); }; From 17355c847c41fbc00e5a516be8b0abcc989004a0 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 06:54:44 -0600 Subject: [PATCH 47/75] Re-enabled action traces size check in finality_proof --- unittests/finality_proof.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 659ac1000e..6a3ace5040 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -204,7 +204,7 @@ namespace finality_proof { signed_block_ptr block = result.block; - //BOOST_REQUIRE(result.onblock_trace->action_traces.size()>0); + BOOST_REQUIRE(result.onblock_trace->action_traces.size()>0); action_trace onblock_trace = result.onblock_trace->action_traces[0]; From 14f887380d42e1d597fa02e251eb65714821d873 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 06:56:30 -0600 Subject: [PATCH 48/75] Pass result to process_result by const reference --- unittests/finality_proof.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 6a3ace5040..6693707561 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -200,7 +200,7 @@ namespace finality_proof { return {finality_leaves.begin(), finality_leaves.begin() + cutoff + 1}; } - ibc_block_data_t process_result(eosio::testing::produce_block_result_t result){ + ibc_block_data_t process_result(const eosio::testing::produce_block_result_t& result){ signed_block_ptr block = result.block; From b223247a28403fe921cf97de10ecdd62b3333aa0 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 06:58:39 -0600 Subject: [PATCH 49/75] Removed unnecessary check to process_finalizer_votes, removed commented out code --- unittests/finality_proof.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 6693707561..8857a03649 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -258,10 +258,7 @@ namespace finality_proof { } //process votes - if (vote_propagation.size() == 0) this->process_votes(1, this->num_needed_for_quorum); //enough to reach quorum threshold - else this->process_finalizer_votes(vote_propagation); //enough to reach quorum threshold - - //this->process_votes(1, this->num_needed_for_quorum); //enough to reach quorum threshold + this->process_finalizer_votes(vote_propagation); //enough to reach quorum threshold // compute the IBC-relevant data finality_data_t finality_data = *this->node0.control->head_finality_data(); From e92e125d20ffe1fe535bc4386c769df088492c76 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 07:06:42 -0600 Subject: [PATCH 50/75] Updated functions svnn_finality_violation_tests to use const reference parameters --- unittests/svnn_finality_violation_tests.cpp | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index a69ef09f24..fc0a22e83a 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -35,7 +35,7 @@ struct finality_block_data { }; -finality_block_data get_finality_block_data(const ibc_block_data_t block_result){ +finality_block_data get_finality_block_data(const ibc_block_data_t& block_result){ return finality_block_data { .block_num = block_result.block->block_num(), @@ -50,11 +50,11 @@ finality_block_data get_finality_block_data(const ibc_block_data_t block_result) }; } -mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, - const finality_block_data fake_qc_block, - const qc_t fake_qc, - const finality_block_data real_qc_block, - const qc_t real_qc){ +mvo prepare_rule_1_proof( const finalizer_policy& active_finalizer_policy, + const finality_block_data& fake_qc_block, + const qc_t& fake_qc, + const finality_block_data& real_qc_block, + const qc_t& real_qc){ return mvo() ("finalizer_policy", active_finalizer_policy) @@ -95,11 +95,11 @@ mvo prepare_rule_1_proof( const finalizer_policy active_finalizer_policy, } -mvo prepare_rule_2_3_proof( const finalizer_policy active_finalizer_policy, - const finality_block_data high_qc_block, - const qc_t high_qc, - const finality_block_data low_qc_block, - const qc_t low_qc, +mvo prepare_rule_2_3_proof( const finalizer_policy& active_finalizer_policy, + const finality_block_data& high_qc_block, + const qc_t& high_qc, + const finality_block_data& low_qc_block, + const qc_t& low_qc, const std::vector digests){ return mvo() @@ -143,7 +143,7 @@ mvo prepare_rule_2_3_proof( const finalizer_policy active_finalizer_policy, } -bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_name rule, const mvo proof){ +bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_name& rule, const mvo& proof){ try { @@ -164,7 +164,7 @@ bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_n } -bool shouldFail(const finality_proof::proof_test_cluster& chain, const account_name rule, const mvo proof){ +bool shouldFail(const finality_proof::proof_test_cluster& chain, const account_name& rule, const mvo& proof){ bool last_action_failed = false; try { @@ -178,7 +178,7 @@ bool shouldFail(const finality_proof::proof_test_cluster& chain, const account_n } -digest_type compute_block_ref_digest(const ibc_block_data_t b){ +digest_type compute_block_ref_digest(const ibc_block_data_t& b){ block_ref_digest_data data = { .block_num = b.block->block_num(), @@ -193,7 +193,7 @@ digest_type compute_block_ref_digest(const ibc_block_data_t b){ } -digest_type compute_block_ref_digest(const finality_block_data b){ +digest_type compute_block_ref_digest(const finality_block_data& b){ block_ref_digest_data data = { .block_num = b.block_num, From 52436cffeb4a8e4f55094c24a3c0c0b64df65e9a Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 07:10:21 -0600 Subject: [PATCH 51/75] Simplified shouldPass function, removed unnecessary try catch --- unittests/svnn_finality_violation_tests.cpp | 29 +++++++-------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index fc0a22e83a..19dc659371 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -143,24 +143,15 @@ mvo prepare_rule_2_3_proof( const finalizer_policy& active_finalizer_policy, } -bool shouldPass(const finality_proof::proof_test_cluster& chain, const account_name& rule, const mvo& proof){ +void shouldPass(const finality_proof::proof_test_cluster& chain, const account_name& rule, const mvo& proof){ - try { - - action_trace trace = chain.node0.push_action("violation"_n, rule, "violation"_n, proof)->action_traces[0]; - - std::pair blame = fc::raw::unpack>(trace.return_value); - - //finalizers 0 and 1 are guilty, while finalizer 2 and 3 are innocent, see bitset tests in svnn_ibc_tests - BOOST_TEST(blame.first == "30"); //0011 (reverse order) - BOOST_TEST(blame.second == "c0"); //1100 (reverse order) + action_trace trace = chain.node0.push_action("violation"_n, rule, "violation"_n, proof)->action_traces[0]; - } - catch (const eosio_assert_message_exception& e){ - std::rethrow_exception(std::current_exception()); - } + std::pair blame = fc::raw::unpack>(trace.return_value); - return true; + //finalizers 0 and 1 are guilty, while finalizer 2 and 3 are innocent, see bitset tests in svnn_ibc_tests + BOOST_TEST(blame.first == "30"); //0011 (reverse order) + BOOST_TEST(blame.second == "c0"); //1100 (reverse order) } @@ -516,7 +507,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) get_finality_block_data(real_chain_block_7_result), get_finality_block_data(real_chain_block_8_result).qc_data.qc.value()); - BOOST_CHECK(shouldPass(real_chain, "rule1"_n, valid_rule_1_proof)); + shouldPass(real_chain, "rule1"_n, valid_rule_1_proof); //we temporarily disable a finalizer on the fake chain, which allow us to set up a proof of violation of rule #2 fake_chain.vote_propagation = {1,0,0}; @@ -623,7 +614,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), block_14_reversible_blocks_digests); - BOOST_CHECK(shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2)); + shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2); //Fake chain resume production, catches up with real chain auto fake_chain_block_15_result = light_client_data.scan_block(fake_chain.produce_block()); @@ -681,7 +672,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), block_19_reversible_blocks_digests); - BOOST_CHECK(shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1)); + shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1); //Resume production on fake chain auto fake_chain_block_20_result = light_client_data.scan_block(fake_chain.produce_block()); @@ -750,7 +741,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) get_finality_block_data(real_chain_block_24_result).qc_data.qc.value(), block_24_reversible_blocks_digests); - BOOST_CHECK(shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_2)); + shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_2); } FC_LOG_AND_RETHROW() } From 891296eacc21276572ec79786627046b702da088 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 07:23:39 -0600 Subject: [PATCH 52/75] Updated savanna contracts to pass function parameters by const reference where applicable, recompiled contracts --- unittests/svnn_finality_violation_tests.cpp | 2 +- .../test-contracts/savanna/common/savanna.hpp | 14 +++++------ .../finality_violation/finality_violation.cpp | 22 +++++++++--------- .../finality_violation/finality_violation.hpp | 20 ++++++++-------- .../finality_violation.wasm | Bin 55591 -> 50189 bytes unittests/test-contracts/savanna/ibc/ibc.cpp | 4 ++-- unittests/test-contracts/savanna/ibc/ibc.hpp | 4 ++-- unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 74754 -> 72732 bytes 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 19dc659371..8b69812a86 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -100,7 +100,7 @@ mvo prepare_rule_2_3_proof( const finalizer_policy& active_finalizer_policy, const qc_t& high_qc, const finality_block_data& low_qc_block, const qc_t& low_qc, - const std::vector digests){ + const std::vector& digests){ return mvo() ("finalizer_policy", active_finalizer_policy) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index b5e335f2d7..bd59c11dd2 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -72,7 +72,7 @@ namespace savanna { }; //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) { + uint64_t calculate_max_depth(const uint64_t node_count) { if(node_count <= 1) return node_count; return 64 - __builtin_clzll(2 << (64 - 1 - __builtin_clzll ((node_count - 1)))); //instead of std::bit_ceil available in C++ 20 @@ -82,7 +82,7 @@ namespace savanna { return __builtin_bswap32(input); } - checksum256 hash_pair(const std::pair p){ + checksum256 hash_pair(const std::pair& p){ auto result = eosio::pack(p); return sha256(result.data(), result.size()); } @@ -95,7 +95,7 @@ namespace savanna { } //compute path for proof of inclusion - std::vector _get_proof_path(uint64_t leaf_index, const uint64_t leaf_count) { + std::vector _get_proof_path(const uint64_t leaf_index, const uint64_t leaf_count) { std::vector proof_path; uint64_t current_leaf_count = leaf_count; uint64_t current_index = leaf_index; @@ -117,7 +117,7 @@ namespace savanna { } //compute the merkle root of target node and vector of merkle branches - checksum256 _compute_root(const std::vector proof_nodes, const checksum256& target, const uint64_t target_block_index, const uint64_t final_block_index){ + checksum256 _compute_root(const std::vector& proof_nodes, const checksum256& target, const uint64_t target_block_index, const uint64_t final_block_index){ checksum256 hash = target; std::vector proof_path = _get_proof_path(target_block_index, final_block_index+1); check(proof_path.size() == proof_nodes.size(), "internal error"); //should not happen @@ -141,7 +141,7 @@ namespace savanna { } //verify that the quorum certificate over the finality digest is valid - void _check_qc(const quorum_certificate_input& qc, const checksum256& finality_digest, const finalizer_policy_input finalizer_policy){ + void _check_qc(const quorum_certificate_input& qc, const checksum256& finality_digest, const finalizer_policy_input& finalizer_policy){ auto fa_itr = finalizer_policy.finalizers.begin(); auto fa_end_itr = finalizer_policy.finalizers.end(); size_t finalizer_count = finalizer_policy.finalizers.size(); @@ -184,7 +184,7 @@ namespace savanna { check(_verify(s_agg_pub_key, qc.signature, message), "signature verification failed"); } - checksum256 get_merkle_root(std::vector leaves) { + checksum256 get_merkle_root(const std::vector& leaves) { std::vector> tree; tree.push_back(leaves); @@ -295,7 +295,7 @@ namespace savanna { struct level_3_commitments_t : level_3_commitments_input { checksum256 base_digest; - level_3_commitments_t(const level_3_commitments_input& base, const checksum256 _base_digest) : + level_3_commitments_t(const level_3_commitments_input& base, const checksum256& _base_digest) : level_3_commitments_input(base), base_digest(_base_digest){ } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 0787b67f51..36cf4ee832 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -1,7 +1,7 @@ #include "finality_violation.hpp" //Verify QCs presented as proof -void check_qcs( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ +void check_qcs( const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2){ //Verify we have our level 3 commitments for both proofs check(proof_1.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); @@ -18,7 +18,7 @@ void check_qcs( const finalizer_policy_input finalizer_policy, const finality_pr } //Rule #1 : Do not vote on different blocks with the same timestamp -std::pair finality_violation::rule1(const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2){ +std::pair finality_violation::rule1(const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2){ //Verify QCs check_qcs(finalizer_policy, proof_1, proof_2); @@ -47,10 +47,10 @@ std::pair finality_violation::rule1(const finalizer_po } //Rule #2 : Do not vote on a block that conflicts with the time interval of a strong vote -std::pair finality_violation::rule2( const finalizer_policy_input finalizer_policy, - const finality_proof high_proof, - const finality_proof low_proof, - const std::vector reversible_blocks_digests){ +std::pair finality_violation::rule2( const finalizer_policy_input& finalizer_policy, + const finality_proof& high_proof, + const finality_proof& low_proof, + const std::vector& reversible_blocks_digests){ //Verify QCs check_qcs(finalizer_policy, high_proof, low_proof); @@ -88,10 +88,10 @@ std::pair finality_violation::rule2( const finalizer } //Rule #3 : Do not vote on a block that conflicts with another block on which you are locked -std::pair finality_violation::rule3( const finalizer_policy_input finalizer_policy, - const finality_proof high_proof, - const finality_proof low_proof, - const std::vector reversible_blocks_digests){ +std::pair finality_violation::rule3( const finalizer_policy_input& finalizer_policy, + const finality_proof& high_proof, + const finality_proof& low_proof, + const std::vector& reversible_blocks_digests){ //Verify QCs check_qcs(finalizer_policy, high_proof, low_proof); @@ -130,7 +130,7 @@ std::pair finality_violation::rule3( const finalizer } //For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation -ACTION finality_violation::testmroot(const checksum256 root, const std::vector reversible_blocks_digests){ +ACTION finality_violation::testmroot(const checksum256& root, const std::vector& reversible_blocks_digests){ checksum256 c_root = get_merkle_root(reversible_blocks_digests); check(c_root == root, "invalid root"); } \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index bfa1b7f29a..131ac86a4e 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -14,20 +14,20 @@ CONTRACT finality_violation : public contract { using contract::contract; [[eosio::action]] - std::pair rule1( const finalizer_policy_input finalizer_policy, const finality_proof proof_1, const finality_proof proof_2); + std::pair rule1( const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2); [[eosio::action]] - std::pair rule2( const finalizer_policy_input finalizer_policy, - const finality_proof high_proof, - const finality_proof low_proof, - const std::vector reversible_blocks_digests); + std::pair rule2( const finalizer_policy_input& finalizer_policy, + const finality_proof& high_proof, + const finality_proof& low_proof, + const std::vector& reversible_blocks_digests); [[eosio::action]] - std::pair rule3( const finalizer_policy_input finalizer_policy, - const finality_proof high_proof, - const finality_proof low_proof, - const std::vector reversible_blocks_digests); + std::pair rule3( const finalizer_policy_input& finalizer_policy, + const finality_proof& high_proof, + const finality_proof& low_proof, + const std::vector& reversible_blocks_digests); - ACTION testmroot(const checksum256 root, const std::vector reversible_blocks_digests); + ACTION testmroot(const checksum256& root, const std::vector& reversible_blocks_digests); }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 686f09e65f3f83b118beb6d6dd6ebdba6048431b..082e25aada94753f81681545828aed7135ae4468 100755 GIT binary patch delta 8310 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 Tp?qTRpS{lCoXL$H!!+%mU-UUm From fce30e8a58fa8a5511d9291f53fc444e67457010 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 26 Dec 2024 10:44:34 -0600 Subject: [PATCH 71/75] Reworked rule 2 and rule 3 tests to correctly identify finality violations --- unittests/svnn_finality_violation_tests.cpp | 436 +++++++++++------- unittests/svnn_ibc_tests.cpp | 12 +- .../test-contracts/savanna/common/savanna.hpp | 20 +- .../finality_violation/finality_violation.abi | 23 +- .../finality_violation/finality_violation.cpp | 27 +- .../finality_violation/finality_violation.hpp | 1 + .../finality_violation.wasm | Bin 57058 -> 71386 bytes unittests/test-contracts/savanna/ibc/ibc.abi | 4 +- unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 74038 -> 74062 bytes 9 files changed, 335 insertions(+), 188 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 0039eb3dc6..5a03c93408 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -49,108 +49,6 @@ finality_block_data get_finality_block_data(const ibc_block_data_t& block_result }; } -mvo prepare_rule_1_proof( const finalizer_policy& active_finalizer_policy, - const finality_block_data& fake_qc_block, - const qc_t& fake_qc, - const finality_block_data& real_qc_block, - const qc_t& real_qc){ - - return mvo() - ("finalizer_policy", active_finalizer_policy) - ("proof_1", mvo() - ("qc_block", mvo() - ("major_version", 1) - ("minor_version", 0) - ("active_finalizer_policy_generation", 1) - ("pending_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) - ("last_pending_finalizer_policy", active_finalizer_policy) - ("level_3_commitments", fake_qc_block.level_3_commitments) - ("witness_hash", fake_qc_block.base_digest) - ("finality_mroot", fake_qc_block.finality_root) - ) - ("active_policy_qc", mvo() - ("signature", fake_qc.active_policy_sig.sig.to_string()) - ("strong_votes", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) - ) - ) - ("proof_2", mvo() - ("qc_block", mvo() - ("major_version", 1) - ("minor_version", 0) - ("active_finalizer_policy_generation", 1) - ("pending_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) - ("last_pending_finalizer_policy", active_finalizer_policy) - ("level_3_commitments", real_qc_block.level_3_commitments) - ("witness_hash", real_qc_block.base_digest) - ("finality_mroot", real_qc_block.finality_root) - ) - ("active_policy_qc", mvo() - ("signature", real_qc.active_policy_sig.sig.to_string()) - ("strong_votes", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) - ) - ); - -} - -mvo prepare_rule_2_3_proof( const finalizer_policy& active_finalizer_policy, - const finality_block_data& high_qc_block, - const qc_t& high_qc, - const finality_block_data& low_qc_block, - const qc_t& low_qc, - const std::vector& digests){ - - return mvo() - ("finalizer_policy", active_finalizer_policy) - ("high_proof", mvo() - ("qc_block", mvo() - ("major_version", 1) - ("minor_version", 0) - ("active_finalizer_policy_generation", 1) - ("pending_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_start_timestamp", high_qc_block.last_pending_finalizer_policy_start_timestamp) - ("last_pending_finalizer_policy", active_finalizer_policy) - ("level_3_commitments", high_qc_block.level_3_commitments) - ("witness_hash", high_qc_block.base_digest) - ("finality_mroot", high_qc_block.finality_root) - ) - ("active_policy_qc", mvo() - ("signature", high_qc.active_policy_sig.sig.to_string()) - ("strong_votes", finality_proof::finalizers_string(high_qc.active_policy_sig.strong_votes.value())) - ) - ) - ("low_proof", mvo() - ("qc_block", mvo() - ("major_version", 1) - ("minor_version", 0) - ("active_finalizer_policy_generation", 1) - ("pending_finalizer_policy_generation", 1) - ("last_pending_finalizer_policy_start_timestamp", low_qc_block.last_pending_finalizer_policy_start_timestamp) - ("last_pending_finalizer_policy", active_finalizer_policy) - ("level_3_commitments", low_qc_block.level_3_commitments) - ("witness_hash", low_qc_block.base_digest) - ("finality_mroot", low_qc_block.finality_root) - ) - ("active_policy_qc", mvo() - ("signature", low_qc.active_policy_sig.sig.to_string()) - ("strong_votes", finality_proof::finalizers_string(low_qc.active_policy_sig.strong_votes.value())) - ) - ) - ("reversible_proof_of_inclusion", mvo() - ("target_block_index", 0) - ("final_block_index", 0) - ("target", mvo() - ("block_num", 0) - ("timestamp", low_qc_block.timestamp) - ("finality_digest", low_qc_block.finality_digest) - ("parent_timestamp", low_qc_block.parent_timestamp) - ) - ("merkle_branches", finality_proof::generate_proof_of_inclusion(digests, 0)) - ); - -} - void shouldPass(const finality_proof::proof_test_cluster& chain, const account_name& rule, const mvo& proof){ @@ -208,8 +106,164 @@ digest_type compute_block_ref_digest(const finality_block_data& b){ } +std::vector get_reversible_blocks_digests(const std::vector& blocks){ + + std::vector block_ref_digests; + + for (int i = 0 ; i < blocks.size(); i++){ + + finality_block_data b = blocks[i]; + + digest_type digest = compute_block_ref_digest(b); + + block_ref_digests.push_back(digest); + + } + + return block_ref_digests; + +} + +digest_type calculate_reversible_blocks_merkle(const std::vector& blocks){ + + std::vector block_ref_digests = get_reversible_blocks_digests(blocks); + + return calculate_merkle(block_ref_digests); + +} + + +std::pair get_target_reversible_block(const std::vector& reversible_blocks, const uint32_t& block_num){ + + auto itr = std::find_if(reversible_blocks.begin(), reversible_blocks.end(), [&](const finality_block_data& b){ + return b.block_num == block_num; + }); + + size_t index = std::distance(reversible_blocks.begin(), itr); + + return std::make_pair(*itr, index); + +} + BOOST_AUTO_TEST_SUITE(svnn_finality_violation) + mvo prepare_rule_1_proof( const finalizer_policy& active_finalizer_policy, + const finality_block_data& fake_qc_block, + const qc_t& fake_qc, + const finality_block_data& real_qc_block, + const qc_t& real_qc){ + + return mvo() + ("finalizer_policy", active_finalizer_policy) + ("proof_1", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", fake_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", fake_qc_block.level_3_commitments) + ("witness_hash", fake_qc_block.base_digest) + ("finality_mroot", fake_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", fake_qc.active_policy_sig.sig.to_string()) + ("strong_votes", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) + ) + ) + ("proof_2", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", real_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", real_qc_block.level_3_commitments) + ("witness_hash", real_qc_block.base_digest) + ("finality_mroot", real_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", real_qc.active_policy_sig.sig.to_string()) + ("strong_votes", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) + ) + ); + + } + + mvo prepare_rule_2_3_proof( const finalizer_policy& active_finalizer_policy, + const finality_block_data& high_qc_block, + const qc_t& high_qc, + const finality_block_data& low_qc_block, + const qc_t& low_qc, + const finality_block_data& target_reversible_block, + const size_t target_reversible_block_index, + const std::vector& reversible_blocks){ + + std::cout << "target_reversible_block_index: " << target_reversible_block_index << std::endl; + std::cout << "reversible_blocks.size(): " << reversible_blocks.size() << std::endl; + + std::cout << "target_reversible_block: " << compute_block_ref_digest(target_reversible_block) << std::endl; + + std::vector merkle_branches = finality_proof::generate_proof_of_inclusion(get_reversible_blocks_digests(reversible_blocks), target_reversible_block_index); + + for(const auto& digest : merkle_branches){ + std::cout << "merkle branch: " << digest << std::endl; + } + + + return mvo() + ("finalizer_policy", active_finalizer_policy) + ("high_proof", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", high_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", high_qc_block.level_3_commitments) + ("witness_hash", high_qc_block.base_digest) + ("finality_mroot", high_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", high_qc.active_policy_sig.sig.to_string()) + ("strong_votes", finality_proof::finalizers_string(high_qc.active_policy_sig.strong_votes.value())) + ) + ) + ("low_proof", mvo() + ("qc_block", mvo() + ("major_version", 1) + ("minor_version", 0) + ("active_finalizer_policy_generation", 1) + ("pending_finalizer_policy_generation", 1) + ("last_pending_finalizer_policy_start_timestamp", low_qc_block.last_pending_finalizer_policy_start_timestamp) + ("last_pending_finalizer_policy", active_finalizer_policy) + ("level_3_commitments", low_qc_block.level_3_commitments) + ("witness_hash", low_qc_block.base_digest) + ("finality_mroot", low_qc_block.finality_root) + ) + ("active_policy_qc", mvo() + ("signature", low_qc.active_policy_sig.sig.to_string()) + ("strong_votes", finality_proof::finalizers_string(low_qc.active_policy_sig.strong_votes.value())) + ) + ) + ("reversible_proof_of_inclusion", mvo() + ("target_reversible_block_index", target_reversible_block_index) + ("final_reversible_block_index", reversible_blocks.size() - 1) + ("target", mvo() + ("block_num", target_reversible_block.block_num) + ("timestamp", target_reversible_block.timestamp) + ("finality_digest", target_reversible_block.finality_digest) + ("parent_timestamp", target_reversible_block.parent_timestamp) + ) + ("merkle_branches", merkle_branches) + ); + + } + + BOOST_AUTO_TEST_CASE(contract_get_merkle_root) { try { //verify that get_merkle_root function from savanna contract produces the same results than calculate_merkle @@ -312,9 +366,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //The data_cache stores data relevant to finality violation proofs. //It only operates in optimistic mode, which is sufficient for finality violation proofs testing purposes - //store the reversible blocks - std::vector reversible_blocks; - //store the last block over which we have a QC, as well as said QC finality_block_data high_qc_block; qc_t high_qc; @@ -323,9 +374,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) finality_block_data last_final_block; qc_t last_final_qc; - //store all policies sunset data - std::vector policy_sunsets; - //current active finalizer policy finalizer_policy active_finalizer_policy; @@ -393,24 +441,36 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) } - std::vector get_reversible_blocks_digests(){ + // std::vector get_reversible_blocks_digests(){ - std::vector block_ref_digests; + // std::vector block_ref_digests; - for (int i = 0 ; i < reversible_blocks.size() - 1; i++){ + // for (int i = 0 ; i < reversible_blocks.size() - 1; i++){ - finality_block_data b = reversible_blocks[i]; + // finality_block_data b = reversible_blocks[i]; - digest_type digest = compute_block_ref_digest(b); + // digest_type digest = compute_block_ref_digest(b); - block_ref_digests.push_back(digest); + // block_ref_digests.push_back(digest); - } + // } + + // return block_ref_digests; + + // } - return block_ref_digests; + std::vector get_reversible_blocks(){ + std::vector result(reversible_blocks.begin(), reversible_blocks.end() - 1); + return result; + } + finality_block_data get_qc_block(){ + return reversible_blocks.back(); } + //store the reversible blocks + std::vector reversible_blocks; + }; data_cache light_client_data; @@ -457,7 +517,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_REQUIRE(fake_chain_block_4_result.qc_data.qc.has_value()); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + BOOST_TEST(light_client_data.get_reversible_blocks().size() == 1u); //produce a few blocks on the real chain auto real_chain_genesis_block_result = real_chain.produce_block(); @@ -494,7 +554,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_5_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_5_result = real_chain.produce_block(); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + BOOST_TEST(light_client_data.get_reversible_blocks().size() == 1u); //verify the chains have diverged BOOST_TEST(fake_chain_block_5_result.finality_digest != real_chain_block_5_result.finality_digest); @@ -532,7 +592,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_9_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_9_result = real_chain.produce_block(); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + BOOST_TEST(light_client_data.get_reversible_blocks().size() == 1u); //restore vote propagation for fake chain. This leaves a one-block gap where no finality progress was achieved fake_chain.vote_propagation = {1,1,0}; @@ -540,7 +600,22 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_10_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_10_result = real_chain.produce_block(); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 2u); + //get the reversible blocks for block #10 (should be two digests, block ref data of #8 and #9) + std::vector block_10_reversible_blocks = light_client_data.get_reversible_blocks(); + + BOOST_TEST(light_client_data.get_reversible_blocks().size() == 2u); + BOOST_TEST(block_10_reversible_blocks.size() == 2u); + + BOOST_TEST(compute_block_ref_digest(block_10_reversible_blocks[0]) == compute_block_ref_digest(fake_chain_block_8_result)); + BOOST_TEST(compute_block_ref_digest(block_10_reversible_blocks[1]) == compute_block_ref_digest(fake_chain_block_9_result)); + + //calculate the merkle root of the reversible blocks digests + digest_type block_10_reversible_blocks_merkle_root = calculate_reversible_blocks_merkle(block_10_reversible_blocks); + + //verify the merkle root of the reversible blocks digests is the same as the one recorded by the light client + BOOST_TEST(fake_chain_block_10_result.level_3_commitments.reversible_blocks_mroot == block_10_reversible_blocks_merkle_root); + + auto proof_1_target_reversible_block = get_target_reversible_block(light_client_data.get_reversible_blocks(), real_chain_block_9_result.block->block_num()); //Real chain has a QC on #9 carried by #10, but fake chain doesn't BOOST_TEST(!fake_chain_block_10_result.qc_data.qc.has_value()); @@ -553,17 +628,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_11_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_11_result.qc_data.qc.has_value()); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 3u); - - //get the reversible blocks for block #10 (should be two digests, block ref data of #8 and #9) - std::vector block_10_reversible_blocks_digests = light_client_data.get_reversible_blocks_digests(); - - //since we're preparing the proof for block #10, we must discard block #11 - block_10_reversible_blocks_digests.pop_back(); - - BOOST_TEST(block_10_reversible_blocks_digests.size() == 2u); - BOOST_TEST(block_10_reversible_blocks_digests[0] == compute_block_ref_digest(fake_chain_block_8_result)); - BOOST_TEST(block_10_reversible_blocks_digests[1] == compute_block_ref_digest(fake_chain_block_9_result)); + BOOST_TEST(light_client_data.get_reversible_blocks().size() == 3u); //Light client recorded the last QC (over block #10) on the fake chain, which was delivered via block #11. //Block #10 claims a QC over block #8. We provide fake block #10 and the QC over it, as well as the digests of the reversible blocks the light client recorded (block #8 and block #9). @@ -571,13 +636,18 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Since there is a time range conflict, and since the real block #9 finality digest doesn't appear in the list of reversible blocks digests committed to by the fake block QC, //this is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, - light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], - light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), + light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #10 + light_client_data.get_qc_block().qc_data.qc.value(), //QC over fake block #10 get_finality_block_data(real_chain_block_9_result), get_finality_block_data(real_chain_block_10_result).qc_data.qc.value(), - block_10_reversible_blocks_digests); + proof_1_target_reversible_block.first, + proof_1_target_reversible_block.second, + block_10_reversible_blocks); + real_chain.node0.push_action("violation"_n, "rule2"_n, "violation"_n, valid_rule_2_proof_1); + + std::cout << std::endl << "proof 1 passed" << std::endl << std::endl; auto fake_chain_block_12_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_12_result = real_chain.produce_block(); @@ -585,7 +655,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_12_result.qc_data.qc.has_value()); BOOST_TEST(real_chain_block_12_result.qc_data.qc.has_value()); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + BOOST_TEST(light_client_data.get_reversible_blocks().size() == 1u); //we can now test the other possibility for rule #2, where the high proof is actually from the real chain @@ -595,7 +665,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_13_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_13_result = real_chain.produce_block(); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + BOOST_TEST(light_client_data.get_reversible_blocks().size() == 1u); //restore vote propagation on the real chain real_chain.vote_propagation = {1,0,1}; @@ -603,10 +673,23 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto fake_chain_block_14_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_14_result = real_chain.produce_block(); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 1u); + BOOST_TEST(light_client_data.get_reversible_blocks().size() == 1u); //compute reversible digests on block #12 and #13 on the real chain - std::vector block_14_reversible_blocks_digests = { compute_block_ref_digest(real_chain_block_12_result), compute_block_ref_digest(real_chain_block_13_result)}; + std::vector block_14_reversible_blocks = { get_finality_block_data(real_chain_block_12_result), + get_finality_block_data(real_chain_block_13_result)}; + + for (const auto& block : block_14_reversible_blocks){ + std::cout << "block # : " << block.block_num << " -> " << compute_block_ref_digest(block) << std::endl; + } + + //calculate the merkle root of the reversible blocks digests + digest_type block_14_reversible_blocks_merkle_root = calculate_reversible_blocks_merkle(block_14_reversible_blocks); + + //verify the merkle root of the reversible blocks digests is the same as the one recorded by the light client + BOOST_TEST(real_chain_block_14_result.level_3_commitments.reversible_blocks_mroot == block_14_reversible_blocks_merkle_root); + + std::cout << "block 14 reversible blocks merkle root: " << block_14_reversible_blocks_merkle_root << std::endl; //Fake chain has a QC on #13 carried by #14, but real chain doesn't BOOST_TEST(fake_chain_block_14_result.qc_data.qc.has_value()); @@ -617,6 +700,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Things are back to normal on the real chain, and we have a QC BOOST_TEST(real_chain_block_15_result.qc_data.qc.has_value()); + + auto proof_2_target_reversible_block = get_target_reversible_block(block_14_reversible_blocks, block_14_reversible_blocks[block_14_reversible_blocks.size()-1].block_num); //We discovered a QC (over block #14) on the real chain, which was delivered via block #15. //Last QC recorded on the fake chain was over block #13, and was delivered by block #14 @@ -626,9 +711,11 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, get_finality_block_data(real_chain_block_14_result), get_finality_block_data(real_chain_block_15_result).qc_data.qc.value(), - light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], - light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), - block_14_reversible_blocks_digests); + light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #13 + light_client_data.get_qc_block().qc_data.qc.value(), //QC over fake block #13 + proof_2_target_reversible_block.first, + proof_2_target_reversible_block.second, + block_14_reversible_blocks); shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2); @@ -665,31 +752,41 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) auto real_chain_block_19_result = real_chain.produce_block(); //compute reversible digests on block #16, #17 and #18 on the real chain - std::vector block_19_reversible_blocks_digests = { compute_block_ref_digest(real_chain_block_16_result), compute_block_ref_digest(real_chain_block_17_result), compute_block_ref_digest(real_chain_block_18_result)}; + std::vector block_19_reversible_blocks = { get_finality_block_data(real_chain_block_16_result), get_finality_block_data(real_chain_block_17_result), get_finality_block_data(real_chain_block_18_result)}; + + //calculate the merkle root of the reversible blocks digests + digest_type block_19_reversible_blocks_merkle_root = calculate_reversible_blocks_merkle(block_19_reversible_blocks); + + //verify the merkle root of the reversible blocks digests is the same as the one recorded by the light client + BOOST_TEST(real_chain_block_19_result.level_3_commitments.reversible_blocks_mroot == block_19_reversible_blocks_merkle_root); BOOST_TEST(fake_chain_block_19_result.qc_data.qc.has_value()); BOOST_TEST(!real_chain_block_19_result.qc_data.qc.has_value()); - //At this point, we can verify that the time range of the last fake chain block on which we have a QC is fully encapsulated within the time range of the last real chain block + //At this point, we can verify that the time range of the last fake chain block on which we have a QC is fully contained within the time range of the last real chain block BOOST_TEST(fake_chain_block_18_result.level_3_commitments.latest_qc_claim_block_num == fake_chain_block_17_result.block->block_num()); BOOST_TEST(real_chain_block_19_result.level_3_commitments.latest_qc_claim_block_num == real_chain_block_16_result.block->block_num()); + auto proof_3_target_reversible_block = get_target_reversible_block(block_19_reversible_blocks, fake_chain_block_17_result.block->block_num()); + //stop producing on fake chain, produce one more block on the real chain to get a QC on previous block auto real_chain_block_20_result = real_chain.produce_block(); BOOST_TEST(real_chain_block_20_result.qc_data.qc.has_value()); //We can now produce a proof of finality violation demonstrating that finalizers were locked on #18 on the fake chain, while also voting on a conflicting block #19 - //on the real chain which is not a descendant of #18, where the time range committed to by #18 is fully encapsulated within the time range committed to by #19 + //on the real chain which is not a descendant of #18, where the time range committed to by #18 is fully contained within the time range committed to by #19 mutable_variant_object valid_rule_3_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, get_finality_block_data(real_chain_block_19_result), get_finality_block_data(real_chain_block_20_result).qc_data.qc.value(), - light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], - light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), - block_19_reversible_blocks_digests); + light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #18 + light_client_data.get_qc_block().qc_data.qc.value(), //QC over fake block #18 + proof_3_target_reversible_block.first, + proof_3_target_reversible_block.second, + block_19_reversible_blocks); shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1); - + //Resume production on fake chain auto fake_chain_block_20_result = light_client_data.scan_block(fake_chain.produce_block()); BOOST_TEST(fake_chain_block_20_result.qc_data.qc.has_value()); @@ -729,33 +826,40 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //At this point, we can verify that the time range of the last real chain block on which we have a QC is fully encapsulated within the time range of the last fake chain block BOOST_TEST(fake_chain_block_24_result.level_3_commitments.latest_qc_claim_block_num == fake_chain_block_21_result.block->block_num()); BOOST_TEST(real_chain_block_23_result.level_3_commitments.latest_qc_claim_block_num == real_chain_block_22_result.block->block_num()); - - //stop producing on real chain, produce one more block on the fake chain to get a QC on previous block - auto fake_chain_block_25_result = light_client_data.scan_block(fake_chain.produce_block()); - BOOST_TEST(fake_chain_block_25_result.qc_data.qc.has_value()); + //get the reversible blocks for block #24 (should be three blocks: #21, #22 and #23) + std::vector block_24_reversible_blocks = light_client_data.get_reversible_blocks(); + + + BOOST_TEST(block_24_reversible_blocks.size() == 3u); - BOOST_TEST(light_client_data.get_reversible_blocks_digests().size() == 4u); + BOOST_TEST(compute_block_ref_digest(block_24_reversible_blocks[0]) == compute_block_ref_digest(fake_chain_block_21_result)); + BOOST_TEST(compute_block_ref_digest(block_24_reversible_blocks[1]) == compute_block_ref_digest(fake_chain_block_22_result)); + BOOST_TEST(compute_block_ref_digest(block_24_reversible_blocks[2]) == compute_block_ref_digest(fake_chain_block_23_result)); - //get the reversible blocks for block #24 (should be three digests, block ref data of #21, #22 and #23) - std::vector block_24_reversible_blocks_digests = light_client_data.get_reversible_blocks_digests(); + //calculate the merkle root of the reversible blocks digests + digest_type block_24_reversible_blocks_merkle_root = calculate_reversible_blocks_merkle(block_24_reversible_blocks); - //since we're preparing the proof for block #24, we must discard block #25 - block_24_reversible_blocks_digests.pop_back(); + //verify the merkle root of the reversible blocks digests is the same as the one recorded by the light client + BOOST_TEST(fake_chain_block_24_result.level_3_commitments.reversible_blocks_mroot == block_24_reversible_blocks_merkle_root); + + //stop producing on real chain, produce one more block on the fake chain to get a QC on previous block + auto fake_chain_block_25_result = light_client_data.scan_block(fake_chain.produce_block()); + + BOOST_TEST(fake_chain_block_25_result.qc_data.qc.has_value()); - BOOST_TEST(block_24_reversible_blocks_digests.size() == 3u); - BOOST_TEST(block_24_reversible_blocks_digests[0] == compute_block_ref_digest(fake_chain_block_21_result)); - BOOST_TEST(block_24_reversible_blocks_digests[1] == compute_block_ref_digest(fake_chain_block_22_result)); - BOOST_TEST(block_24_reversible_blocks_digests[2] == compute_block_ref_digest(fake_chain_block_23_result)); + auto proof_4_target_reversible_block = get_target_reversible_block(block_24_reversible_blocks, fake_chain_block_22_result.block->block_num()); //We can now produce a proof of finality violation demonstrating that finalizers were locked on #23 on the real chain, while also voting on a conflicting block #24 - //on the fake chain which is not a descendant of #23, where the time range committed to by #23 is fully encapsulated within the time range committed to by #24 + //on the fake chain which is not a descendant of #23, where the time range committed to by #23 is fully contained within the time range committed to by #24 mutable_variant_object valid_rule_3_proof_2 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, - light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-2], - light_client_data.reversible_blocks[light_client_data.reversible_blocks.size()-1].qc_data.qc.value(), + light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #23 + light_client_data.get_qc_block().qc_data.qc.value(), //QC over fake block #23 get_finality_block_data(real_chain_block_23_result), - get_finality_block_data(real_chain_block_24_result).qc_data.qc.value(), - block_24_reversible_blocks_digests); + get_finality_block_data(real_chain_block_24_result).qc_data.qc.value(), + proof_4_target_reversible_block.first, + proof_4_target_reversible_block.second, + block_24_reversible_blocks); shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_2); diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 5ace56a2e1..4f47bb209b 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -294,8 +294,8 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) // onblock action proof mutable_variant_object onblock_action_proof = mvo() - ("target_block_index", 0) - ("final_block_index", 3) + ("target_action_index", 0) + ("final_action_index", 3) ("target", mvo() ("action", mvo() ("account", block_7_result.onblock_trace.act.account) @@ -313,8 +313,8 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) // first action proof (check_heavy_proof_1) mutable_variant_object action_proof_1 = mvo() - ("target_block_index", 1) - ("final_block_index", 3) + ("target_action_index", 1) + ("final_action_index", 3) ("target", mvo() ("action", mvo() ("account", check_heavy_proof_1_trace.act.account) @@ -331,8 +331,8 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) // second action proof (check_light_proof_1) mutable_variant_object action_proof_2 = mvo() - ("target_block_index", 2) - ("final_block_index", 3) + ("target_action_index", 2) + ("final_action_index", 3) ("target", mvo() ("action", mvo() ("account", check_light_proof_1_trace.act.account) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index 64c6cf0d4e..9a459906cf 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -126,7 +126,13 @@ namespace savanna { checksum256 _compute_root(const std::vector& proof_nodes, const checksum256& target, const uint64_t target_block_index, const uint64_t final_block_index){ checksum256 hash = target; std::vector proof_path = _get_proof_path(target_block_index, final_block_index+1); - check(proof_path.size() == proof_nodes.size(), "internal error"); //should not happen + + print("target_block_index: ", target_block_index, "\n"); + print("final_block_index: ", final_block_index, "\n"); + print("proof_path size: ", proof_path.size(), "\n"); + print("proof_nodes size: ", proof_nodes.size(), "\n"); + + check(proof_path.size() == proof_nodes.size(), "proof path size and proof nodes size mismatch"); for (int i = 0 ; i < proof_nodes.size() ; i++){ const checksum256 node = proof_nodes[i]; hash = hash_pair(proof_path[i] ? std::make_pair(node, hash) : std::make_pair(hash, node)); @@ -332,8 +338,8 @@ namespace savanna { }; struct action_proof_of_inclusion { - uint64_t target_block_index = 0; - uint64_t final_block_index = 0; + uint64_t target_action_index = 0; + uint64_t final_action_index = 0; action_data target; @@ -342,7 +348,7 @@ namespace savanna { //returns the merkle root obtained by hashing target.digest() with merkle_branches checksum256 root() const { checksum256 digest = action_data_internal(target).digest(); - checksum256 root = _compute_root(merkle_branches, digest, target_block_index, final_block_index); + checksum256 root = _compute_root(merkle_branches, digest, target_action_index, final_action_index); return root; }; }; @@ -395,8 +401,8 @@ namespace savanna { }; struct reversible_proof_of_inclusion { - uint64_t target_block_index = 0; - uint64_t final_block_index = 0; + uint64_t target_reversible_block_index = 0; + uint64_t final_reversible_block_index = 0; block_ref_data target; @@ -405,7 +411,7 @@ namespace savanna { //returns the merkle root obtained by hashing target.digest() with merkle_branches checksum256 root() const { checksum256 digest = target.digest(); - checksum256 root = _compute_root(merkle_branches, digest, target_block_index, final_block_index); + checksum256 root = _compute_root(merkle_branches, digest, target_reversible_block_index, final_reversible_block_index); return root; }; }; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 3e262e8eee..358d06b032 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -184,11 +184,11 @@ "base": "", "fields": [ { - "name": "target_block_index", + "name": "target_reversible_block_index", "type": "uint64" }, { - "name": "final_block_index", + "name": "final_reversible_block_index", "type": "uint64" }, { @@ -276,6 +276,20 @@ "type": "checksum256[]" } ] + }, + { + "name": "testpath", + "base": "", + "fields": [ + { + "name": "target_block_index", + "type": "uint32" + }, + { + "name": "final_block_index", + "type": "uint32" + } + ] } ], "actions": [ @@ -298,6 +312,11 @@ "name": "testmroot", "type": "testmroot", "ricardian_contract": "" + }, + { + "name": "testpath", + "type": "testpath", + "ricardian_contract": "" } ], "tables": [], diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index d6edc43376..c71691b251 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -156,24 +156,28 @@ std::pair finality_violation::rule3( const finalizer auto finalizer_digests = check_qcs(finalizer_policy, high_proof, low_proof); //Compare timestamps - block_timestamp high_proof_timestamp = high_proof.qc_block.level_3_commitments.value().timestamp; + block_timestamp target_proof_timestamp = proof_of_inclusion.target.timestamp; block_timestamp low_proof_last_claim_timestamp = low_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - block_timestamp high_proof_parent_timestamp = proof_of_inclusion.target.parent_timestamp; + block_timestamp target_proof_parent_timestamp = proof_of_inclusion.target.parent_timestamp; //Verify that the proof of inclusion resolves to the reversible blocks mroot of the high proof check(proof_of_inclusion.root() == high_proof.qc_block.level_3_commitments.value().reversible_blocks_mroot, "proof of inclusion must resolve to the reversible blocks mroot of the high proof"); + print("target_proof_timestamp: ", target_proof_timestamp.to_string(), "\n"); + print("low_proof_last_claim_timestamp: ", low_proof_last_claim_timestamp.to_string(), "\n"); + print("target_proof_parent_timestamp: ", target_proof_parent_timestamp.to_string(), "\n"); + //A lock violation has occured if the high proof timestamp is greater than or equal to the low proof last claim timestamp and the high proof parent timestamp is less than the low proof last claim timestamp - bool lock_violation = high_proof_timestamp >= low_proof_last_claim_timestamp && high_proof_parent_timestamp < low_proof_last_claim_timestamp; + bool lock_violation = target_proof_timestamp >= low_proof_last_claim_timestamp && target_proof_parent_timestamp < low_proof_last_claim_timestamp; check(lock_violation, "proofs must demonstrate a lock violation"); bool finality_violation = false; //If the timestamp for the submitted reversible blocks leaf node is strictly greater than low_proof_last_claim_timestamp, we know that the low proof block is not an ancestor of the high proof block //and therefore, a rule 3 violation has occurred. - if(proof_of_inclusion.target.timestamp > low_proof_last_claim_timestamp) finality_violation = true; - else if(proof_of_inclusion.target.timestamp == low_proof_last_claim_timestamp) { + if(target_proof_timestamp > low_proof_last_claim_timestamp) finality_violation = true; + else if(target_proof_timestamp == low_proof_last_claim_timestamp) { //If the timestamp for the submitted reversible blocks leaf node is exactly equal to low_proof_timestamp, we need to compare the finality digest of the low proof block to the finality digest of the submitted reversible blocks leaf node, //to check that they are not the same. If they are the same, the submitted proof is not correct. But if they are different, then we know that the low proof block is not an ancestor of the high proof block check(finalizer_digests.second != proof_of_inclusion.target.finality_digest, "finality digest of low proof must be different from the finality digest of the submitted reversible blocks leaf node"); @@ -192,4 +196,17 @@ std::pair finality_violation::rule3( const finalizer ACTION finality_violation::testmroot(const checksum256& root, const std::vector& reversible_blocks_digests){ checksum256 c_root = get_merkle_root(reversible_blocks_digests); check(c_root == root, "invalid root"); +} + +//For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation +ACTION finality_violation::testpath(const uint32_t target_block_index, const uint32_t final_block_index){ + std::vector proof_path = _get_proof_path(target_block_index, final_block_index+1); + print("target_block_index: ", target_block_index, "\n"); + print("final_block_index: ", final_block_index, "\n"); + print("proof_path.size(): ", proof_path.size(), "\n"); + for(int i = 0; i < proof_path.size(); i++){ + print("proof_path[", i, "]: ", uint16_t(proof_path[i]), "\n"); + } + + //check(false, "expected failure"); } \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 2126c0d0ad..32f8edd057 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -35,5 +35,6 @@ CONTRACT finality_violation : public contract { //For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation ACTION testmroot(const checksum256& root, const std::vector& reversible_blocks_digests); + ACTION testpath(const uint32_t target_block_index, const uint32_t final_block_index); }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 24f213f545e26506eeb63707a4664f5848c0ebe5..18e89be2d55823c08c77e21ff9a92ce04b47aaf1 100755 GIT binary patch delta 24821 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 JDGj&L{|2G~?w0@n diff --git a/unittests/test-contracts/savanna/ibc/ibc.abi b/unittests/test-contracts/savanna/ibc/ibc.abi index 9f6405eb90..12ad8e14a4 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.abi +++ b/unittests/test-contracts/savanna/ibc/ibc.abi @@ -67,11 +67,11 @@ "base": "", "fields": [ { - "name": "target_block_index", + "name": "target_action_index", "type": "uint64" }, { - "name": "final_block_index", + "name": "final_action_index", "type": "uint64" }, { diff --git a/unittests/test-contracts/savanna/ibc/ibc.wasm b/unittests/test-contracts/savanna/ibc/ibc.wasm index ce246487b28f7d190a2e2f6b38f0692ede861b37..c0420b76eea7c2f28b46ff8f5e5e75d442f83c6e 100755 GIT binary patch delta 1812 zcmaJ>dr*{B6#ve*3oe7?_5u}jH&+mZ)j-Em1Y9vtL`I#)e64_hqQWi#jnxcV8w>O*>ITk1(pI)!@QsnezXLs;0_K1t%ipzH24wF5wa** zgeE+`_*>{<>p~Yu;1m{zRYL;{4PP&TKba|F1mH26A}11>Bd5R#Y>!;*cH_Dr2WN4S ze3DY|kqE0?1dX7{>0q-~eC0&Jm)B@`uzWom3+~!L!R*ln2@t=LjsArGS`(bcR_$tP zqiT~vgkB7diJ^<~n6UBI4k|GooGEcByB{-$Q=1vDhC6I+d_TZ#7QUrILgA1RM0nOP z1Lt>6kVztJRP2Vr?QXU!1uC*-?APsUBv64~y7f@OcIl=9SlD5`NeYKpNcvgXbE&pu z*du3P#S_UJZ)UiTWkXeF3=H6>nTcZVHP)Yb0iYUx%sK|QFnU+zbGddH>~S;j8-qP- z@Ms`_=o*+zyn~2^?n{m z2ew+hFtS8JhNO~@M5w`AU%pSu+Jgs0x=kuk;le8(#(o3i2}Yi_w&k}*ZDeMk_PJ5z)(^DZm7bP!E;#H;6szEYlyI+0;iv$9{}kpw)Kq6 zftEO>Ni8Kyt5$`}N<79r-NooDYJBn)2Qs@X1 zAq%R^7ezAKSgba$ltPb`uKb;V9>5PTbEy-=ODF+tw$alCracjNxPUjg6uW?ZK~=_k zF0kU+!ub<`wwiBhBRPH`=e5n>(ce)b7Q374UWH3i@03)%ev2|CHa$@ptBY6KRh>RQ zAx)XK?c)Sx^0u_(*z~wW^ZtoYC&~oP(Kf-^ATus^hsnldcbINQcQ`Lm3Yzi`!TGXe kb5eqm62|KE>4^!c$~e6)JvBBiT`=!dKs`u5oFxeV0qp|`asU7T delta 1767 zcmaJ>dr(wm6u;-*1(!i|djSHv=n8_c8t7PxfGY-y$SBj8ua&KQrHDi}PJckSNXBDW zaych0%@a7RSbOaw%2?t=J!%2Hh+n?jaC~pSM+n zFYws>qtL=uCd`rGAXX&SK{ZQAS|!0nW=4OO{1xQ@GWH;Du7tS|Q*z&`vo?+9GR^qq&F%C$xBj+=tDdUw{i z;XHb%Wry-bQS9$vhYKdT=*8X3+#~YE=)ypnMrz>>D8f2}KVetlYB+^4MH@-qSImcK zFPaDC=v#bkSQA_l=X?C;XDrBpdF6UAu=^$6B6$z33BfSKXyPKu$6=}=eYnbb!`oCP zqKsF;vN*z@$~08v#XY=A?;fs+D3AaDDiS&Y3h++FM8cQ$THzP=_ zw_@ApuTexmO$f$Shr4uNlS4qw-Z6C6$Jr%bE$2imVZ(`JLQf0l7g$oE*k~CGE3wzo zOaWh6`*>yE*V+0A;`-~CLr-~M0T?iA;0%^EhSTQi8%K1gj&Z)q9T_(6Ytx3tA6dX zsLRy$Gt?}7IQ*00x4E>FzleWAYKvmH>l>Z-hFRwTF=ymNgJ20~7n?ULw%myYDxWcjW_G7*qW%dv?p==okzlH+l>U zepm?<-t@=>a2DVA=M?GJ4uxXe5P#&_A@dWx8L0$suCz;KGy}|n9-|!~i%6mfd0?=f z7fI>rBF(x~2Hi3;MR@@|fRj*br5CJ|C;?rzF~kQZJ!ZG~Kp44H_<;LBRmQtMu;j_Y zC1ZiEnqO)YIliypwXI*$-%%1D1z77}fD5uQ?__RYpPl(;M)3OV>}}cBqyA7Y8n*^Q sfLzcVx*~Y%<;LBCFxgrg2;WP_BTDcmL-h^8+ag&FN@xI?Zkizc3x5&oO8@`> From 20090c652349239f263e136f7ab4c3175484fc06 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Thu, 26 Dec 2024 10:47:52 -0600 Subject: [PATCH 72/75] Use shouldPass() semantics for first proof --- unittests/svnn_finality_violation_tests.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 5a03c93408..16130a4f83 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -644,8 +644,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) proof_1_target_reversible_block.second, block_10_reversible_blocks); - - real_chain.node0.push_action("violation"_n, "rule2"_n, "violation"_n, valid_rule_2_proof_1); + shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_1); std::cout << std::endl << "proof 1 passed" << std::endl << std::endl; From c7ade593c92904e40a4765608fc7294e133ef006 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sat, 28 Dec 2024 06:32:23 -0600 Subject: [PATCH 73/75] Added tests for proofs being rejected due to low proof block being an ancestor of the high proof block --- unittests/svnn_finality_violation_tests.cpp | 136 ++++++++---------- .../test-contracts/savanna/common/savanna.hpp | 5 - .../finality_violation/finality_violation.abi | 38 ----- .../finality_violation/finality_violation.cpp | 23 --- .../finality_violation/finality_violation.hpp | 4 - .../finality_violation.wasm | Bin 71386 -> 52101 bytes unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 74062 -> 74062 bytes 7 files changed, 58 insertions(+), 148 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 16130a4f83..fec8412704 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -263,32 +263,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) } - - BOOST_AUTO_TEST_CASE(contract_get_merkle_root) { try { - - //verify that get_merkle_root function from savanna contract produces the same results than calculate_merkle - std::vector digests = { digest_type{"cb7ea678fe3a84fcff103f9cf08b97e5dd0b01bee54635f49255050f34bdbf34"}, - digest_type{"ac2607856dea36137a1d6d0dd79b980f1ac7b104c59dc1e5fdd5f1568c7169c3"}, - digest_type{"a1af721189bdf59121043169b891a9e8fa545611be35eb95139bb1f2f409fcb3"}, - digest_type{"1afa2d6299822185c1f5332445f1fe304e12bf51a6221abdf75fcda6cdc38f56"}, - digest_type{"2dafbbd44d65e07ba48652cd5d83857765d19f93075ae2a9eb7749d2cdf1341d"} }; - - digest_type root = calculate_merkle(digests); - - tester c; - - c.create_accounts( {account_name("violation")} ); - - c.produce_block(); - - c.set_code( "violation"_n, test_contracts::finality_violation_wasm() ); - c.set_abi( "violation"_n, test_contracts::finality_violation_abi() ); - - c.push_action("violation"_n, "testmroot"_n, "eosio"_n, mvo()("root", root)("reversible_blocks_digests", digests)); - - - } FC_LOG_AND_RETHROW() } - BOOST_AUTO_TEST_CASE(cluster_vote_propagation_tests) { try { finality_proof::proof_test_cluster cluster_1; @@ -350,17 +324,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) ("memo", ""); //setup a light client data cache - - //data retained for every policy change - struct policy_sunset_data { - - finalizer_policy policy; - - ibc_block_data_t last_qc_block; - qc_data_t last_qc; - - }; - struct data_cache { //The data_cache stores data relevant to finality violation proofs. @@ -441,33 +404,17 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) } - // std::vector get_reversible_blocks_digests(){ - - // std::vector block_ref_digests; - - // for (int i = 0 ; i < reversible_blocks.size() - 1; i++){ - - // finality_block_data b = reversible_blocks[i]; - - // digest_type digest = compute_block_ref_digest(b); - - // block_ref_digests.push_back(digest); - - // } - - // return block_ref_digests; - - // } - std::vector get_reversible_blocks(){ std::vector result(reversible_blocks.begin(), reversible_blocks.end() - 1); return result; } - finality_block_data get_qc_block(){ + finality_block_data get_current_block(){ return reversible_blocks.back(); } + private: + //store the reversible blocks std::vector reversible_blocks; @@ -615,7 +562,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //verify the merkle root of the reversible blocks digests is the same as the one recorded by the light client BOOST_TEST(fake_chain_block_10_result.level_3_commitments.reversible_blocks_mroot == block_10_reversible_blocks_merkle_root); - auto proof_1_target_reversible_block = get_target_reversible_block(light_client_data.get_reversible_blocks(), real_chain_block_9_result.block->block_num()); + auto valid_proof_1_target_reversible_block = get_target_reversible_block(light_client_data.get_reversible_blocks(), real_chain_block_9_result.block->block_num()); //Real chain has a QC on #9 carried by #10, but fake chain doesn't BOOST_TEST(!fake_chain_block_10_result.qc_data.qc.has_value()); @@ -637,16 +584,42 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //this is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #10 - light_client_data.get_qc_block().qc_data.qc.value(), //QC over fake block #10 + light_client_data.get_current_block().qc_data.qc.value(), //QC over fake block #10 get_finality_block_data(real_chain_block_9_result), get_finality_block_data(real_chain_block_10_result).qc_data.qc.value(), - proof_1_target_reversible_block.first, - proof_1_target_reversible_block.second, + valid_proof_1_target_reversible_block.first, + valid_proof_1_target_reversible_block.second, block_10_reversible_blocks); + + //The contract correctly accepts the valid proof, as it is a proof of violation of rule #2 shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_1); - - std::cout << std::endl << "proof 1 passed" << std::endl << std::endl; + + //Now, to ensure the smart contract rejects invalid proofs, we can test providing a proof from the real chain where the high proof block is a descendant of the low proof block + mutable_variant_object invalid_rule_2_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, + get_finality_block_data(real_chain_block_10_result), //real block #10 + get_finality_block_data(real_chain_block_11_result).qc_data.qc.value(), //QC over real block #11 + get_finality_block_data(real_chain_block_9_result), + get_finality_block_data(real_chain_block_10_result).qc_data.qc.value(), + get_finality_block_data(real_chain_block_9_result), + 0, + {get_finality_block_data(real_chain_block_9_result)}); + + //The contract rejects the invalid proof + shouldFail(real_chain, "rule2"_n, invalid_rule_2_proof_1); + + //Now, to ensure the smart contract rejects invalid proofs, we can test providing a proof from the real chain where the high proof block is a descendant of the low proof block + mutable_variant_object invalid_rule_2_proof_2 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, + get_finality_block_data(real_chain_block_10_result), //real block #10 + get_finality_block_data(real_chain_block_11_result).qc_data.qc.value(), //QC over real block #11 + get_finality_block_data(real_chain_block_9_result), + get_finality_block_data(real_chain_block_10_result).qc_data.qc.value(), + valid_proof_1_target_reversible_block.first, + valid_proof_1_target_reversible_block.second, + block_10_reversible_blocks); + + //The contract rejects the invalid proof + shouldFail(real_chain, "rule2"_n, invalid_rule_2_proof_2); auto fake_chain_block_12_result = light_client_data.scan_block(fake_chain.produce_block()); auto real_chain_block_12_result = real_chain.produce_block(); @@ -678,10 +651,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) std::vector block_14_reversible_blocks = { get_finality_block_data(real_chain_block_12_result), get_finality_block_data(real_chain_block_13_result)}; - for (const auto& block : block_14_reversible_blocks){ - std::cout << "block # : " << block.block_num << " -> " << compute_block_ref_digest(block) << std::endl; - } - //calculate the merkle root of the reversible blocks digests digest_type block_14_reversible_blocks_merkle_root = calculate_reversible_blocks_merkle(block_14_reversible_blocks); @@ -700,7 +669,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Things are back to normal on the real chain, and we have a QC BOOST_TEST(real_chain_block_15_result.qc_data.qc.has_value()); - auto proof_2_target_reversible_block = get_target_reversible_block(block_14_reversible_blocks, block_14_reversible_blocks[block_14_reversible_blocks.size()-1].block_num); + auto valid_proof_2_target_reversible_block = get_target_reversible_block(block_14_reversible_blocks, block_14_reversible_blocks[block_14_reversible_blocks.size()-1].block_num); //We discovered a QC (over block #14) on the real chain, which was delivered via block #15. //Last QC recorded on the fake chain was over block #13, and was delivered by block #14 @@ -711,9 +680,9 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) get_finality_block_data(real_chain_block_14_result), get_finality_block_data(real_chain_block_15_result).qc_data.qc.value(), light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #13 - light_client_data.get_qc_block().qc_data.qc.value(), //QC over fake block #13 - proof_2_target_reversible_block.first, - proof_2_target_reversible_block.second, + light_client_data.get_current_block().qc_data.qc.value(), //QC over fake block #13 + valid_proof_2_target_reversible_block.first, + valid_proof_2_target_reversible_block.second, block_14_reversible_blocks); shouldPass(real_chain, "rule2"_n, valid_rule_2_proof_2); @@ -766,7 +735,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_18_result.level_3_commitments.latest_qc_claim_block_num == fake_chain_block_17_result.block->block_num()); BOOST_TEST(real_chain_block_19_result.level_3_commitments.latest_qc_claim_block_num == real_chain_block_16_result.block->block_num()); - auto proof_3_target_reversible_block = get_target_reversible_block(block_19_reversible_blocks, fake_chain_block_17_result.block->block_num()); + auto valid_proof_3_target_reversible_block = get_target_reversible_block(block_19_reversible_blocks, fake_chain_block_17_result.block->block_num()); //stop producing on fake chain, produce one more block on the real chain to get a QC on previous block auto real_chain_block_20_result = real_chain.produce_block(); @@ -779,12 +748,23 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) get_finality_block_data(real_chain_block_19_result), get_finality_block_data(real_chain_block_20_result).qc_data.qc.value(), light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #18 - light_client_data.get_qc_block().qc_data.qc.value(), //QC over fake block #18 - proof_3_target_reversible_block.first, - proof_3_target_reversible_block.second, + light_client_data.get_current_block().qc_data.qc.value(), //QC over fake block #18 + valid_proof_3_target_reversible_block.first, + valid_proof_3_target_reversible_block.second, block_19_reversible_blocks); shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1); + + mutable_variant_object invalid_rule_3_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, + get_finality_block_data(real_chain_block_19_result), + get_finality_block_data(real_chain_block_20_result).qc_data.qc.value(), + get_finality_block_data(real_chain_block_16_result), //real block #16 + get_finality_block_data(real_chain_block_17_result).qc_data.qc.value(), //QC over fake block #16 + get_finality_block_data(real_chain_block_16_result), + 0, + {get_finality_block_data(real_chain_block_16_result)}); + + shouldFail(real_chain, "rule3"_n, invalid_rule_3_proof_1); //Resume production on fake chain auto fake_chain_block_20_result = light_client_data.scan_block(fake_chain.produce_block()); @@ -847,17 +827,17 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) BOOST_TEST(fake_chain_block_25_result.qc_data.qc.has_value()); - auto proof_4_target_reversible_block = get_target_reversible_block(block_24_reversible_blocks, fake_chain_block_22_result.block->block_num()); + auto valid_proof_4_target_reversible_block = get_target_reversible_block(block_24_reversible_blocks, fake_chain_block_22_result.block->block_num()); //We can now produce a proof of finality violation demonstrating that finalizers were locked on #23 on the real chain, while also voting on a conflicting block #24 //on the fake chain which is not a descendant of #23, where the time range committed to by #23 is fully contained within the time range committed to by #24 mutable_variant_object valid_rule_3_proof_2 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #23 - light_client_data.get_qc_block().qc_data.qc.value(), //QC over fake block #23 + light_client_data.get_current_block().qc_data.qc.value(), //QC over fake block #23 get_finality_block_data(real_chain_block_23_result), get_finality_block_data(real_chain_block_24_result).qc_data.qc.value(), - proof_4_target_reversible_block.first, - proof_4_target_reversible_block.second, + valid_proof_4_target_reversible_block.first, + valid_proof_4_target_reversible_block.second, block_24_reversible_blocks); shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_2); diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index 9a459906cf..b9c5e1fd74 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -127,11 +127,6 @@ namespace savanna { checksum256 hash = target; std::vector proof_path = _get_proof_path(target_block_index, final_block_index+1); - print("target_block_index: ", target_block_index, "\n"); - print("final_block_index: ", final_block_index, "\n"); - print("proof_path size: ", proof_path.size(), "\n"); - print("proof_nodes size: ", proof_nodes.size(), "\n"); - check(proof_path.size() == proof_nodes.size(), "proof path size and proof nodes size mismatch"); for (int i = 0 ; i < proof_nodes.size() ; i++){ const checksum256 node = proof_nodes[i]; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 358d06b032..863ac2d24d 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -262,34 +262,6 @@ "type": "reversible_proof_of_inclusion" } ] - }, - { - "name": "testmroot", - "base": "", - "fields": [ - { - "name": "root", - "type": "checksum256" - }, - { - "name": "reversible_blocks_digests", - "type": "checksum256[]" - } - ] - }, - { - "name": "testpath", - "base": "", - "fields": [ - { - "name": "target_block_index", - "type": "uint32" - }, - { - "name": "final_block_index", - "type": "uint32" - } - ] } ], "actions": [ @@ -307,16 +279,6 @@ "name": "rule3", "type": "rule3", "ricardian_contract": "" - }, - { - "name": "testmroot", - "type": "testmroot", - "ricardian_contract": "" - }, - { - "name": "testpath", - "type": "testpath", - "ricardian_contract": "" } ], "tables": [], diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index c71691b251..8b5efa2a45 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -164,10 +164,6 @@ std::pair finality_violation::rule3( const finalizer //Verify that the proof of inclusion resolves to the reversible blocks mroot of the high proof check(proof_of_inclusion.root() == high_proof.qc_block.level_3_commitments.value().reversible_blocks_mroot, "proof of inclusion must resolve to the reversible blocks mroot of the high proof"); - print("target_proof_timestamp: ", target_proof_timestamp.to_string(), "\n"); - print("low_proof_last_claim_timestamp: ", low_proof_last_claim_timestamp.to_string(), "\n"); - print("target_proof_parent_timestamp: ", target_proof_parent_timestamp.to_string(), "\n"); - //A lock violation has occured if the high proof timestamp is greater than or equal to the low proof last claim timestamp and the high proof parent timestamp is less than the low proof last claim timestamp bool lock_violation = target_proof_timestamp >= low_proof_last_claim_timestamp && target_proof_parent_timestamp < low_proof_last_claim_timestamp; check(lock_violation, "proofs must demonstrate a lock violation"); @@ -191,22 +187,3 @@ std::pair finality_violation::rule3( const finalizer return {result.first.to_string(), result.second.to_string()}; } - -//For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation -ACTION finality_violation::testmroot(const checksum256& root, const std::vector& reversible_blocks_digests){ - checksum256 c_root = get_merkle_root(reversible_blocks_digests); - check(c_root == root, "invalid root"); -} - -//For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation -ACTION finality_violation::testpath(const uint32_t target_block_index, const uint32_t final_block_index){ - std::vector proof_path = _get_proof_path(target_block_index, final_block_index+1); - print("target_block_index: ", target_block_index, "\n"); - print("final_block_index: ", final_block_index, "\n"); - print("proof_path.size(): ", proof_path.size(), "\n"); - for(int i = 0; i < proof_path.size(); i++){ - print("proof_path[", i, "]: ", uint16_t(proof_path[i]), "\n"); - } - - //check(false, "expected failure"); -} \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 32f8edd057..df9784ef34 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -33,8 +33,4 @@ CONTRACT finality_violation : public contract { // compare two bitsets static std::pair compare_qc(const quorum_certificate_input& qc1, const quorum_certificate_input& qc2); - //For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation - ACTION testmroot(const checksum256& root, const std::vector& reversible_blocks_digests); - ACTION testpath(const uint32_t target_block_index, const uint32_t final_block_index); - }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 18e89be2d55823c08c77e21ff9a92ce04b47aaf1..f367c6b2468c1b65f26eec09d309908511f7b180 100755 GIT binary patch delta 9801 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 From c8fd55f81a91051b10b5cfcdf703fcaad479c0e1 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sat, 28 Dec 2024 06:32:30 -0600 Subject: [PATCH 74/75] Added comments --- unittests/svnn_finality_violation_tests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index fec8412704..f2e2261ce7 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -755,6 +755,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1); + //Now, to ensure the smart contract rejects invalid proofs, we can test providing a proof from the real chain where the high proof block is a descendant of the low proof block mutable_variant_object invalid_rule_3_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, get_finality_block_data(real_chain_block_19_result), get_finality_block_data(real_chain_block_20_result).qc_data.qc.value(), @@ -764,6 +765,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) 0, {get_finality_block_data(real_chain_block_16_result)}); + //The contract rejects the invalid proof shouldFail(real_chain, "rule3"_n, invalid_rule_3_proof_1); //Resume production on fake chain From 955aa879d13b28b9cd3fc0ba13ecd2eb618692a8 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sat, 28 Dec 2024 07:00:33 -0600 Subject: [PATCH 75/75] Adjusted comments --- unittests/svnn_finality_violation_tests.cpp | 24 +++++-------------- .../finality_violation/finality_violation.cpp | 1 + .../finality_violation/finality_violation.hpp | 3 +++ 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index f2e2261ce7..da95e44423 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -201,18 +201,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) const size_t target_reversible_block_index, const std::vector& reversible_blocks){ - std::cout << "target_reversible_block_index: " << target_reversible_block_index << std::endl; - std::cout << "reversible_blocks.size(): " << reversible_blocks.size() << std::endl; - - std::cout << "target_reversible_block: " << compute_block_ref_digest(target_reversible_block) << std::endl; - std::vector merkle_branches = finality_proof::generate_proof_of_inclusion(get_reversible_blocks_digests(reversible_blocks), target_reversible_block_index); - for(const auto& digest : merkle_branches){ - std::cout << "merkle branch: " << digest << std::endl; - } - - return mvo() ("finalizer_policy", active_finalizer_policy) ("high_proof", mvo() @@ -580,8 +570,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //Light client recorded the last QC (over block #10) on the fake chain, which was delivered via block #11. //Block #10 claims a QC over block #8. We provide fake block #10 and the QC over it, as well as the digests of the reversible blocks the light client recorded (block #8 and block #9). //We also provide the real block #9 and a QC over it, delivered via block #10. - //Since there is a time range conflict, and since the real block #9 finality digest doesn't appear in the list of reversible blocks digests committed to by the fake block QC, - //this is a proof of violation of rule #2. + //Since there is a time range conflict, and since the low proof block is not a descendant of the high proof block, this is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, light_client_data.get_reversible_blocks()[light_client_data.get_reversible_blocks().size()-1], //fake block #10 light_client_data.get_current_block().qc_data.qc.value(), //QC over fake block #10 @@ -608,7 +597,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //The contract rejects the invalid proof shouldFail(real_chain, "rule2"_n, invalid_rule_2_proof_1); - //Now, to ensure the smart contract rejects invalid proofs, we can test providing a proof from the real chain where the high proof block is a descendant of the low proof block + //Now, to ensure the smart contract rejects invalid proofs, we can test providing a proof from the real chain where the high proof block is a descendant of the + //low proof block mutable_variant_object invalid_rule_2_proof_2 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, get_finality_block_data(real_chain_block_10_result), //real block #10 get_finality_block_data(real_chain_block_11_result).qc_data.qc.value(), //QC over real block #11 @@ -657,8 +647,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //verify the merkle root of the reversible blocks digests is the same as the one recorded by the light client BOOST_TEST(real_chain_block_14_result.level_3_commitments.reversible_blocks_mroot == block_14_reversible_blocks_merkle_root); - std::cout << "block 14 reversible blocks merkle root: " << block_14_reversible_blocks_merkle_root << std::endl; - //Fake chain has a QC on #13 carried by #14, but real chain doesn't BOOST_TEST(fake_chain_block_14_result.qc_data.qc.has_value()); BOOST_TEST(!real_chain_block_14_result.qc_data.qc.has_value()); @@ -674,8 +662,7 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //We discovered a QC (over block #14) on the real chain, which was delivered via block #15. //Last QC recorded on the fake chain was over block #13, and was delivered by block #14 //We provide the real block #14 and its QC, the reversible digests at that time (block #12 and #13), as well as the fake block #13 and its QC. - //Since there is a time range conflict, and the fake block #13 finality digest doesn't appear in the list of the digests committed to by the real block QC, - //this is a proof of violation of rule #2. + //Since there is a time range conflict, and the low proof block is not a descendant of the high proof block, this is a proof of violation of rule #2. mutable_variant_object valid_rule_2_proof_2 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, get_finality_block_data(real_chain_block_14_result), get_finality_block_data(real_chain_block_15_result).qc_data.qc.value(), @@ -755,7 +742,8 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) shouldPass(real_chain, "rule3"_n, valid_rule_3_proof_1); - //Now, to ensure the smart contract rejects invalid proofs, we can test providing a proof from the real chain where the high proof block is a descendant of the low proof block + //Now, to ensure the smart contract rejects invalid proofs, we can test providing a proof from the real chain where the high proof block is a descendant of the + //low proof block mutable_variant_object invalid_rule_3_proof_1 = prepare_rule_2_3_proof( light_client_data.active_finalizer_policy, get_finality_block_data(real_chain_block_19_result), get_finality_block_data(real_chain_block_20_result).qc_data.qc.value(), diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 8b5efa2a45..79cea6dce5 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -182,6 +182,7 @@ std::pair finality_violation::rule3( const finalizer check(finality_violation, "proofs must demonstrate a finality violation"); + //Proof of rule #3 finality violation auto result = check_bitsets(finalizer_policy, high_proof, low_proof, true, false); return {result.first.to_string(), result.second.to_string()}; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index df9784ef34..92658af192 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -13,15 +13,18 @@ CONTRACT finality_violation : public contract { public: using contract::contract; + //Rule #1 : Do not vote on different blocks with the same timestamp [[eosio::action]] std::pair rule1( const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2); + //Rule #2 : Do not vote on a block that conflicts with the time interval of a strong vote [[eosio::action]] std::pair rule2( const finalizer_policy_input& finalizer_policy, const finality_proof& high_proof, const finality_proof& low_proof, const reversible_proof_of_inclusion& reversible_proof_of_inclusion); + //Rule #3 : Do not vote on a block that conflicts with another block on which you are locked [[eosio::action]] std::pair rule3( const finalizer_policy_input& finalizer_policy, const finality_proof& high_proof,

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 f^8JS!wx>j8&AhrzuS27pEdsmTN$EQ delta 13421 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 ZS|5L=<*CQp_q7gt>Z|RgFZ^fEe*?vU_BQ|k diff --git a/unittests/test-contracts/savanna/ibc/ibc.cpp b/unittests/test-contracts/savanna/ibc/ibc.cpp index 528836e506..980aa53e15 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.cpp +++ b/unittests/test-contracts/savanna/ibc/ibc.cpp @@ -123,7 +123,7 @@ void ibc::_check_finality_proof(const finality_proof& finality_proof, const bloc } -void ibc::_check_target_block_proof_of_inclusion(const block_proof_of_inclusion& proof, const std::optional reference_root){ +void ibc::_check_target_block_proof_of_inclusion(const block_proof_of_inclusion& proof, const std::optional& reference_root){ //resolve the proof to its merkle root checksum256 finality_mroot = proof.root(); @@ -174,7 +174,7 @@ ACTION ibc::checkproof(const proof& proof){ } -ACTION ibc::testbitset(const std::string bitset_string, const std::vector bitset_vector, const uint32_t finalizers_count){ +ACTION ibc::testbitset(const std::string& bitset_string, const std::vector& bitset_vector, const uint32_t finalizers_count){ savanna::bitset b(finalizers_count, bitset_vector); check(b.to_string() == bitset_string, "bitset mismatch"); diff --git a/unittests/test-contracts/savanna/ibc/ibc.hpp b/unittests/test-contracts/savanna/ibc/ibc.hpp index 3f535fe231..e4a95f2591 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.hpp +++ b/unittests/test-contracts/savanna/ibc/ibc.hpp @@ -60,11 +60,11 @@ CONTRACT ibc : public contract { finalizer_policy_input _get_stored_finalizer_policy(const uint64_t finalizer_policy_generation); void _check_finality_proof(const finality_proof& finality_proof, const block_proof_of_inclusion& target_block_proof_of_inclusion); - void _check_target_block_proof_of_inclusion(const block_proof_of_inclusion& proof, const std::optional reference_root); + void _check_target_block_proof_of_inclusion(const block_proof_of_inclusion& proof, const std::optional& reference_root); ACTION setfpolicy(const finalizer_policy_input& policy, const uint32_t from_block_num); //set finality policy ACTION checkproof(const proof& proof); - ACTION testbitset(const std::string bitset_string, const std::vector bitset_vector, const uint32_t finalizers_count); + ACTION testbitset(const std::string& bitset_string, const std::vector& bitset_vector, const uint32_t finalizers_count); }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/ibc/ibc.wasm b/unittests/test-contracts/savanna/ibc/ibc.wasm index 0632e258e3853f5d27f8dc170cd17c0978e74e6a..6e73897047cc84ecee2428ebaf4db94e248867f6 100755 GIT binary patch delta 6727 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 delta 8515 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 From f07308fd76dae81df5cff4ce52d6c65ddf805963 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 08:08:46 -0600 Subject: [PATCH 53/75] Replaced find_if with lower_bound to perform binary search in svnn_finality_violation_tests --- unittests/svnn_finality_violation_tests.cpp | 24 +++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 8b69812a86..625fcec0a1 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -337,17 +337,27 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) if (block_result.qc_data.qc.has_value()) { - auto high_qc_block_itr = std::find_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto& r) { - return r.block_num == block_result.qc_data.qc.value().to_qc_claim().block_num; - }); + auto high_qc_block_itr = std::lower_bound( + reversible_blocks.begin(), + reversible_blocks.end(), + block_result.qc_data.qc.value().to_qc_claim().block_num, + [](const auto& r, const auto& block_num) { + return r.block_num < block_num; + } + ); if ( high_qc_block_itr != reversible_blocks.end() && high_qc_block_itr->qc_data.qc.has_value()) { - auto last_final_block_itr = std::find_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto& r) { - return r.block_num == high_qc_block_itr->qc_data.qc.value().to_qc_claim().block_num; - }); - + auto last_final_block_itr = std::lower_bound( + reversible_blocks.begin(), + reversible_blocks.end(), + high_qc_block_itr->qc_data.qc.value().to_qc_claim().block_num, + [](const auto& r, const auto& block_num) { + return r.block_num < block_num; + } + ); + if ( last_final_block_itr != reversible_blocks.end() && block_result.qc_data.qc.value().to_qc_claim().is_strong_qc) { From 1e1f0e4b7b03e669732d35c8d5eccb941fd140b2 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 26 Aug 2024 08:59:56 -0600 Subject: [PATCH 54/75] bumped chainbase submodule --- libraries/chainbase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chainbase b/libraries/chainbase index 1dc531655d..8992c78bad 160000 --- a/libraries/chainbase +++ b/libraries/chainbase @@ -1 +1 @@ -Subproject commit 1dc531655da7366ae6c0816b7d0862fb6327fedb +Subproject commit 8992c78badf70b9bee716da2461e7e3278a3d5d6 From d715a3181d8342b0989dea1f60792d47abbc1c27 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sun, 1 Sep 2024 11:53:03 -0600 Subject: [PATCH 55/75] Replaced static functions with inline functions in finality_proof --- unittests/finality_proof.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 8857a03649..63786a2532 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -70,12 +70,12 @@ namespace finality_proof { return bitset_to_input_string(finalizers); }; - static digest_type hash_pair(const digest_type& a, const digest_type& b) { + inline digest_type hash_pair(const digest_type& a, const digest_type& b) { return fc::sha256::hash(std::pair(a, b)); } //generate a proof of inclusion for a node at index from a list of leaves - static std::vector generate_proof_of_inclusion(const std::vector& leaves, const size_t index) { + inline std::vector generate_proof_of_inclusion(const std::vector& leaves, const size_t index) { auto _leaves = leaves; auto _index = index; @@ -109,7 +109,7 @@ namespace finality_proof { } //extract instant finality data from block header extension, as well as qc data from block extension - static qc_data_t extract_qc_data(const signed_block_ptr& b) { + inline qc_data_t extract_qc_data(const signed_block_ptr& b) { assert(b); auto hexts = b->validate_and_extract_header_extensions(); if (auto f_entry = hexts.find(finality_extension::extension_id()); f_entry != hexts.end()) { @@ -126,7 +126,7 @@ namespace finality_proof { return {}; } - static bool has_finalizer_policy_diffs(const signed_block_ptr& block){ + inline bool has_finalizer_policy_diffs(const signed_block_ptr& block){ // extract new finalizer policy finality_extension f_ext = block->extract_header_extension(); @@ -135,7 +135,7 @@ namespace finality_proof { } - static finalizer_policy update_finalizer_policy(const signed_block_ptr block, const finalizer_policy& current_policy){ + inline finalizer_policy update_finalizer_policy(const signed_block_ptr block, const finalizer_policy& current_policy){ // extract new finalizer policy finality_extension f_ext = block->extract_header_extension(); From e85e5f8cc1b6942647ebdf84edf9844796f599c1 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sun, 1 Sep 2024 11:55:01 -0600 Subject: [PATCH 56/75] Removed default empty initializers in ibc_block_data_t --- unittests/finality_proof.hpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 63786a2532..5c8a032e08 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -11,21 +11,21 @@ namespace finality_proof { // data relevant to IBC struct ibc_block_data_t { signed_block_ptr block = nullptr; - qc_data_t qc_data{}; - action_trace onblock_trace{}; - finality_data_t finality_data{}; - finalizer_policy active_finalizer_policy{}; - finalizer_policy last_pending_finalizer_policy{}; - finalizer_policy last_proposed_finalizer_policy{}; - digest_type action_mroot{}; //this is the real action_mroot, as returned from finality_data - digest_type base_digest{}; - block_timestamp_type last_pending_finalizer_policy_start_timestamp{}; - digest_type finality_digest{}; - level_3_commitments_t level_3_commitments{}; - level_2_commitments_t level_2_commitments{}; - digest_type finality_leaf{}; - digest_type finality_root{}; - block_timestamp_type parent_timestamp{}; + qc_data_t qc_data; + action_trace onblock_trace; + finality_data_t finality_data; + finalizer_policy active_finalizer_policy; + finalizer_policy last_pending_finalizer_policy; + finalizer_policy last_proposed_finalizer_policy; + digest_type action_mroot; //this is the real action_mroot, as returned from finality_data + digest_type base_digest; + block_timestamp_type last_pending_finalizer_policy_start_timestamp; + digest_type finality_digest; + level_3_commitments_t level_3_commitments; + level_2_commitments_t level_2_commitments; + digest_type finality_leaf; + digest_type finality_root; + block_timestamp_type parent_timestamp; digest_type level_3_commitments_digest() const { return fc::sha256::hash(level_3_commitments); From 243c11ddf4aaeb0861fd3bf293f012c828770fd4 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sun, 1 Sep 2024 12:56:37 -0600 Subject: [PATCH 57/75] Simplified the pruning function in data cache struct --- unittests/svnn_finality_violation_tests.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 625fcec0a1..ddeb8dd324 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -364,11 +364,12 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //new strong QC last_final_block = *last_final_block_itr; last_final_qc = high_qc_block_itr->qc_data.qc.value(); - +/* reversible_blocks.erase(std::remove_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto &r){ return r.block_num <= last_final_block.block_num; - }), reversible_blocks.end()); + }), reversible_blocks.end());*/ + reversible_blocks.erase(reversible_blocks.begin(), ++last_final_block_itr); } From 8d8d4bae70811b50b0554c0205be609347f828e8 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sun, 1 Sep 2024 12:56:51 -0600 Subject: [PATCH 58/75] Removed commented out code --- unittests/svnn_finality_violation_tests.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index ddeb8dd324..5ff01b95b5 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -364,10 +364,6 @@ BOOST_AUTO_TEST_SUITE(svnn_finality_violation) //new strong QC last_final_block = *last_final_block_itr; last_final_qc = high_qc_block_itr->qc_data.qc.value(); -/* - reversible_blocks.erase(std::remove_if(reversible_blocks.begin(), reversible_blocks.end(), [&](auto &r){ - return r.block_num <= last_final_block.block_num; - }), reversible_blocks.end());*/ reversible_blocks.erase(reversible_blocks.begin(), ++last_final_block_itr); From 68c2c1d20b911b0f00a84aa1983502eb4c3914f6 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Tue, 3 Sep 2024 05:50:01 -0600 Subject: [PATCH 59/75] Moved finality digests comparison to the check_qcs function in finality_violation contract --- .../finality_violation/finality_violation.cpp | 8 ++------ .../finality_violation.wasm | Bin 50189 -> 49643 bytes 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 36cf4ee832..e3bafea1d4 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -11,6 +11,8 @@ void check_qcs( const finalizer_policy_input& finalizer_policy, const finality_p checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); + check(digest_1 != digest_2, "finality digests must be different"); + //Verify QC signatures over the finality digests _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); @@ -29,12 +31,6 @@ std::pair finality_violation::rule1(const finalizer_po check(timestamp_1 == timestamp_2, "proofs must be over blocks that have the same timestamp"); - //Verify that finality digests are different - checksum256 digest_1 = block_finality_data_internal(proof_1.qc_block).finality_digest(); - checksum256 digest_2 = block_finality_data_internal(proof_2.qc_block).finality_digest(); - - check(digest_1 != digest_2, "finality digests must be different"); - //Proof of rule #1 finality violation savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), proof_1.active_policy_qc.finalizers); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 082e25aada94753f81681545828aed7135ae4468..fa85f0d43753b199590333371cee1c230a25cc56 100755 GIT binary patch delta 607 zcmZvZze~eF6vtoQC26ph-k=aV9dSva>QdWI;uW-mpx~f_E`lys90YNVpamDF{0|C( zP{`=ef^#Jbf^OX$oWxbccS(OJxV*dje&5~ao0rQA_I}3N9}KVGqEGhFz@8P|uWsWy zE(um-G3p8}U0;&D(>kxiMh>*nPZUnBj8REfQ7i3}bPASh0+k4YNhNLb@wvQmtN8XhFp)lnsD!%2}{D6F@pb%Ca zs?4R6ORxA?(#3B|8KeJwfQ=|x@GPgFnaN-tm@37FDfJq_9p$962#E8{g6%eu8Kyq-6 zplqqd6+tXS0$X2YWnoBTVWoX4u@-!9_BPo<>}+n{eBZu#@B7}nlRbDjfy1A`KcAGp z;AoxycFRw99`bT@_k%e1HgvrF3e$&Bv>)jF+ZQICmh$v5GYaH(+sBw?yr;ZX)`b*r zsXlo#`~9Vm~2m5fgS!yx1P^K%zZy$eS9^2vpHNGTDfV1f!p@)qygSEAtdBD2{U z5i)Ke;p?8c$`~=ej!n#2B=Exfmg`s`P+}c)OAX1G0!q7&!2vYH5n8LW z%>PtJekMT9Cy=^4yt(%BeAx~0Q;ja0~~A=>hnAL_$xCV z#w6|+5utXUH!z_B?U*Q_Y0^^SB8sYHjy)srgPADA)u6H)&9WOD(XJOHJ-H@L?ggGuOjEUwPxv&8Nki PS+968yH Date: Tue, 3 Sep 2024 05:55:40 -0600 Subject: [PATCH 60/75] Fixed lock violation check --- .../finality_violation/finality_violation.cpp | 4 ++-- .../finality_violation.wasm | Bin 49643 -> 49648 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index e3bafea1d4..018066e859 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -98,8 +98,8 @@ std::pair finality_violation::rule3( const finalizer block_timestamp low_proof_last_claim_timestamp = low_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - //If the high proof timestamp is higher than the low proof timestamp, but the high proof last QC claim timestamp is lower than the low proof last QC claim, the lock was violated - bool lock_violation = high_proof_timestamp > low_proof_timestamp && high_proof_last_claim_timestamp < low_proof_timestamp; + //If the low proof timestamp is less than the high proof timestamp, but the high proof last QC claim timestamp is lower than or equal to the low proof last QC claim, the lock was violated + bool lock_violation = high_proof_last_claim_timestamp <= low_proof_last_claim_timestamp && low_proof_timestamp < high_proof_timestamp ; check(lock_violation, "proofs must demonstrate a lock violation"); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index fa85f0d43753b199590333371cee1c230a25cc56..c400f06dfbbe0cccf332a171ea6b2e5fe0f2597e 100755 GIT binary patch delta 586 zcmY*WJxc>Y5WU?!b9;9=Il)3iaC3!RAY#N2?aZptMo|Po!I)F9*`Sz05Ss`|TATX= z3U*ou`3e32Yr7N{f{of~<;>>dM|1P`&3m(RZ(p12t;z1+8M*6xu$>Bdh*$S#r%ZW6 zR7efM60q(ezUq-?0@gg@N<)4<#z&PJvErvn(BL8^QXy@zB7LENd67ORwF^~tsS=id zFq%M+Ua$a6Xc4^T&(jhfkSxQUdH{C((1ngA=hUQOV8@anZW!2bpe5bQsjvYI7xoGW z>>|x2JArus z_uyE}_;W8`=2>Ql!8T0i3Zd6-Ef|~nW?vI&?2TyXwp$w>b%g_rPyvUF($s~`;^XQ# z;qhg448FJ3#bF&%A+VP{gF!~pS0JKMHpL|YV>}(r3~}M;OcD(UPNRvw;5u5``VAFQ j>17IO=l_IV=<}xjD-6uUSA25UdBaC$#^swoF-OKS?wjnBXs*QUxP&oFgNZ?b1 Date: Tue, 3 Sep 2024 06:02:08 -0600 Subject: [PATCH 61/75] Added bool flag to check_qc to control whether or not we verify the quorum threshold has been met --- .../test-contracts/savanna/common/savanna.hpp | 6 +++--- .../finality_violation/finality_violation.cpp | 4 ++-- .../finality_violation.wasm | Bin 49648 -> 49657 bytes unittests/test-contracts/savanna/ibc/ibc.cpp | 4 ++-- unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 72732 -> 72741 bytes 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index bd59c11dd2..d7a6161dd8 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -141,7 +141,7 @@ namespace savanna { } //verify that the quorum certificate over the finality digest is valid - void _check_qc(const quorum_certificate_input& qc, const checksum256& finality_digest, const finalizer_policy_input& finalizer_policy){ + void _check_qc(const quorum_certificate_input& qc, const checksum256& finality_digest, const finalizer_policy_input& finalizer_policy, const bool enforce_threshold_check){ auto fa_itr = finalizer_policy.finalizers.begin(); auto fa_end_itr = finalizer_policy.finalizers.end(); size_t finalizer_count = finalizer_policy.finalizers.size(); @@ -168,8 +168,8 @@ namespace savanna { fa_itr++; } - //verify that we have enough vote weight to meet the quorum threshold of the target policy - check(weight>=finalizer_policy.threshold, "insufficient signatures to reach quorum"); + //if enforce_threshold_check is true, verify that we have enough vote weight to meet the quorum threshold of the target policy + if (enforce_threshold_check) check(weight>=finalizer_policy.threshold, "insufficient signatures to reach quorum"); std::string message; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 018066e859..83973d8dae 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -14,8 +14,8 @@ void check_qcs( const finalizer_policy_input& finalizer_policy, const finality_p check(digest_1 != digest_2, "finality digests must be different"); //Verify QC signatures over the finality digests - _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy); - _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy); + _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy, false); + _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy, false); } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index c400f06dfbbe0cccf332a171ea6b2e5fe0f2597e..09088a7a077cbfe2005426e008454bb41646187f 100755 GIT binary patch delta 1348 zcmbtUK~ED=5T5t;L6_Zbc!@wN!T5F|aeE+eFl|#v>}z5?ka$p|#-Ff&woobubSqu3 z2R#^0`UkvX8ly+z*@H&onV&#BX=22g_X?amacK9OneXj(y3X&ev;WXx<9m;s zZT7zZ(^;8IUwcBN>?!@GK_BqtNX&o|dY>nX>#-!}5Dh6Fa~TaU>v&axycgzpjn~wW z8om)g9YQzHe0pdWj%WxyhoZTd3;Kh|U)D}bc}oFk(8u3EGUE?@pVv~&l&5pemh$oG zw3AtY=Jfzff?-`CJMuLnpBmOvq$l(dDTDHuSz)+sd>4~2@1yo3NK{_=^0s-)$l$cl zUsz(J`Vgd;q_e5z_262Uom#TFnWI178M3e$K8 zC>Je>eL~GBaDhqcjfi=H(i&fhkDZXHGK$IARwI$BJX_VfPN#Dqur%`mtf@flih@OW z&XC2lYizf!dW*1*9=9H+2-ggpN7OW^f~bKgFvKpM4c=lG+;$aq$?~*BxMN`fVMfqJ zP8A-TI8ocD%ngHx*h0ielv}Utn*6(UR87K!Rga>*#VfqoCU(_ODS2 z6$t;ynL{z+zYcjTy)4Vh&Zf;HPxJgQO?tjOJt{n?Q}3Qx7$e-akowqRXCtJ{Jn%-z zj!==e%8+6=afbL-8S)loI5}gM`2r_r3dfZ$wu2XoS5J5Cn$>L>H*zK zL5PuqS-*o96EQvLk((DyO#A|T0z;jibC~6jM1pL3+YGk}?$F$qJ`?CN`dgL4ev%AW;~W%Vrnv_iCZP zvc%5nV~|dT)}|(kuEjFq4xx)VO?YJH zDYbW&xoL0MLNW!))lAb=(b8;FrpeM@M%Gc`l zCaS#K0Ec2#zSV$&r5*IYWD{Sa{}XuaWKSRMK~Kps@2~mkKivPH`P8i*^|eQDM4i*_ z0xQI8`13B9HkP-sD{XUuF3|I>WzF!(UZ(drTE_#G?VreqQt%mFK cH_ovy!%s^ecpz`$*P)N!2%&}t*H$-v0ZqKQd;kCd diff --git a/unittests/test-contracts/savanna/ibc/ibc.cpp b/unittests/test-contracts/savanna/ibc/ibc.cpp index 980aa53e15..b14add8bbc 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.cpp +++ b/unittests/test-contracts/savanna/ibc/ibc.cpp @@ -95,7 +95,7 @@ void ibc::_check_finality_proof(const finality_proof& finality_proof, const bloc finalizer_policy_input finalizer_policy = _get_stored_finalizer_policy(finality_proof.qc_block.active_finalizer_policy_generation); //verify QC. If QC is valid, it means that we have reached finality on the block referenced by the finality_mroot - _check_qc(finality_proof.active_policy_qc, block_finality_data_internal(finality_proof.qc_block).finality_digest(), finalizer_policy); + _check_qc(finality_proof.active_policy_qc, block_finality_data_internal(finality_proof.qc_block).finality_digest(), finalizer_policy, true); if (finality_proof.qc_block.pending_finalizer_policy_generation.has_value()){ @@ -105,7 +105,7 @@ void ibc::_check_finality_proof(const finality_proof& finality_proof, const bloc check(target.finality_data.last_pending_finalizer_policy.has_value(), "must provide pending finalizer policy for transition blocks"); - _check_qc(finality_proof.pending_policy_qc.value(), block_finality_data_internal(finality_proof.qc_block).finality_digest(), target.finality_data.last_pending_finalizer_policy.value()); + _check_qc(finality_proof.pending_policy_qc.value(), block_finality_data_internal(finality_proof.qc_block).finality_digest(), target.finality_data.last_pending_finalizer_policy.value(), true); _maybe_set_finalizer_policy(target.finality_data.last_pending_finalizer_policy.value(), target.dynamic_data.block_num); diff --git a/unittests/test-contracts/savanna/ibc/ibc.wasm b/unittests/test-contracts/savanna/ibc/ibc.wasm index 6e73897047cc84ecee2428ebaf4db94e248867f6..2b63ab17cffe41717d54d5a15474866a769ab296 100755 GIT binary patch delta 1342 zcmbtU&ubGw6n-;1Nw&LLoI@?KR64uuVRI<h&dO=Az$jV7%* zC?YvYy(owv2ra!7Pxa!-gV2N5{s{#y9>n_IOxm72^^olMe$2k_z4_k0-4`GB#m>77 zn;yQ;d}F`%?qz4Lr*AwVQuc&?GoTN6BNDTqgx=wa;(9!ZIYb%7V=kj?L&pyk$Yz-1 zHC|I0mAx529YQzHe0pdWj%f%zhoZTd3;Kh|Z)m5jyjuZi(60_bGUE?@pVv~&l&5p8 zw({}mbdouM){Ov6fvhf&9r>D(Pi6HK@dtz zHaacz50==dJ_0Ew>1=AeXrBcT$b=FmCK1{r)*&XTuuO^$D0c(fg>Z~mvn2Fr7&=ZO znh5o!93t#0UUb~srtzQOv2kJtC0olWF@#t@EW{kix3i=gCVQH z4Bi3KMN8tGkTVioWRhkhVqT!K##iFwr%BWq#pIIhMq;Y+Y*)M8ZudZ7Yvu*mQ-Q`6 z1yy*?FpFu|*lt}7s_+m!Zaq#Bt{OOxsAbRsq9&riB=+cR@DZorb}Fz%mZv4cO$!SM z`vhIgslX!>r_|0_=9)p2*ha*Nlv`ieGWECW$eM_sK(CLU!pL#7+(XN?*70<&PeFG; z*}pGQs6hBn&K!yn|8>Z_FzEODWoO;ik!N-OS0=q!odGo-)M<84EsPO9vykT4W#RYRNRCMn9u zrFAZGdb_~8K>?jEve`k|B8{3g0i90}%0xEIUY(e|vJ!5du6oSZo{jN}c!}Os^Tx-MAoEK+r@drg5AjF~KXd#sz|3Omfpz53Od<78j@d0RI{c$OhD>(`iVzJjZCHksC zRtM;vt$@A2$12-N2?hI%k2B0CE)g;oMRc>F@WA?}vN(!}`%lOWx%E|hO7N#z;TMs) zD1Ha+O>=E-y<{H-2QLpcqc2brLua5|!*>UO9(izHFC`&D z3+7B{R2{l407fbF8bDeaRfF{=_zvA_LdBsi(S%8t_((M1IKz|hBo~ViGp@r(v|!fK zB14mCmJBQk%|fYER{2^cNNr4T1t(d`6w}KtpefaauFu`!aJcVLv-SWZXq=>7QeJ`w zg0ch#S4fx|0WxwZ}XS(ZF@6O66KP03 zT~UWLhrf@*?zQgGn!0TREOUX*bjL32FON5Nw7XEP|2t>iZu_o7o_&FtQ?q)R4j;cb X(?22JjwV-k$DdZ8iSgx&`yYM-H@&}O From 1b7fb4f15ee0d260d1da8a115689cadb62152a36 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Tue, 3 Sep 2024 06:21:22 -0600 Subject: [PATCH 62/75] Renamed finalizers to strong_votes in savanna quorum_certificate_input --- unittests/svnn_finality_violation_tests.cpp | 8 ++++---- unittests/svnn_ibc_tests.cpp | 16 ++++++++-------- .../test-contracts/savanna/common/savanna.hpp | 5 +++-- .../finality_violation/finality_violation.abi | 2 +- .../finality_violation/finality_violation.cpp | 12 ++++++------ unittests/test-contracts/savanna/ibc/ibc.abi | 2 +- 6 files changed, 23 insertions(+), 22 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 5ff01b95b5..9b171ee0c8 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -72,7 +72,7 @@ mvo prepare_rule_1_proof( const finalizer_policy& active_finalizer_policy, ) ("active_policy_qc", mvo() ("signature", fake_qc.active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(fake_qc.active_policy_sig.strong_votes.value())) ) ) ("proof_2", mvo() @@ -89,7 +89,7 @@ mvo prepare_rule_1_proof( const finalizer_policy& active_finalizer_policy, ) ("active_policy_qc", mvo() ("signature", real_qc.active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(real_qc.active_policy_sig.strong_votes.value())) ) ); @@ -118,7 +118,7 @@ mvo prepare_rule_2_3_proof( const finalizer_policy& active_finalizer_policy, ) ("active_policy_qc", mvo() ("signature", high_qc.active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(high_qc.active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(high_qc.active_policy_sig.strong_votes.value())) ) ) ("low_proof", mvo() @@ -135,7 +135,7 @@ mvo prepare_rule_2_3_proof( const finalizer_policy& active_finalizer_policy, ) ("active_policy_qc", mvo() ("signature", low_qc.active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(low_qc.active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(low_qc.active_policy_sig.strong_votes.value())) ) ) ("reversible_blocks_digests", digests); diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 5a7029537b..45bfcd7eed 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -86,7 +86,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_4_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(block_4_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(block_4_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -125,7 +125,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_4_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(block_4_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(block_4_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -160,7 +160,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_5_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(block_5_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(block_5_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -360,7 +360,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_9_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(block_9_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(block_9_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -462,7 +462,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_11_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(block_11_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(block_11_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -521,11 +521,11 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_12_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(block_12_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(block_12_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ("pending_policy_qc", mvo() ("signature", block_12_result.qc_data.qc.value().pending_policy_sig.value().sig.to_string()) - ("finalizers", finality_proof::finalizers_string(block_12_result.qc_data.qc.value().pending_policy_sig.value().strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(block_12_result.qc_data.qc.value().pending_policy_sig.value().strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() @@ -574,7 +574,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ) ("active_policy_qc", mvo() ("signature", block_13_result.qc_data.qc.value().active_policy_sig.sig.to_string()) - ("finalizers", finality_proof::finalizers_string(block_13_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) + ("strong_votes", finality_proof::finalizers_string(block_13_result.qc_data.qc.value().active_policy_sig.strong_votes.value())) ) ) ("target_block_proof_of_inclusion", mvo() diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index d7a6161dd8..a65b035eca 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -24,7 +24,8 @@ namespace savanna { struct quorum_certificate_input { //representation of a bitset, where each bit represents the ordinal finalizer position according to canonical sorting rules of the finalizer policy - std::vector finalizers; + std::vector strong_votes; + //string representation of a BLS signature std::string signature; std::optional is_weak; @@ -145,7 +146,7 @@ namespace savanna { auto fa_itr = finalizer_policy.finalizers.begin(); auto fa_end_itr = finalizer_policy.finalizers.end(); size_t finalizer_count = finalizer_policy.finalizers.size(); - savanna::bitset b(finalizer_count, qc.finalizers); + savanna::bitset b(finalizer_count, qc.strong_votes); bool first = true; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index afad8c464e..128dd41b80 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -144,7 +144,7 @@ "base": "", "fields": [ { - "name": "finalizers", + "name": "strong_votes", "type": "bytes" }, { diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 83973d8dae..03b2e1b339 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -33,8 +33,8 @@ std::pair finality_violation::rule1(const finalizer_po //Proof of rule #1 finality violation - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), proof_1.active_policy_qc.finalizers); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), proof_2.active_policy_qc.finalizers); + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), proof_1.active_policy_qc.strong_votes); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), proof_2.active_policy_qc.strong_votes); auto result = bitset::compare(proof_1_bitset, proof_2_bitset); @@ -74,8 +74,8 @@ std::pair finality_violation::rule2( const finalizer //Proof of rule #2 finality violation - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.finalizers); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.finalizers); + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.strong_votes); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.strong_votes); auto result = bitset::compare(proof_1_bitset, proof_2_bitset); @@ -116,8 +116,8 @@ std::pair finality_violation::rule3( const finalizer //Proof of rule #3 finality violation - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.finalizers); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.finalizers); + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.strong_votes); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.strong_votes); auto result = bitset::compare(proof_1_bitset, proof_2_bitset); diff --git a/unittests/test-contracts/savanna/ibc/ibc.abi b/unittests/test-contracts/savanna/ibc/ibc.abi index 825afeb745..730d4c44db 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.abi +++ b/unittests/test-contracts/savanna/ibc/ibc.abi @@ -329,7 +329,7 @@ "base": "", "fields": [ { - "name": "finalizers", + "name": "strong_votes", "type": "bytes" }, { From eca93521f02f36e405bc62a9d8d8cb4c7523e6da Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sun, 22 Sep 2024 07:51:52 -0600 Subject: [PATCH 63/75] Modified _check_qc function to accomodate optional weak QC verification --- .../test-contracts/savanna/common/savanna.hpp | 131 ++++++++++++++++-- .../finality_violation/finality_violation.abi | 10 +- .../finality_violation/finality_violation.cpp | 22 +-- .../finality_violation.wasm | Bin 49657 -> 51727 bytes unittests/test-contracts/savanna/ibc/ibc.abi | 10 +- unittests/test-contracts/savanna/ibc/ibc.cpp | 4 +- unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 72741 -> 74058 bytes 7 files changed, 147 insertions(+), 30 deletions(-) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index a65b035eca..80d8a06860 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -22,13 +22,18 @@ namespace savanna { return std::string(res.begin(), res.end()); } + inline std::string create_strong_digest(const checksum256& digest){ + std::array fd_data = digest.extract_as_byte_array(); + return std::string(fd_data.begin(), fd_data.end()); + } + struct quorum_certificate_input { - //representation of a bitset, where each bit represents the ordinal finalizer position according to canonical sorting rules of the finalizer policy - std::vector strong_votes; + //representation of optional strong and weak bitsets, where each bit represents the ordinal finalizer position according to canonical sorting rules of the finalizer policy + std::optional> strong_votes; + std::optional> weak_votes; - //string representation of a BLS signature - std::string signature; - std::optional is_weak; + //string representation of a BLS signature + std::string signature; }; struct finalizer_authority_internal { @@ -140,13 +145,118 @@ namespace savanna { bool _verify(const std::string& public_key, const std::string& signature, const std::string& message){ return bls_signature_verify(decode_bls_public_key_to_g1(public_key), decode_bls_signature_to_g2(signature), message); } - - //verify that the quorum certificate over the finality digest is valid - void _check_qc(const quorum_certificate_input& qc, const checksum256& finality_digest, const finalizer_policy_input& finalizer_policy, const bool enforce_threshold_check){ + + void check_duplicate_votes(const savanna::bitset& strong_votes, const savanna::bitset& weak_votes, const finalizer_policy_input& finalizer_policy){ + auto fa_itr = finalizer_policy.finalizers.begin(); auto fa_end_itr = finalizer_policy.finalizers.end(); + + size_t index = 0; + + while (fa_itr != fa_end_itr){ + bool voted_strong = strong_votes.test(index); + bool voted_weak = weak_votes.test(index); + check (!(voted_strong && voted_weak), "conflicting finalizer votes detected in QC"); + index++; + fa_itr++; + } + + } + + uint64_t aggregate_keys(const savanna::bitset& votes, const finalizer_policy_input& finalizer_policy, bls_g1& agg_pub_key){ + + auto fa_itr = finalizer_policy.finalizers.begin(); + auto fa_end_itr = finalizer_policy.finalizers.end(); + + bool first = true; + + size_t index = 0; + uint64_t weight = 0; + + while (fa_itr != fa_end_itr){ + if (votes.test(index)){ + bls_g1 pub_key = decode_bls_public_key_to_g1(fa_itr->public_key); + if (first){ + first=false; + agg_pub_key = pub_key; + } + else agg_pub_key = _g1add(agg_pub_key, pub_key); + weight+=fa_itr->weight; + } + index++; + fa_itr++; + } + + return weight; + + } + + void check_message(const std::string& message, const bls_g1& agg_pub_key, const std::string& agg_sig){ + + std::string s_agg_pub_key = encode_g1_to_bls_public_key(agg_pub_key); + + //verify signature validity + check(_verify(s_agg_pub_key, agg_sig, message), "signature verification failed"); + + } + + //verify that the quorum certificate over the finality digest is valid + void _check_qc(const quorum_certificate_input& qc, const checksum256& finality_digest, const finalizer_policy_input& finalizer_policy, const bool count_strong_only, const bool enforce_threshold_check){ + + check(qc.strong_votes.has_value() || qc.weak_votes.has_value(), "quorum certificate must have at least one bitset"); + + size_t finalizer_count = finalizer_policy.finalizers.size(); + + uint64_t weight = 0; + + if (count_strong_only){ + bls_g1 agg_pub_key; + check(qc.strong_votes.has_value(), "required strong votes are missing"); + savanna::bitset strong_b(finalizer_count, qc.strong_votes.value()); + weight = aggregate_keys(strong_b, finalizer_policy, agg_pub_key); + check_message(create_strong_digest(finality_digest), agg_pub_key, qc.signature); + } + else { + + if (qc.strong_votes.has_value() && qc.weak_votes.has_value()){ + bls_g1 strong_agg_pub_key; + bls_g1 weak_agg_pub_key; + savanna::bitset strong_b(finalizer_count, qc.strong_votes.value()); + savanna::bitset weak_b(finalizer_count, qc.weak_votes.value()); + check_duplicate_votes(strong_b, weak_b, finalizer_policy); + uint64_t strong_weight = aggregate_keys(strong_b, finalizer_policy, strong_agg_pub_key); + uint64_t weak_weight = aggregate_keys(strong_b, finalizer_policy, weak_agg_pub_key); + check_message(create_strong_digest(finality_digest), strong_agg_pub_key, qc.signature); + check_message(create_weak_digest(finality_digest), weak_agg_pub_key, qc.signature); + weight=strong_weight+weak_weight; + } + else if (qc.weak_votes.has_value()){ + bls_g1 agg_pub_key; + savanna::bitset weak_b(finalizer_count, qc.weak_votes.value()); + weight = aggregate_keys(weak_b, finalizer_policy, agg_pub_key); + check_message(create_weak_digest(finality_digest), agg_pub_key, qc.signature); + } + else { + bls_g1 agg_pub_key; + savanna::bitset strong_b(finalizer_count, qc.strong_votes.value()); + weight = aggregate_keys(strong_b, finalizer_policy, agg_pub_key); + check_message(create_strong_digest(finality_digest), agg_pub_key, qc.signature); + } + + } + + if (enforce_threshold_check) check(weight>=finalizer_policy.threshold, "insufficient signatures to reach quorum"); + +/* size_t finalizer_count = finalizer_policy.finalizers.size(); - savanna::bitset b(finalizer_count, qc.strong_votes); + check(qc.strong_votes.has_value() || qc.weak_votes.has_value(), "quorum certificate must have at least one bitset"); + + auto fa_itr = finalizer_policy.finalizers.begin(); + auto fa_end_itr = finalizer_policy.finalizers.end(); + + //todo : check weak_votes as well + + savanna::bitset strong_b(finalizer_count, qc.strong_votes.value()); bool first = true; @@ -156,7 +266,7 @@ namespace savanna { bls_g1 agg_pub_key; while (fa_itr != fa_end_itr){ - if (b.test(index)){ + if (strong_b.test(index)){ bls_g1 pub_key = decode_bls_public_key_to_g1(fa_itr->public_key); if (first){ first=false; @@ -183,6 +293,7 @@ namespace savanna { std::string s_agg_pub_key = encode_g1_to_bls_public_key(agg_pub_key); //verify signature validity check(_verify(s_agg_pub_key, qc.signature, message), "signature verification failed"); + */ } checksum256 get_merkle_root(const std::vector& leaves) { diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 128dd41b80..65df7e736c 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -145,15 +145,15 @@ "fields": [ { "name": "strong_votes", - "type": "bytes" + "type": "bytes?" }, { - "name": "signature", - "type": "string" + "name": "weak_votes", + "type": "bytes?" }, { - "name": "is_weak", - "type": "bool?" + "name": "signature", + "type": "string" } ] }, diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 03b2e1b339..bbb94ca44b 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -14,8 +14,8 @@ void check_qcs( const finalizer_policy_input& finalizer_policy, const finality_p check(digest_1 != digest_2, "finality digests must be different"); //Verify QC signatures over the finality digests - _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy, false); - _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy, false); + _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy, false, false); + _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy, false, false); } @@ -33,8 +33,10 @@ std::pair finality_violation::rule1(const finalizer_po //Proof of rule #1 finality violation - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), proof_1.active_policy_qc.strong_votes); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), proof_2.active_policy_qc.strong_votes); + //todo : check bitsets + + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), proof_1.active_policy_qc.strong_votes.value()); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), proof_2.active_policy_qc.strong_votes.value()); auto result = bitset::compare(proof_1_bitset, proof_2_bitset); @@ -74,8 +76,10 @@ std::pair finality_violation::rule2( const finalizer //Proof of rule #2 finality violation - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.strong_votes); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.strong_votes); + //todo : check bitsets + + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.strong_votes.value()); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.strong_votes.value()); auto result = bitset::compare(proof_1_bitset, proof_2_bitset); @@ -116,8 +120,10 @@ std::pair finality_violation::rule3( const finalizer //Proof of rule #3 finality violation - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.strong_votes); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.strong_votes); + //todo : check bitsets + + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.strong_votes.value()); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.strong_votes.value()); auto result = bitset::compare(proof_1_bitset, proof_2_bitset); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 09088a7a077cbfe2005426e008454bb41646187f..92297b30c875893694317b0623c19c2693ef2931 100755 GIT binary patch delta 9282 zcmeHNdvI09dEec0?n7LGj>jN`jPc%UL@r1&_;rOZ;BZ862nGj?V<5IM0*P4nA_*iR zYzK41TS+jMv$9QH3?iX`Q$mCRq<7w%`DXp2Izi;>4 zqkC;gI@3w}k2S;Ye!Ji9e*61&zsI@!=N9X!KC6^9bhL2B7(dL%JYA~7mgjkV?7%n^UKFe4as>`Iy^&<{8hqKORA|2SeQXI$Ft$8F3b4Elo1uXIPwjwIIKKiv*$J zT9)(y3nj%AC+T)Q<(gHk`z#`DQ98XyV5j zFf57t5SGK-C7Hx9a~bgHaT+K>>%r1gl86!Lj^=ejiZfhBDMXW25+$1J3TnAnpn+}? zC?@|npcjYK67uw=;I6X##AWJ5gSl?r;2`nHiK2wB`VeX$Zo9lL$y=(i9cmHh2MB?Fvl z0+?@)-rIai8%K1&z|CN>qqfC8u`xtWNoErQ z7FWO^SCENLSku{PkXF6yna-}&15rI-qvuc}lxC!QyFLjboMu83Q$R%0jB8>lh-eyb z0jnHJl(^HPU1?Shs>F5B8l_tmXpJOxaHJUyfl=Zk>udNz)KbwK|J9?X24BJSbLqD0 z1Z=a!(+hAU8yX z_RkO)BEnCF5eF1<;(5c8x`UsYV4Devi1Ud0$>Oh!I?XaQj5$-m48Lc&c9(nNw0deu zxvE_uqs+fqmHX7%g?PpB~uelq60s0evB zMIgOaHWAj3#geu|XZ>>7OYdm-!|oKgA?HE$M#DX2zb@#rp}BJp&4sqAm)*x#44lyH zg|LL(ZtH5WloY|DHzDtf8&M2n9u*s?T+>(`jZjc=Tf$P8A1<#7APU2D!^3Kb@~~MF zVO=H>QC{P%s%3d3Wg$YaW&-^P)Rq|LmaPS#d2VvhSDyaQjbBp#w0zd|5{d#Mr0xQXYf?#8+XMth*1rq+SD|v^Z^$8IL2+V*8}SH4RgIqAvh>IB`)e(hB7J94d39N z4Q22QT$X}~wCY)%bsh{UEXQ_q{8HxFpdyytOx=Aw~P#0me{e_|+|2-g6;sV<4 z1+p?8Se+I_k)0!JNr5Q_-CwXGa%vIf6j5*R49S+Cd7_v+TTGrWAU*7Jp@0U7;G}S^ zv*;=i>M)TS6fGm%W5O4#cPEAtI3rr9@3Y!g!`cQj?w@CVG4@Tmec3RB3xk zj18zLsEUBHK^gsH8LfZ1yq4P&&N!_`ER?hb+2*!NzbiDH=^- z9hHbNtfV*}N(dMiG1HJAsn;^gMxPJGZA_3NB|bt{Ze|aJ)h*dt#0ATGPghSs>sPC1p{#0gy&hgkUnoXWw(oq4Q@cX9zpdL8XL08{ zx{Y89?VSjgaA<^A$AqzWjvt?r^2;DEmWWbeCoFt3K%n!h)9|ReCyqp3xMhSJr<9GU zL#@{?Eh953==DdXFmE2T^E3w-SdaTzH8#F$IG3n^C>6HzT~3idLO~sAVya-6Ktq~I zeYLGN>^C>Jjj!fuRtf@>uQdb`cF-XCs<;}2Q*bXNi~R?;Lii%2y`iHoK8hbnVE#7Kz`cX*3KV&ap<5NA>x5ni5eppZ{*2!n_1 zgAHK~gZIkF`We}tKPbnS1`z!2(4SbYm{~!2$&j6f77g1N)TsFf>!!KA0wdAqp0@ z1|-4}8lb=y!Gbiw0;s}Zfyjbjfykj?fhZV9eT(%vZb7g>g9X6?kp;m5kp;m*w+Ff) zSfJs8U_nm~ygNx;=(C6{a$3|;;I)W^>V|>^>hS#*!Wsf|Fdr-Y z<2m>=8b9I(3*A)1)%i|J!|%H&+C_Uim~#WO@pjV#QhU3pn)HZUySv+hVRCmS(4awb zcP7ptih>0bW)MZ*&gdVM)<0z*mb=>z7PQyWumui}y8K{)n)zU1h*P^fmGz2(1z7|< zs3P1!7r+jx2zAgt=&H8+R_`r8Sm^hI1rzLG|9r3uehuG`jZ+7ygN5S(so@(4NR8f+ zfF$$|2Na>_hZ$H%2Md0f0g~YJ6(})_^heO4glngG!Jq=ptHI~T3jv=7k32eTJz_e# zIf&r>b+Bkx0WFAfD0NLBn`Vo;rbbQ(XS#y{4^*Q>tA?uNBG_t-XmCb#PG1>$p~ z^|{fanEZ32g~tiA|GCjBGFsXHH>36K&D&t8oGn{ny7uR;Qv+M}ly#8-hF8#56<_@x z*m{T9|8DNtty#{$sl4sis~fgItXoumuzkKRZ&u58j796_9TiIL_=+AMr#{*7McsE@ z*zL#9DI0PR_H^3ld3R^KE-TftCsWsb6QBKH@#(`{c%n6@zT4hVXU)NnhnVoACl}lN z-CX3mDcb!`)rm5vsv-3UYOXHFsX2X=s(qjm>N|bI_^#ZueOV#8xf0L)jo8Fzu8;Ex2hZWb?GrO@E`80j&8vZ{`4cb${tSu#9hZL)&2cn{uKboZ3R;$GaX>0f8@#^OX={&nyZ&!&!3wVp_IF!aIjvjiF zBH!sR(lK|uxE|%@7foIAa$;b@iVQAdsfwcOiBPl%$~#jAZ}gyl+oa@-d;=e;tC=N7~(x%WQ)YXou; zolwzFxC#7DfeOj1L=w&=k{?PuQBmTFf)dX%okbyAqAX%4F(}9=8HI|a^otH6089jp zG!^W$f?f;xBo?6GE4+Eb1vU`)!4lCtc=49#%MTK1wdw9_J)C+D#G&7HOyaE5X-S`- z@JRsa3uU(XACKOle3iCCr>P*MK3zVQ{v8s1Mj)?9BJ&GZaEgSDbL8biwyhI;BOe`M zNIoNrgUZi-2@CTBI%S<&C>r%YkLpqLp9MT@MxPmUa!O#~$Mif{nBjv}K`k=N;MN8H z|Hf5B!|xjqZKMy~SgH|waTLzY$hh-Jq8riJ3}(k-Z;#3y`I9?BlUBC1u3Fu^va`AM z8*){1Ys2d1$D7(^S6gROhiq)>Y+Bjb)F_)<<-%F2^tBuAUAwNWeO-%O+0@?IysCL+ zLuZq0S=Z4iA8F`nk`0}5byEYVw$>)OqPer9sgt!gtzFmLjx{aNU U8#~k=4vxvaGdO|UgVl`vHzCrcfB*mh delta 7347 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 diff --git a/unittests/test-contracts/savanna/ibc/ibc.abi b/unittests/test-contracts/savanna/ibc/ibc.abi index 730d4c44db..9f6405eb90 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.abi +++ b/unittests/test-contracts/savanna/ibc/ibc.abi @@ -330,15 +330,15 @@ "fields": [ { "name": "strong_votes", - "type": "bytes" + "type": "bytes?" }, { - "name": "signature", - "type": "string" + "name": "weak_votes", + "type": "bytes?" }, { - "name": "is_weak", - "type": "bool?" + "name": "signature", + "type": "string" } ] }, diff --git a/unittests/test-contracts/savanna/ibc/ibc.cpp b/unittests/test-contracts/savanna/ibc/ibc.cpp index b14add8bbc..3c49d963a0 100644 --- a/unittests/test-contracts/savanna/ibc/ibc.cpp +++ b/unittests/test-contracts/savanna/ibc/ibc.cpp @@ -95,7 +95,7 @@ void ibc::_check_finality_proof(const finality_proof& finality_proof, const bloc finalizer_policy_input finalizer_policy = _get_stored_finalizer_policy(finality_proof.qc_block.active_finalizer_policy_generation); //verify QC. If QC is valid, it means that we have reached finality on the block referenced by the finality_mroot - _check_qc(finality_proof.active_policy_qc, block_finality_data_internal(finality_proof.qc_block).finality_digest(), finalizer_policy, true); + _check_qc(finality_proof.active_policy_qc, block_finality_data_internal(finality_proof.qc_block).finality_digest(), finalizer_policy, true, true); if (finality_proof.qc_block.pending_finalizer_policy_generation.has_value()){ @@ -105,7 +105,7 @@ void ibc::_check_finality_proof(const finality_proof& finality_proof, const bloc check(target.finality_data.last_pending_finalizer_policy.has_value(), "must provide pending finalizer policy for transition blocks"); - _check_qc(finality_proof.pending_policy_qc.value(), block_finality_data_internal(finality_proof.qc_block).finality_digest(), target.finality_data.last_pending_finalizer_policy.value(), true); + _check_qc(finality_proof.pending_policy_qc.value(), block_finality_data_internal(finality_proof.qc_block).finality_digest(), target.finality_data.last_pending_finalizer_policy.value(), true, true); _maybe_set_finalizer_policy(target.finality_data.last_pending_finalizer_policy.value(), target.dynamic_data.block_num); diff --git a/unittests/test-contracts/savanna/ibc/ibc.wasm b/unittests/test-contracts/savanna/ibc/ibc.wasm index 2b63ab17cffe41717d54d5a15474866a769ab296..869e46c8c5dded1143f64bcc35708c8d61d199e0 100755 GIT binary patch delta 8091 zcmcIp33yaRw!WwC?M{bI;Ep0o2;ue>uz|3M(jchhvN-}q!8gk2_}C1X4iHE}P#|>J z1lf#~+Q=eNK?P9>SGFhP0B(#MQ9u&5VHjmVMRaro`EZ>7)a@+r#`)g&y>IwP-BVSk zs?L5+Rj=J5F^XdEHYQC@#@{~ zxzDNw)(t=Be&VleMW4FQwG9e-%Y8N6#h_2*@k|G;-Eju(lh1VQiMFC^7n#zjP(hYk z-f4>fm>Fp;r(JskP&Z%83(j3zf5CGevMldZ=$^W+ueTtNUUZj5zSm=~37rL|$FA0# zos?(mtu>J)vMdS<(>5$}8b`xIvwgRuYJIejKZSz)aVV7JXd%)WRSOG)8_f>nIeL*r zgHkm|bI$G4Oj6^aKu}~kgw2vB6YJIA%Cm23sfaf;@o#A@NqVAeF!c%1-PJEj1$Nt(rI-4#v{ z$C%M}x@P&OgE=jf6axL14BS~e*8gvlqm}|LG;jf~lr+jegfy5EB~MF0ym?M|e8|s~V~Gy7Yga;gA2Yt%sDG*+w0zEa zidue4GYf1#DEB$bC>k^KLrDPVV~Wf|K;fu2V@||gs;fbM9*UW#2$`h_;pvWAI3X}! zSadOOf|7V}J~&rOz-Sp^*z&V(K_GzuJpyIx=1nYCIDld>q$?JfYgV4_{LC9bc8W4Z zbVz4j(!mQxn<-8pakC&4z@aP*<|-^2+sOm4L$c-13pIcq{K~*|HZU}rovaMZKgct% zn6faQfHlCr{A^!#ke9>4>=YLMq);kO2=ESQF5f|Iz~k5eXsJv-ulJUs2TdIOKxqy( z9}W4f2F|dk-M}-3SdAi-9(2_UN6~2rCIdA(VZ|wRC6vN_){331u>OTw&ezjGsi;*< zjdMURCac94Q$|6KfSF@2Z0v{vXZ?Q7F)-qEF>kbC%ssW%&>I$=r-%mAwS4WwMe42@ z9ymvI*HS!i5x~YC_&C6H%h#u(w6t`m(OnO~5&RKhddHvzpA%ycnP-^)PgXn)X||u` zAK-&PoX59PyBgQ)#;t}{()3VLEJd(ODr;kxV9CPTlwsMPI+hXZgPjDfQ7ftNzZX=v zR!*6RWA*q;p6XObn_>4AHcFMa1lK_6lz!f2zQStQr?ULISzU%40t5a5Pt!B-5OVq%7m zlaiHZ3q)K2?KR^NZR4tCW7Qjd!sz&WYC?6opp(>7 zBh~4FPM{}z0lU0lCp*0Y#XYqq=p{QQpsN(r3{Yc62S!f~OkkGrIQn{cK8ED7Hh+t; zt07lt%3Vs0?gFvjqKH7MvlKmuO-u<{iUzXO=^&;v8NJ|$fZCD2JN8yk!|qx`hQib& z0=g7lS}!e)Aq3H$@JwRAw!DFs&3kGsJ*pvLnyJYjoe62hX>bxPB2r*8 zy3-}fbm@ps<`g-z&kciqc^d1&A|(VV0l!Ma!VyraP?|hdrBufIK@2Jrn-5A~-@){d z9NIUVs^v3%@1d#kV&5W~=1%IjK+pksalkx!NzS=t22GRtug}wdX$@=EiIuOibXa79 zF%TBEg%ca7&iO218mf@6gjp>6+_)My=QNKN6>tkJ2byrNkS6+6i3a#5E8GmQ;V82kRXr-EV?}v&cxC|V}dm^CR6Xs z06Up?9u%eI8viwx#7vUoelv%@kl+4h8rT|t*G_sxcDlQSCpO$&=<$X2hG8wu%jhqW zV}^ZChvdD#wapT{L0I&#l92L{XI3AQi+}qxeJV%nwq^D`T~H0i?#U3pTJcdrK8h-W z1YY^w5IVHD{=>cWttmEP{;v-{cnKeGJ=840 zkv^57Q0yAncWP5LmLW671{y(E=2%Gh8~z5j$g-(J>7e|})CbTGp0O2I?cj5e2?``!%ijJ7fm^Uh63P|k>zq!*{bpuYEX z3%h|=Oh%wyN@%3pv^1OOak*gjHS~mBGP5<{gR|QMJ~p#$#$mP|+$C-dX>1lfqUMu? zIL5A~nQ=K_PG@(>tR4i>^ZKXcrrE6l^ZKWLVg2&D^())~bGi|nY5H8-EVkjm;d;*W z%G@cKYPhHw+TIV}ONZr(hsOfWp3lfA`ofmK*7Lf}%K}32yo0}lP&)tSCd|y`QD^ND zd3?z%I^y2_2*j{QKEGfGw(z_g1l9NMCl)@4DUYCQ7LCSv3l}j#-!1BlPym8@)FY_t zVh0FCiJwRh7s%$zq1V#*o^xDn)GTT~!+{tX2P)X+x3QY(;N+?xOK+!dltoW=j+OIG5mUHCDFQ5? zIa{SqCBV`ojDiWZH&A@&;s|rT%4#?UPRnaenb3K;-}b2unZ7l+9MqQGWteedd>zA% z?eycry6f0ArcP#sFy%f+6!G=^R|4*>J19uYaG|yIj?kn>CX47&o>+VaMd26d;0-M@5zqX&qyEHSP{ZB+k@ydH8}Qbf%6Iw=JaF~uK1%{8ho9OP0v*?gbUL>A;n9kt_f;8UTffz-=( z%oVehWPU(^paUu8}v{_nNFEF%`k1cP9i1GUJ+hd1tii-Ehw&r1&!XrBAoPM?1DnV1d z4^+*Nczn>D{emsaGe^zYlPD-OEZd=GY+?(;s})E^tqpehQmi@K>M+A%gNW7OZLE){ z4g|lDRPBKA$1Bqytglu^Xr;V*)xGq*8(Z}vL9oXfP@3#lK2*FwGG5*fNJq+B%b!=b zubYe00ryDx96{^k$mdGXI%`|We>~?Tpy6ve*FsmyU2D?OD_`4Oo?COP8vk$A57Bye z_u9z@Ivduvkaw;hrl28W9m$IIO?AJy-fjNUFoP=Hf>$Gi$#okiqit2u3N-buxCw1Z zMOWG&H&!$SyoX2PI|7s;yHpu6yeZQoc$j>7Q&W0bzP+g|9U@^Z)H=N{&q=CHJCoc@ zTH`+L?&50$@=g*zMUwfdT(qSPMmBA^3$6L}Tx|PoGVPW_wzWq+Gkx1Y)n>@gw^<6Z z-SGBeL9Sf=`eAy_owj2Op}_0<#s!@=%Z|IdauKwY2ISd~ zxCrB3+r13+-phq<$=>_WhAM6O)84BUlu5f}cI9xut*YxNS)lsY$!#CA5UM|pfMxTO zG@^NKhkd7r-gX-w_yZp|;vue_x$?WBs;mAk+47DVSE0uh_16_B6j+EgD^7H@}c98nJh&aHq+2@l=_A)PG|mc4xtE7_fv_0ibWL ztqY*)0TYMgI%VO*aSM%MR+0SrXj4DxCk)Bpv5qz1&Bt1gL0C*dRICjVxajlCjEYAd z8x}up5I=3aDG>a?PnUZ9bpF5a(^;;Lw}^H2IKTr|BGIMSWItxdV&-~kruRwlKFSso zse`Gp_1X>|Aq`F@A`28CePr_PVB1RvT#R@So{$aXg%pZ`3rttnWP?n;4&qJ1N(52J zAnZkKeyC8i^4qS8)co{aFHi$P3Hz7C)&<(PwBXyi&QI)k4g!rrzJKgi-0ZLVQ$F2% z7(d5&qlhBd%!b(ULFr%#etiZftVMiSi)dJjjTFHkOlF+OpcHx2i92F=wuk-w8tXVM zdnTi*4vQ1L!8t`~xaM{0_e35!d~Bw7Y-asqS!nzU2~$RRR~<-@94iVT)=~h;VZG&K zd9UpnUJOBKO}nuR{O+tX$B?9!8vk0YiBE3l_QwQQ%HHzNMU{LDv(22ov*4V#i ztBkMmegdPmY|_|bHgNq_cWcL~ssd1TK;4#A$o*`{hFte~XSjCt=X2?4Is9ZVsAR>- zMH~sjrv{=eJT(RFm!}S(-F5nYwDm&ugfCGt)F1frt488ek~4Rw%aRKL%!ST$z}5A( zGaTvp&avza2bJw-E{pk>Z{*H7-kw5x*}*_RxJvOpnlc^m=UJTWePU9~b6JID0xRJ~ zN(gn5hfCqbTSFD4yiLt|Ki|NbI3OtlcVdI_s|Jh?&+T~p!~(V>SWYvBA{_Np=wm$^ zSj46#pEzN5!s{Zi8Yk>4oxe3^G5|`fF*gjuiiXO4-yCRugqVo1s1$b6CAGP`>sy29 zsC?&d4n!6BE+3Cm4}Lco@Y~-ZNxFl+$MnAn)M07-(2{2kw@ETp?ERC>{-G}&cbERq zi|CmA=v-48=AJyago5*ke@$~VXVFm^`*At_QKnsB9c5kM@Sk;|jeO}sFaL2w2&aoY zbGiox>*;;`#ct{xI|v4pT^r_~W#vVb2J)wi6VXoisT8E1{RssI^DsnC`MDLHaN|EW zCi=_f4YXO%XYo-w{eY~$+MqV^83w^J7|w6_Il#oeC^NUF`{<<5Pa>nw+}*P7#@Z%+ zdqAzh?X?5_HNG-Hs9?BK@k$uGx#oH8DPX}Y%;))wA3L5W`ORr&pq8VrunzTEKAnzd z2Pqr9RY8i-fvx+4l%i9Mt$%JvBYf>g=T8_jE_ZZM?u4<{nA`~?$K_7VDX@z3i*gFB z`*VtNMi=GWZ{<#~hV+d;(3n~coisVWVDfltbWTB0?wH)sBa3pZ@skUStOrIG=U5|) ztZ_Lb(aWEZV~xr!D$FU;3UVe*&Mm+ug+&GVIK1)x|&Ff zue*xQ1vF>NCt7e;AYR^tO5@?CleJQxe--g`juqil)>^$DM53 qAuafY7+#cb<;K5lL083Bx1gl>`)M>heh9yy$Fp0|KI6qsn)XjtQ^{HY delta 7040 zcmb_hdw3L8vOjgYXEJ0G(g;cthomz+93BxwokSj28kff+qOjkh$f}^I%S7do@DPC+ zNPvKdfrAbrQB;(Nf{zx$dQk(qfGY?~f`EhtSMH+XUAgK7^!myD)#*t9UH`e?&BvUs zKBuZqRh@dB<}cgK7dM+3Vsz<)B!r*^vi;(W@7)Z#(=^_sH`If^p7DG`l0QTw^Z6n3 z!{qk^5JLK;AO9#Phe9FAzx=kctzU-P0ztOR$-xLx$$yyfgM~*TDW8|{LRcXDN&NT? z?;4w_OdpM>U{H1Tw+u9hU|cxQH>m#pcA1;$PgF}=DgE0j^9?n{AEl?&27l}9YV!3> zBo1wJ&m2EE$d*IRKDMyQdd@xK-S|jxbhlLND@*m8a`U?0AORLd+o`!%T?h2qtC&c3|2a`P?Q=(6{jrB!LNB>5M87lQ zPo{z-HNGHC_K|&TpY0!z zDf)_nKn4Z*;ZQ*{M--$RyqXq~CJ!PUEOCr7JN?E?;RxsPI$@EXF9-%@pSVMIx|24c z%nssdOpX-_F`-|k&>Q%jY<>?19pX9L?}S*Mf*>GULmAlF#`0u6mlG?<2txK#B4%gU zLF)m{rCEyFboNrrTXl{J%gklkxSqv((*gi1#t4_?0`y3{2{V^BwqhL(VF#u|T~W}C zrKVUxIvc}@#KLS5*2^|n;D9YV8iCuj;s6rs5@LEUj-2SMs}qii8K;}oE1d3#9-^I{ZkvT-d=DWQiiinO0fX9RM=_~L_&jJv zk;wF*IaDMr^$;JUsAyxS$8EE#SWG=d0Z$hSMLk4YZ8p4Eu$&e~^dyU|WkzH+Z_5Hg zjaE#-Nc|WKYgTV>!2+tC?&P|4V5vv?_+ z;UCj9Q3UW$2G15(R#a3J8B@J&tP8e@>uvs@_o-yXGyAgbW_chRbhZbw9TU{(Lh&BL z)(#=ZM?D|vD9efjECU~s(~Crg2UWmxT6u^I;W;5VFdu9J2aBWUvkv^9ye9s0x8dLm z#Nb8&4)CaC?k0`~4%8UhIx4LRh#W6WgpVU>KoNmaYwt<8avnHt({Rh)z^+o?7bWjx!xSggbHK2^9x`BZUB-JZ5 zXdbOqm)|&(rmE#Pp3UW483Z}Uh!qJ-Cq5{XkVH4ssaFQKvAktu4Oq&SR;fBP_-Zg7 zy`-i5(?K_U(=ej<-AO~r4VvtJKlFNjyY6?}CCyzjd>l+;cm!FRsp;lS>}0U% z4OkM~rrSnDX22i<)-Nz+A1lW;IvUwSwT-_zsNNfXfM&QWZoQvqrt*!bpfA*d5syF% z|LwK(irR7e49xc*Svug?wSan@6;NGzU8ExyisL`iZrYMRd`E}W_lv^Tv~YSv7Tan4 zwJ6UXQdivZ4DDAX+r!BiQM&r(jt(fLId|sC7S+2`duY`NHX1l;DA+6?bxmGr7^fc5 zO83`hefE%tjNSL{cE1|6jcC1k_3pf297uan5XqXQ4&D75nyvEg=_jpm>i&CbPHWEP;F+k z22>F1t;Y^(PApqPdCHm@aNm#pk!ZeK_`s7y3)K4$4wnZ%QlWoccM{h5Xx)W{j0ZXML}Cg@NKVtp^88g<3YVf3+@HuH}d zKbu*NnOX63^p={Hh+t-2;yS>`6Ga$9vx@0}`u(hggy+EXn+;t_XZcQhb3VJF6TZq%S@YYzkd?8$F&ALb4PCbvlmr186AF)X%*oE~1M0J-8|78s4^6!`bW8t#y17=iK>8I_B zMddPp&N3qw_Hk1vY=a+F1}4#JB~niWsk73E!NX1oOBR@%IgD6}z**A>>b?>zoXw3h zhEsbU+!;R)SNg$?3#{QJ{IRfX3qNn^UV9Wyh>%32&%H$WF`rVNkOi zTuwE_#i&Z9FYg*p=w91E4rOZe{yrxwquG3E)$HE*E!7|9>A=IDBdZ4jVLYb_=( z6DIs&A2$M8z6w9M=?(!>obrWzP8?yt96K7vZY?svb-3P{4}>N6IkJp9mlhH+Y&h{K zsF-gdPEvE#CRz#C!0IVKGG=h8k9=`lz7j@BSmN>p&+Q@hsOC>!)}o#>0?NBtU~q=r zBh_=qTH`uai1ndQcOpYge|k^^CB{1&=`3TgCoz*MnQ{;5RC=HathhS9LfVp#I)SIt zEGp~qwBZMP`H3V>IIXiDT+8rx9=uloK9!mmu`rJJEAI`Z3rF|4_mVL&Dvx_$0N^nX zGyyhfFxp2R1mGOh*Gtq?YX=e~@2{s5dK!z?PUsCh)X1>k`haqEcS(h!R#A099Ol%{ z$hQd`&|e7Yl20BD8{vWkgM%v{6giq@L?*CnL{>=aL!3J~3-v}AtpUieY&KhVrDc*$ zN4%@;w{~eK0wZfTr%?x2qgsh$uvVt=@kuA47FJ)Q1HPXg+p8|mbObc~nVaJuLnNKJ z(L`AXfd7;v!@KQmPIUwVVhU%CeoM8fg4+R*ar22|Um?T9P9vO^kcimt$@50R8o&rm zbW0oQm}x}1OP}pb^t`HFc{#nH8dl~)tTQX4v|L^J+(=sGE_iMYVQSQ-Ty^uRVf3O} z`2y>3VpThpzN%BxN|Nf`6RRpDtyT}Lo`G@j+I-b)jgx|6Yq~Z;E!D0yA-S5=gw@$f zuKlfExRWP_uFXyJJ1Z$>t#Nm+on&I=h7F|{Yc>oGttMQYar}L3zwPg64D6dQrxzIm zanlBNyH{^BX^%U@jS|q`d~*`Bt3BL0aHHs#SdNj zcQsbTt*RL4DKW!)VZ zU*8^9f%>)@>O$|T5%qTgc6OvBP>Vn4th(*@=B`%n?PpHM_eUYyx^6S+{BEPJdRnG&FWui!}67L%C-CFYT}qcF0xxj{HdVYSs_!RqhXV3L2Gy zBC7moTg$kDa<{>M4m z@=`R1b{L4(3!E7cpBXXhd4Q7haC0~_5CS}yVFO=;P_z3)Tbv3 zU=ri=YvW$gms~S!Th|54d*IU*XXOZ}aVM1cwuO`|`>;%fB9xL&FX}YQ$rU7=?7Rev zD>+lLVK~EbcC~v_HedH}gT?0po3M9t$yzd*W}sSsTIf%KgFbCH;sPf9!Gtp~4ylnB z-`k;Ut5gmImk{Somc&b&xHw0_nNuc&>4tN(`TkDUFm>ebBXB7$`fMDz=f3Gg@iks{ z)(YYy9NI^%C(h26{W77rjnvDU;=yU1PfTm7KR!@OP0V+tY^a%~W%H~Ab{S1+T7OVY zrEu*-I=$PMKu~;wVnN|7&@wBi27Nc76AYkvv-aRzKv>pBz)sjEnEDzKtn;aNKFC%7 zjCEI`lJEIcx!?&d{JO`3{Nz;V62YP;@R>K&jl>6M0?{_`MG+;PiO}~NsP&Z^jG|;R zg*PT-*au37xU)97m16&eQ8WTmRiHqMxGt}{$Xfc`P+q%|C@>%qil5i3Xg$%42wi*) z6kaW4Q*pHMWw{6y5D2R-)B=?-bpR_a(tTS6{O-@6 zFXZYw?8`wItG}Fr@zRs~sZO0dIRr-QG;4|4rSi6%M-WI8NHwPv4@T%@!_Bg znt|B!*dd$og2pAWWMtJ^2P%K)MV(~xAK`DaNd6Fb!>M`u#1 zE$Mj4aUkLUEv!}iL>65|YSw>U3%bTr-j=*Ai@cWWw&dX~YEA!<{5Ffwk0kqAP@=x( zVSFYtD7R*AOB&@%&d#AovPBN%)vU{*wv^nHOQ(~&FQ-I6IGgr~;Os#1rVu%;$38T9 z^aEqZ*b_%jvF{#RR(fxlT{?E!y~)Qy6sp-8qUMHiSJ}AadYkf+XY=VUkZ=A(1iz%@ QxjYIbOKqw%BUcLXzh81RC;$Ke From b4b47f868c4cf186d71839d61f93aa46fa3cd5b2 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 30 Sep 2024 07:30:12 -0600 Subject: [PATCH 64/75] Added support for combined weak/strong bitset comparison --- .../test-contracts/savanna/common/savanna.hpp | 33 +++++--- .../finality_violation/finality_violation.cpp | 76 +++++++++++++----- .../finality_violation.wasm | Bin 51727 -> 52666 bytes 3 files changed, 79 insertions(+), 30 deletions(-) diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index 80d8a06860..e7e592226e 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -142,9 +142,9 @@ namespace savanna { } // verify signature - bool _verify(const std::string& public_key, const std::string& signature, const std::string& message){ +/* bool _verify(const std::string& public_key, const std::string& signature, const std::string& message){ return bls_signature_verify(decode_bls_public_key_to_g1(public_key), decode_bls_signature_to_g2(signature), message); - } + }*/ void check_duplicate_votes(const savanna::bitset& strong_votes, const savanna::bitset& weak_votes, const finalizer_policy_input& finalizer_policy){ @@ -191,13 +191,20 @@ namespace savanna { } - void check_message(const std::string& message, const bls_g1& agg_pub_key, const std::string& agg_sig){ + void _verify(const std::vector& messages, const std::vector& agg_pub_keys, const std::string& agg_sig){ + + check(messages.size() == agg_pub_keys.size(), "messages vector and pub key vectors must be of the same size"); + + for (size_t i = 0 ; i < messages.size(); i++){ + + //std::string s_agg_pub_key = encode_g1_to_bls_public_key(agg_pub_keys[i]); + + //verify signature validity + check(bls_signature_verify(agg_pub_keys[i], decode_bls_signature_to_g2(agg_sig), messages[i]), "signature verification failed"); + + } - std::string s_agg_pub_key = encode_g1_to_bls_public_key(agg_pub_key); - //verify signature validity - check(_verify(s_agg_pub_key, agg_sig, message), "signature verification failed"); - } //verify that the quorum certificate over the finality digest is valid @@ -214,11 +221,12 @@ namespace savanna { check(qc.strong_votes.has_value(), "required strong votes are missing"); savanna::bitset strong_b(finalizer_count, qc.strong_votes.value()); weight = aggregate_keys(strong_b, finalizer_policy, agg_pub_key); - check_message(create_strong_digest(finality_digest), agg_pub_key, qc.signature); + _verify({create_strong_digest(finality_digest)}, {agg_pub_key}, qc.signature); } else { if (qc.strong_votes.has_value() && qc.weak_votes.has_value()){ + //weak QC (composed of strong and weak votes) bls_g1 strong_agg_pub_key; bls_g1 weak_agg_pub_key; savanna::bitset strong_b(finalizer_count, qc.strong_votes.value()); @@ -226,21 +234,22 @@ namespace savanna { check_duplicate_votes(strong_b, weak_b, finalizer_policy); uint64_t strong_weight = aggregate_keys(strong_b, finalizer_policy, strong_agg_pub_key); uint64_t weak_weight = aggregate_keys(strong_b, finalizer_policy, weak_agg_pub_key); - check_message(create_strong_digest(finality_digest), strong_agg_pub_key, qc.signature); - check_message(create_weak_digest(finality_digest), weak_agg_pub_key, qc.signature); + _verify({create_strong_digest(finality_digest), create_weak_digest(finality_digest)}, {strong_agg_pub_key, weak_agg_pub_key}, qc.signature); weight=strong_weight+weak_weight; } else if (qc.weak_votes.has_value()){ + //weak QC (composed of weak votes) bls_g1 agg_pub_key; savanna::bitset weak_b(finalizer_count, qc.weak_votes.value()); weight = aggregate_keys(weak_b, finalizer_policy, agg_pub_key); - check_message(create_weak_digest(finality_digest), agg_pub_key, qc.signature); + _verify({create_weak_digest(finality_digest)}, {agg_pub_key}, qc.signature); } else { + //strong QC bls_g1 agg_pub_key; savanna::bitset strong_b(finalizer_count, qc.strong_votes.value()); weight = aggregate_keys(strong_b, finalizer_policy, agg_pub_key); - check_message(create_strong_digest(finality_digest), agg_pub_key, qc.signature); + _verify({create_strong_digest(finality_digest)}, {agg_pub_key}, qc.signature); } } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index bbb94ca44b..253790ac21 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -1,5 +1,60 @@ #include "finality_violation.hpp" +savanna::bitset merge_bitsets(const savanna::bitset& bitset_1, const savanna::bitset& bitset_2){ + check(bitset_1.size()==bitset_2.size(), "cannot merge bitsets of different sizes"); + savanna::bitset result_bitset(bitset_1.size()); + for (size_t i = 0 ; i < bitset_1.size(); i++){ + if (bitset_1.test(i) || bitset_2.test(i)) result_bitset.set(i); + } + return result_bitset; +} + +savanna::bitset create_bitset(const size_t finalizers_count, const std::optional>& strong_votes, const std::optional>& weak_votes){ + + check(strong_votes.has_value() || weak_votes.has_value(), "must have at least one set of votes to create a bitset"); + + savanna::bitset result_bitset(finalizers_count); + + if (strong_votes.has_value() && weak_votes.has_value()){ + + savanna::bitset strong_bitset(finalizers_count, strong_votes.value()); + savanna::bitset weak_bitset(finalizers_count, weak_votes.value()); + + return merge_bitsets(strong_bitset, weak_bitset); + + } + else if (strong_votes.has_value()) return savanna::bitset(finalizers_count, strong_votes.value()); + + else return savanna::bitset(finalizers_count, weak_votes.value()); + +/* + std::optional> votes_1 = sv1.has_value() && wv1.has_value() ? merge_bitsets(sv1.value(), wv2.value()) : sv1.has_value() ? sv1.value() : wv1.value(); + std::optional> votes_2 = sv2.has_value() && wv2.has_value() ? merge_bitsets(sv2.value(), wv2.value()) : sv2.has_value() ? sv2.value() : wv2.value(); + + savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), votes_1); + savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), votes_2); +*/ + +} + + +std::pair check_bitsets(const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2){ + + std::optional> sv1 = proof_1.active_policy_qc.strong_votes; + std::optional> sv2 = proof_2.active_policy_qc.strong_votes; + + std::optional> wv1 = proof_1.active_policy_qc.weak_votes; + std::optional> wv2 = proof_2.active_policy_qc.weak_votes; + + savanna::bitset proof_1_bitset = create_bitset(finalizer_policy.finalizers.size(), sv1, wv1); + savanna::bitset proof_2_bitset = create_bitset(finalizer_policy.finalizers.size(), sv2, wv2); + + auto result = bitset::compare(proof_1_bitset, proof_2_bitset); + + return result; + +} + //Verify QCs presented as proof void check_qcs( const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2){ @@ -33,12 +88,7 @@ std::pair finality_violation::rule1(const finalizer_po //Proof of rule #1 finality violation - //todo : check bitsets - - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), proof_1.active_policy_qc.strong_votes.value()); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), proof_2.active_policy_qc.strong_votes.value()); - - auto result = bitset::compare(proof_1_bitset, proof_2_bitset); + auto result = check_bitsets(finalizer_policy, proof_1, proof_2); return {result.first.to_string(), result.second.to_string()}; @@ -76,12 +126,7 @@ std::pair finality_violation::rule2( const finalizer //Proof of rule #2 finality violation - //todo : check bitsets - - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.strong_votes.value()); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.strong_votes.value()); - - auto result = bitset::compare(proof_1_bitset, proof_2_bitset); + auto result = check_bitsets(finalizer_policy, high_proof, low_proof); return {result.first.to_string(), result.second.to_string()}; @@ -120,12 +165,7 @@ std::pair finality_violation::rule3( const finalizer //Proof of rule #3 finality violation - //todo : check bitsets - - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), high_proof.active_policy_qc.strong_votes.value()); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), low_proof.active_policy_qc.strong_votes.value()); - - auto result = bitset::compare(proof_1_bitset, proof_2_bitset); + auto result = check_bitsets(finalizer_policy, high_proof, low_proof); return {result.first.to_string(), result.second.to_string()}; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 92297b30c875893694317b0623c19c2693ef2931..2ec1941fc17809415980a3f1f3e83320fc43dfaf 100755 GIT binary patch delta 8624 zcmd5>4Rlr2mA>cP`+kxa2YXA_xJ(&x_=rpq6su z6djabduSJb(qIu-OKQ_zN5DE`S^<+PW6&09b*9V=GsS6Fho!hiSLy0}`<$DXmjE5s zbY|AX#k=S3ea=4L+50>D>~r0^-F$7g+5QQaBh10_-}zYn*cy4C87nYu?ER0i&>$0k zLCy?@KW;MfmRn42hC(bTpD`zgPkmrST*Emf&zQN{_fMWuKJCltXaB(0*oHmQo@h_9 zzhqCfE9~jgwnh$K6`0sce{F|O|E(+d16G+Z1hZS@3~SJ>kCU?Pz{fBB>)DE4-4ou| zEaxWA9ejZ?$6Jy05i7dbY1qs*jEKjW>GCpO<`_lBj&g@J3@{vHqA`wdDtFK-<=e|; zL(b@t+wdxw!x~GKMsU((un44v!JviIi3pTs1K#1-09R%UhjoMN`lhS2MVOe`atOfl$#fraA#|W zVHakh!AkjY1?^~GXc(J>)^R1Y){2w>UGkw_E_c?;8;2E^eBO(UbcF13A7qez)(6@8 zpUSe_nA=)G0lU-}3OXmDTvkxF5?6*B&N-tdX5{&CeO*HW2|9jCD%*-OO`!G)O`xOn zgd#r%KqMq!uc#-)iBdl1D98$K(O_~87y`9Y;TeFWHN|hQS4dlAPHC@*F-0c_&*m>E27n}(V3+^?PxW9CECWQ`bI3{kM(EQhB>BEuL{*5VkO zGTHuVvP;C-O!U+{1c^5za zp4>XBSpM6{M*h##6mnhu3oOTr#&6>M;(IcEOrCqhWa-3$?sR_<0w%4PMGqqusBtn@ z2?h!M7&QpvsxPT!eE2p%s7}#<8lr()A^<&ru>=-oI@^K?_$Lt!Q9CU`G>C>BgH{_& z!#_9O#A;zWYN1CsA$XjJv07ea7_kU>E(4yU`SH>sGP3R2dQvQ383aslCxaXT+GNy# zb9!234s(+r3=alqa-|g!k(NM|!ihL{4PgPRuu|qLWMaZ7x1LS`IGUXdE4S=hvk*@G28duD*bxV7g;hiG#6e*=JKljWBOnTS zCm_4`ieat#W$`4rsqUs7;S`Ii+-`*5%=m4nFH zNW|CRV3LBA&x?#b<=3F#3%&Gfk|zttC<$?m$C>is z(ro_fJMyv8(eAIw-vWp;CJ!*l@2xxm&KdSYu@F*M0L-u-h=$SVHjxYLxoBk|?F38t zStbG^hycaRQhtunzNAj}?OBjR^bz#VIhZ3OKH(^?7!7TQNxfW(b1bctcY;p3drwaR z>2Z>s1t$Ye2c|bD6+K8K!tU3TpofY42w4y^dVsw;y^A3B4|If5p$Y1afg+>Z^t)sS z;`}Tb5Fzk81{+L-s5{b(ynkGN9<5rrBWjYDa^NK&45Ubp_CwK-+&u1XN{C(K;=^FZ z(zeoS9J5&jr>PY26UtK}BnOTk#@pYPW5y44;Th#wq%K&5+wpdTzg+ESqyXk{+Na8c zywN%QbHlN;;I{~Pu?~cRC>VDec@zOGxzvC_1PXY@I74O20-ia}$k4K!aYm+Uxjb*2 zks|^Ve}Iy!!y3oWQ@93eix+ih2dmJ0u0xA3P-rRO8YLP!)(F%k8VtND;5`x4VJ9J0 zmlT4V`G~m7J86r6%D0ws*O7s4Rm|6lA5Ky&61&fQa1{Fe2SLXH)G!LgO>gb!_8X>-r37kXIss~D^U#RfXT98;m30gvc;vNms{%`|! zeiY|KGz4b_1@y{a0TH4(LsN7$1zXV&negp{LTH6_N~8LaE(C(psi!8U(7QlUW4QpNc6VEN1l_?J7?L zfJjnzS4caAE^Y7#bXd`R{ixJy(CctE@kEhp;Qp*SmGZg5!gl}@oxtMtT$Q$nVk@tS z0UqUlcRElKKvn5fqKTwBn;s~h$pE^q=k(jkV<8N4*i;OJUoi%!S5L>;poPN#E8=Qk z)AgeaQ=1A`3aqIbme%>O%>J;z?H*WV8rG!bY*Pig1C|=p4;JE|f@_kGYdSsQwh8)C ztUR&@?n;3*Uc=J5?MZKK>*Fn}7gn)`Rg#1?p8Vu)SMJ)z!RZ+9aToZ}1B=72B|fYP zKCD6wODJ#kgB91Xw9bcxg%d1AJx)Ok00c)61E@CyK`iBY^bj;#oE!yMYE%s4u>B~C z(1Og$TSx`ynCHVPq^G(PXJVUzQ{c6e=Faxk%CHrRXR4`r;IQiEw)i7a7z24rM7F zCZ!md@9Qg5^s|*7$WgD;*?|@oY~O>VWjP(m7M8C?SScDO0(y&f7&S7qppdu!+`Dsu z&3Sc}ygW18TfA^SlM80%j@b5hbTTRO;ZOnj)On?)*e8N+9?RvvnS=0c;^@p#Y=t~O z^KG_VwpZpra;kD9Tj5=*9Kde-jWUG%N5uf{hWJGWbPW3|*y&2v^X`qB^)1F$${#=2 zh|d}&v+u!g`|NzVbM_$Bx>xnaJ;@@;!6vC7eS5+AV+~%2>den-n^2Vx4i2g$8j8^ zO1?y{e0bFTDBa?`Fvjum0I3=^IaUIf;I=%l!TF~%MxxL#?D1NGi+M|m3}(~`B0qR| z*gT>ZnJTHlS4{g(c$Z#qyeQ5k&7*3+x_KzBWq#)TDKmTTVB>qZ1N(8!8SI-P zyPM_=)f>ME@qla5J@g_rTrkBYA1s z?rt7M^r-auw7x4N88MZTw);iw2V!OVeCY3lt$D zOL||jXia)wwxqdz4u3ToXn9 zS_KG;e0Rky2!`CaR6GE~lzUVp1U@L~i@jQCQl6>gKzETROM!p5CaVgorw`vmmv7(8 ze=e&K{j0?%l~wf34o*t4D(tfg&m^lt{aJ-UG(?0`SrztKg^^&@Bg?LXRil^x53ovs z>Bp+@b+bxNUy-}?+SCd6qs~#X`;rzyX*bD@>&D2PwYR4?Q-7eeo8`}HkFrnY@4S)HtII{} z{kkIdv}E-qXs@jwDd*RdbZz})|NTP!2()f!xF5f>8YbTH49lW>N+wP?W3&HTMTAtmAZwa-m>pdR{_#~+xz--9$58pvp%0|V7?Q}3AvOIN_IA`UoU!(Gb~nXPE5vmgKtDBJ zmTkypYviJJLayDA&L~=s=|}+!A~nE0!Je1zyY=ibIjOY*p4!^_6x-+B@?^gG{|kTQ zQANZZKIi(aVw>Fgj0^Nbo?XxO$-U1W9k_<*h%+9)DEsEg>Wz1?zxKYf@lnRs%8box z)$dSwVDm)372HysY-P$7TXOJbZyqe4-%{?&t*vEBHbg$MHN?K|E!)~`K`>)`mi$c4 zRjo?=erNj-D35Lbip96T@7=hwlxCc|tGRG(bw08l}2j83ViVc>&k* zn2C#Zg{m@-$P2qlZoRfHSI*j<3$|U@H9QSgsP3>U<=)*dvRU4wJ#R7gq|Dg+A^S`3 z!rqm{)LCtLtWvIN8;LP{+wxhZce3pX6AEYl+~7ArYNuE(sv8;Vnf;yplhO007mL_> zS@vQT+6NAdk*8n0HET6?3!(KP<6A&-5I<1Fo|44}R;%vQ@cYSuIiT0ngZ1pM zvS_RI8>~NCd@7e5Dq;=ZbB8)OMtpW`6|i-@LA_s_5`@}Bl3XzyzWi@}2&+3~}Ja>miI{baB5ZA^Z@E+_1N zG*Q_q6jsUPIoajqkyQ`*$t!uKoA(=hHG>D`+s6tta=x8u=-#2%_HZV>$$#^E#(pK8 zH=g3_gRoOuwh9S!iKUh9x;l$|=F2qZwC*Qo8#a2}}HB~LI zZW1lkOPcE%MOE!mQNMbzsHuM37s=MHYe^<}wDO7SCS%p=re?9Cs-;>~HH$~9tMFD=TP@I?dbiXyW47ixv81uO zsu_KJQ{KHNzQMwV^Uc#nPdz4}PC(>t4e*gdg delta 7751 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 woF6e4E~%Nn++5leF_+$F&RyEHq^6O{<;T Date: Mon, 30 Sep 2024 07:33:29 -0600 Subject: [PATCH 65/75] Clean up --- unittests/svnn_finality_violation_tests.cpp | 1 - .../test-contracts/savanna/common/savanna.hpp | 59 +----------------- .../finality_violation/finality_violation.cpp | 8 --- unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 74058 -> 73729 bytes 4 files changed, 1 insertion(+), 67 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 9b171ee0c8..6abdb33741 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -20,7 +20,6 @@ using namespace finality_proof; using mvo = mutable_variant_object; - struct finality_block_data { uint32_t block_num{0}; diff --git a/unittests/test-contracts/savanna/common/savanna.hpp b/unittests/test-contracts/savanna/common/savanna.hpp index e7e592226e..64c6cf0d4e 100644 --- a/unittests/test-contracts/savanna/common/savanna.hpp +++ b/unittests/test-contracts/savanna/common/savanna.hpp @@ -140,12 +140,7 @@ namespace savanna { bls_g1_add(op1, op2, r); return r; } - - // verify signature -/* bool _verify(const std::string& public_key, const std::string& signature, const std::string& message){ - return bls_signature_verify(decode_bls_public_key_to_g1(public_key), decode_bls_signature_to_g2(signature), message); - }*/ - + void check_duplicate_votes(const savanna::bitset& strong_votes, const savanna::bitset& weak_votes, const finalizer_policy_input& finalizer_policy){ auto fa_itr = finalizer_policy.finalizers.begin(); @@ -196,15 +191,10 @@ namespace savanna { check(messages.size() == agg_pub_keys.size(), "messages vector and pub key vectors must be of the same size"); for (size_t i = 0 ; i < messages.size(); i++){ - - //std::string s_agg_pub_key = encode_g1_to_bls_public_key(agg_pub_keys[i]); - //verify signature validity check(bls_signature_verify(agg_pub_keys[i], decode_bls_signature_to_g2(agg_sig), messages[i]), "signature verification failed"); - } - } //verify that the quorum certificate over the finality digest is valid @@ -256,53 +246,6 @@ namespace savanna { if (enforce_threshold_check) check(weight>=finalizer_policy.threshold, "insufficient signatures to reach quorum"); -/* - size_t finalizer_count = finalizer_policy.finalizers.size(); - check(qc.strong_votes.has_value() || qc.weak_votes.has_value(), "quorum certificate must have at least one bitset"); - - auto fa_itr = finalizer_policy.finalizers.begin(); - auto fa_end_itr = finalizer_policy.finalizers.end(); - - //todo : check weak_votes as well - - savanna::bitset strong_b(finalizer_count, qc.strong_votes.value()); - - bool first = true; - - size_t index = 0; - uint64_t weight = 0; - - bls_g1 agg_pub_key; - - while (fa_itr != fa_end_itr){ - if (strong_b.test(index)){ - bls_g1 pub_key = decode_bls_public_key_to_g1(fa_itr->public_key); - if (first){ - first=false; - agg_pub_key = pub_key; - } - else agg_pub_key = _g1add(agg_pub_key, pub_key); - weight+=fa_itr->weight; - } - index++; - fa_itr++; - } - - //if enforce_threshold_check is true, verify that we have enough vote weight to meet the quorum threshold of the target policy - if (enforce_threshold_check) check(weight>=finalizer_policy.threshold, "insufficient signatures to reach quorum"); - - std::string message; - - if (qc.is_weak.has_value() && qc.is_weak.value() == true) message = create_weak_digest(finality_digest); - else { - std::array fd_data = finality_digest.extract_as_byte_array(); - message = std::string(fd_data.begin(), fd_data.end()); - } - - std::string s_agg_pub_key = encode_g1_to_bls_public_key(agg_pub_key); - //verify signature validity - check(_verify(s_agg_pub_key, qc.signature, message), "signature verification failed"); - */ } checksum256 get_merkle_root(const std::vector& leaves) { diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 253790ac21..5bdd0a5d2e 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -27,14 +27,6 @@ savanna::bitset create_bitset(const size_t finalizers_count, const std::optional else return savanna::bitset(finalizers_count, weak_votes.value()); -/* - std::optional> votes_1 = sv1.has_value() && wv1.has_value() ? merge_bitsets(sv1.value(), wv2.value()) : sv1.has_value() ? sv1.value() : wv1.value(); - std::optional> votes_2 = sv2.has_value() && wv2.has_value() ? merge_bitsets(sv2.value(), wv2.value()) : sv2.has_value() ? sv2.value() : wv2.value(); - - savanna::bitset proof_1_bitset(finalizer_policy.finalizers.size(), votes_1); - savanna::bitset proof_2_bitset(finalizer_policy.finalizers.size(), votes_2); -*/ - } diff --git a/unittests/test-contracts/savanna/ibc/ibc.wasm b/unittests/test-contracts/savanna/ibc/ibc.wasm index 869e46c8c5dded1143f64bcc35708c8d61d199e0..5e21f2e59906a3c98e09dea2fc4b851c4c6eaa98 100755 GIT binary patch delta 7471 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 Date: Sun, 8 Dec 2024 07:47:17 -0600 Subject: [PATCH 66/75] Reversed endianness of bytes and flipped byte representation in savanna bitset and ibc tests --- unittests/finality_proof.hpp | 2 +- unittests/svnn_ibc_tests.cpp | 16 ++++++++-------- .../test-contracts/savanna/common/bitset.hpp | 4 ++-- .../finality_violation.wasm | Bin 52666 -> 53201 bytes unittests/test-contracts/savanna/ibc/ibc.wasm | Bin 73729 -> 74038 bytes 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/unittests/finality_proof.hpp b/unittests/finality_proof.hpp index 5c8a032e08..2520acd584 100644 --- a/unittests/finality_proof.hpp +++ b/unittests/finality_proof.hpp @@ -359,4 +359,4 @@ namespace finality_proof { }; -} +} \ No newline at end of file diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 45bfcd7eed..a7ec940f18 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -666,43 +666,43 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() - ("bitset_string", "30") //bitset bytes are reversed, so we do the same to test + ("bitset_string", "03") //bitset bytes are reversed, so we do the same to test ("bitset_vector", bitset_2) ("finalizers_count", 3) ); - + chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() - ("bitset_string", "ae00") + ("bitset_string", "00ea") ("bitset_vector", bitset_3) ("finalizers_count", 11) ); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() - ("bitset_string", "1263") + ("bitset_string", "3621") ("bitset_vector", bitset_4) ("finalizers_count", 14) ); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() - ("bitset_string", "fffff1") + ("bitset_string", "1fffff") ("bitset_vector", bitset_5) ("finalizers_count", 21) ); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() - ("bitset_string", "fff700") + ("bitset_string", "007fff") ("bitset_vector", bitset_6) ("finalizers_count", 21) ); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() - ("bitset_string", "30") + ("bitset_string", "03") ("bitset_vector", bitset_7) ("finalizers_count", 4) ); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() - ("bitset_string", "c0") + ("bitset_string", "0c") ("bitset_vector", bitset_8) ("finalizers_count", 4) ); diff --git a/unittests/test-contracts/savanna/common/bitset.hpp b/unittests/test-contracts/savanna/common/bitset.hpp index 5222f01032..9b00cf7661 100644 --- a/unittests/test-contracts/savanna/common/bitset.hpp +++ b/unittests/test-contracts/savanna/common/bitset.hpp @@ -63,8 +63,8 @@ namespace savanna { std::string result; result.reserve(data.size() * 2); // Each byte will be represented by two hex characters for (auto byte : data) { - result.push_back(hex_chars[byte & 0x0F]); - result.push_back(hex_chars[(byte >> 4) & 0x0F]); + result.insert(result.begin(), hex_chars[byte & 0x0F]); + result.insert(result.begin(), hex_chars[(byte >> 4) & 0x0F]); } return result; } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 2ec1941fc17809415980a3f1f3e83320fc43dfaf..e30fa480bc2e1ba10c01220c9a6c95c3fc8eeced 100755 GIT binary patch delta 3229 zcmd5;eNa`$6~AZieIN2jSjQ9|A2Iu$sE;aDDTMe1TnJzxDu{^>&?tT&T%-mBL?gTh zHi@PQ3MU3rQ^jbIjA>)C)8?bGMQ1XpO=qf|*3xuLY|~bgbXq&@AKGbZ&))ZpG-6_> z|MdQ`_w4R(&pG?sbI$G?`}8vh^kegNwxu0&^%ShqpPh7X(Snlu7RCIzOK;~IpTY}x zAur}%znCShwBR{OZfuS=$3<*|l+-4lTJ@gm@BgFC;ElAfizivX4-?&W^B((W-p_yR$9A=Eq84Dhv1(;@bHkl#I z=Tsd-Ap*?pb9e*@M}rS+O-`W~lL}jj&;^*Jg=a>Qq{jvsg$jc4e0&Z;8vIVxcL=cj z2H6ybaB@A#V~+?nM^kg;ljUcYsZsa3%lPI5?^M4|W29JNnrzMF7AcD(rGFr_th1mJ!wNb7+IP` zMuS7fFz_fIPff0ouT9U5tO)>$8O5+o*3DRNc!PeIV8UU#ExyF(CgHSBe)3AJyC)1w z@xyx>@t2ut5X7sQUTVErZzU_7ZZ8Tmwd!_SHKLf{mGW{{0?;y^{fpSqn(1@tinsh--jA`WWjeLFpXfO9)L2}UN5_-B8TX-9Vqi|8|fY5o+@8GL(U0L>wV3G=6mw8o%1`x!aPEz4w zN1c-gJi?$;Pu1c)yh^@PwuiwQtf@$eT|;O?1%<)a;m(Q^cPeiUb6B%@M4EX`pqR;# z>z9@?R-1%B2`1yu9=Jb7VRRKvIrx=jf3>@s8LPT!!9u9Rjlp;dzDI-866=!)c$!&4 zS20Jiu42wXF-N*2Av-Hm2)VCvN`gYF=}RmE`#S{&;p9jqhw;mZZ8~p4Xq0stUnZ|q z?E$cG*YXOY>x1PE@RU=8tC%SF^vl1kNYP0zuSrh`(MhRO3WJ~GCcwTyEGhd-Yu=|Z z%v}8}bm5uRUxGZmuzDb=O7rv^z(SKWVxz(DNTqZkvj$&}2iFwATD-U>qsCdJlG^lI zCN%pDJTiequg_v z+TH9T6V7}sTR%^K^E{QV(;6?suhynRJ=WAFLY{oM_C6r8e!M;_q${!_Fk&l)*s?pq z8Bls+gfm?`&+M(Z6k+2&L)gv0<37WgPMJV;)I}xQ-r;A=N!Ft@(yQ}$dDx5j2~f~D9qEyZ57WwSTb@ZYfK{@>W6 zXtMn78`zt+;fvWD+wv{2w|zsH>aEc@0vqMI#xpSQA*vRa9hB!3iI$zsO{##XAqW3d zG0r6L@es~K1 z(YhaU@R_zLuu~4V#j+CG9@QEu%6?sKRmp;04YXJLQ2^=MboBzD%Z5J!q&9+8Bq z;~DmCpl17J{FVSv`ODp!=5ABv&%@i~LtE>0qWRs9D9yA-+E!Q}#gZC|(yYWN&DVhu znlFUjjL;mJ5Vxp+a8a7OlA#^1bP3M}o9w+&vfCp>lSf4KVE5esTjaUz$-0AX8?GUA zs3`e=a=-DW?&DzhZkh4qafoYIu|wA?wRBxp%YXF@0(9W%-aGN+?v=JQ5%YRYTbhNk zH^r6~QaiD)hT3r73Tof$6VNHI_O%$)Y3XmK_CkMIaz~_~P(>BaG<`vS*w~h>FQDqj zJLQtSJr08X_S-G-9iI1rt~81nVfh$657Z;N92@qRfNHbwPq4?oNP%c*RCIkW1oFZU?qIM- o{_}XRTG#WVMEc>-a293Sd&7e?^vH=~h`v;~defXe`TLlE1I$b%aR2}S delta 2695 zcmd5;dr(wW7{A}SciD|aJx&J8vNng!kjO1G@rB4<6bPS?<_f-F5KAf)2r9;EsHpkK z4;ay=u#zm&!A_5{u*kCdqb8l{K~s~CSu-_LX<6eqX?^GJqBPdzpTW#I-#Opo{?70G z&N+MXDgE`mdPj!NYN|0)_uw3T?``9=bMq!miSEA4s=3CKcov_)bNIb{BA@I$4w}&T zICLAgx|Z3pLh7&ez{xl31S>H7LKDDOV!ja@w@Nu%+rDrAu6yD&-)gzmXzweB4j5;H zbS#h01XFgzZ;pa#I7SQ}lIP83{T7=av4e+248|W{lO0c2GL+agMloY3;RmeFO4!uB1p;&pQh(rIG#5L_U zMQA4d^O;hg(*#h8Epw+6S3l0Rfis_kuN0B*Y?hbjd337lUgV2=g!W18Q3$-Gnq*c_ z6e$ez7M+4R%v}6LTu2c|o}CarD~(_s7W`##tGiHhzNG`y-KG&)WnSALiX;>UFUA*_ z=>v9$ZLkj%Y zJ>dTc7KMT9C5d)iQ{pD*C(3;A0A4AJhfL{uFaq4ta3f}JJZmtXN`oe@w77A9%Ph;vr*_(&cygBR*>0o3 z#iuG`Yn`V6SRJWE1QQvlhjhd9@uSu>e695sP*#ZAsr&ZR&zKAQ-UnERozHw3x1Qvx zF7$@Tl~3|jSH3*->~tk4);5T;2DeRxe7UlXUgHc|d|-?6oPT~+)T1OmGZlGN5>~yi z8tU=f3kRSITiSDAgFM~d0I&&55AK8tymT-JDx`VHt)A;S9XC@2S$^0JupGC(GzNCa z_g*Sg#l_1X0&JH*zA{>;HwNcdd9eA&U3Q0coF12}j@U>YvaS<#)(BXBxKys~8mtoc hiFoSptz&6)(tbPEMpI9`lM9hYn9e!9eD9u({tM1D+j{^2 diff --git a/unittests/test-contracts/savanna/ibc/ibc.wasm b/unittests/test-contracts/savanna/ibc/ibc.wasm index 5e21f2e59906a3c98e09dea2fc4b851c4c6eaa98..ce246487b28f7d190a2e2f6b38f0692ede861b37 100755 GIT binary patch delta 3306 zcmai0dstOf7C&pBb1&#cJQ5Cgxr~P^%C%s^Dbd7)qk|Y~gCd5g86qMV$xs0`l#3#Y z4}2^XAE+pTn3&c^(KN+prKD);pqP&&EoVmiDyx~MnzavP&V2KI^UpbJ@4eRA>-k$d zzh2pYT-ob1$#t*~Gfe(iWV#xhIPTTgCQdpC@4+!R4(*R`Q1*c;sp0Avb*wsGouDSE z$(B7JX_7h$i_QL_WndcUaxJZVxMbT~CnSxHP$D%+19h{pfAu*3zNIPVx9x4b*}Tg$ zZxI6}k3}-#4Ur_ftV5<<=^$xDWCzHaq=PfSq_+<+dw@lg98A*ZW$Jm(0JB&qg2Tbw z156eTG*fy27@EwQ98D?=C7P^(PFmEL+Biv%!K6e97R>VM6hSQMcC%QUG;rD#x>Hj$ zvnpFH#*W7NN#cxba@v_wmuUHf$+*?aC&(_-CTSK-nTBezi?nqltA##rT8;xKsCoJF zZJlFHFap1^MnD38&acP}lkxq4pukkC#&nf`xy;J z;<;z$(|1JhR{G{Ym&8YfL;$?POa9OV@H%FM7Q;0Duh0Qc3gjvEpti~I{O8JB`5k~U zVbRJC&?9hFpJ0A0>;=U))9$7*&7`|K)${;&A{u3KN}Y}v;erhGj?7ryzo#`*Brim= zMRkygrJh(Qr{unxiG%u|@)MG>p~l689#3Lw5`k2w^KLKt04WL|aLMDzyU;J<2Sb0( z|1%*_UjZ|)R^LHU95-+~gDiw7U&uyl)C-U4jHpn^!Oc;_I?S?ATJyqKpeC#F_nO zsExV}i;%@n^UJl8W+{yQiF}oHUuT$6LG_YTor}xjyTClGiqD34^dFrEd3@98RA4SZ zQ$hjU=Svd$1MJ2R#>^FaQHcj3m;aJTk;z4$ao1R?6-y8Lb9H>0xN^>`4dht#ghDd# z+=T05YgbZ$M?TQCjZB1f0X8N1Qzd?v)Wf#`$SzHGu)WMqb?;!cluxQZe12jtcmpR- zyaEgH#pLc?7l25U@Zdw@8z}XW>qVc`o1A*1q)x{ChA^2wAL;=6}6OYpTE9g1;V zP6`RS&kC`v_%A_4*hzv43GxxMDBn&jp6yS;ubG_$3(#|p$XM_k5#+=<6l9(?#|@yE z&wX<<7(x2V2vXqOu>c3qHSZTVgs!}a^u3eUhrWKUgY9RKGj1xm^wHx`Psl&oDour$ zib3i@L9ZD@V7R}QXvWJFuE`hN)3^9hRFKgL^kl|!UWM` z&o3?%ze7ba^ut3%8t<{xo52SdzWgSYf2UPJn7G)J#}=o8IoIy)V12Q^$d_MU5l-E~ zJFQA$T^7k;%IhZSTDn~clr%*EFO>zM&r<9?lGZRifXR_G2o!MV|Q0SqIl9YYx~P;g|TLO4V|`A&)}nt;cymvmHCD@ zQ2FaC2^>tSL6#FKtI?D}!K}4YKL#? zUZ|&c5Tj;bluOlVm(>Hf{S1f0m~b|hZryn{#P&WVngnSO6Ef{>v%9tpNxwc@08XCM zFjd&t@^K;@!H9ElMAL%4rqkd4s>lfcN*OT2B0efBYzVZdYN&fq$P#I41KR0)U)xt}-=S6Khn761+wUMIjV7>-@!JushebJybgZ>1^-^`)vynNFRf59JaZl;`; z-dZ`lwP{#uRDNA(^i~w@!y>7KAVdll?>^BDMz;t&X{9uJPEys^Y$@%6NG(6 zaf-u=JH6?ut9Po1cC`e0lCXFNkva4hYQT3}7C;jZ`+N*Q6Rx})>fI!K60O!mt=2a5 z@X}pQNU``{IO#okk1#Th`+wO0aEaf)|ANde1KuvPVe?O&OEnLAh1NXijME;N-L4p} zdtiB)&*MX0^I*0@@;$zZhW4!`M25s+&bODLnGb(NPtWV<{O%q2lH0z&%-{xo{f|L# zlZXA2!1Yr;{l_Yx`PSv9?eHNU`Kf_Umi|n6y0vd6oMv#lE?frcp3mxH71%;{)_rAy z&H#7n%x+Kvo-O|Y8FlsUaF@xSgO%(bR{F>SF>tpu(gPNG-6KX(Em ceZ>;pK6YJ7~g>o(i=!O)B$#$;ov@r*Iu zm|@Hc-V4ptj6_-=5QVMuZb0Yo64*z0y_b*5A8yu zKvS4@caX>MbPr;qPUkp``BW0?#aw#8J&)QBeJCQMyKZVOEplY2&TFc9O@OI!$VLV| zIrMl}<*Hlzc!k<1j2G3z+DN*Fbq4P=a`^d>-ycRjV}G;`$o_wK_``_x$fP49Dj2`i z$IAq=X!@vdETrgBkNs+AkLrs>v}@E8q1nNv?#>-3qD|eHK!_{BV%i>;g(b4X==M4) zDQ4WuJSZQ>cxB03PizH_$-MDBbk4%g_-BzV?FkIkF*!2vCZ~*gJ>7^E6f*GzT#iThvE*Vo^!Pl~A%AbJUbZ+WW zo{CR8h#VRG4C9hRJ*Hhjs{CbIfjVo&vo#!1;*1r1vnw+i)Rb>l_g-G`5p_c0xeVQH zJkSP;1H#ltXoK`B?%h?wc4&pW@ z?_k?Zjxf;RI6tAYX?c{s>>~@}yBb4Gk1S7%%!+Vp?#f!tU>-6bnSl#f&I%bp<289; zxd3zV`igPbKo1whfT&M?G~0^{da<3G|7gfYzAzM(HKT}b7xbZj=HF>`*<`sCQ({3h zHd!vkmdobfTwa&A3nr@)ie0%vbthMi<&I9SGG+fY9RyBL+`8*b@Td(ja{T&*2*_~+ zImM$?vnpIR77k(V%cu=Ag}5fjyRj28sCeUmhxAyJ>Fv@qhj8CF-<@Btd3tM_@g&a* zw{6jH6ZAllBPf8GkNJ*38eKFHfs|X+Ga|s9>v05xY9V@Xuo*Nm6ebs|5~o0Uq^Ldc z2A$os9qZ-%Vkhvn+_HHvu#GOfJ__3@a!WGuC~u3S{R*qH!#xU2-Oc@Lw10~SrPADb z4ZTlV993L*1R19o5zVx>_PX(=H5TsO{I!ko3H2%&GU0FF(x}#0G~KMqHbz}Jn?c5D z^`VeWyWcEjQ#az&0`@oC7&QpttEkkTQOLV7Si2#Z_(rKbSYjfqo;R>E=oBHwWqEsB zh=@`*uoNQPFAxTg`uau6c#Di3@z_GMb|j%p9@)`Rh@GH|Wob05tQaZsVwqPxQ;4UukvzCw*@XsHjH*!yPTl%WcMoJNe1%bmS!P9$owB^Ui;bv=9vx@rFW2@Ac`%g@Uq`4<{>iZBu_ZrHvpAMaj zqi?Tvl~JcM^gc>@GlCu(Xq{gop4t#s(EYzdAM;b4{w2efK=n5V@KIs6%Gut3D>{sw)!zwUhksE{ z)9qW!aY?3sH3hgt$8Y!Ta7o3aim9H9sr8YfHcgV{;lrjOyzfsTs7X+?S%+HVGnoR$h6|g~8{JR|d6pjC-EKsQm|60)SmXS29C6Cusw{WVi`znDG z0ylgaHgI_~`tt4A&X@D`3`9rZ3*V3+>_*s^|Dy!H@7v%jq2FWzSD%QYzn8d*ouPOF uw~7~rqM+SZyjT&qO!ds)GGAyIx(M9%#qeXo1?PLDJ(j9N-n$?9q5lC1zMf|Q From 2ba9e0461780dc9c8885731d431dc99ef32a8d1e Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Sun, 8 Dec 2024 07:48:20 -0600 Subject: [PATCH 67/75] Removed comment which no longer applies --- unittests/svnn_ibc_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index a7ec940f18..5ace56a2e1 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -666,7 +666,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) ); chain.push_action("ibc"_n, "testbitset"_n, "ibc"_n, mvo() - ("bitset_string", "03") //bitset bytes are reversed, so we do the same to test + ("bitset_string", "03") ("bitset_vector", bitset_2) ("finalizers_count", 3) ); From cc208198be4615e72f5f4a61c559799d845793b1 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 9 Dec 2024 05:37:12 -0600 Subject: [PATCH 68/75] Updated finality_violation tests to reflect new bitset string format --- unittests/svnn_finality_violation_tests.cpp | 4 ++-- .../savanna/finality_violation/finality_violation.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 6abdb33741..3e8d86ac70 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -149,8 +149,8 @@ void shouldPass(const finality_proof::proof_test_cluster& chain, const account_n std::pair blame = fc::raw::unpack>(trace.return_value); //finalizers 0 and 1 are guilty, while finalizer 2 and 3 are innocent, see bitset tests in svnn_ibc_tests - BOOST_TEST(blame.first == "30"); //0011 (reverse order) - BOOST_TEST(blame.second == "c0"); //1100 (reverse order) + BOOST_TEST(blame.first == "03"); //0011 + BOOST_TEST(blame.second == "0c"); //1100 } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 5bdd0a5d2e..eeffc01218 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -24,7 +24,6 @@ savanna::bitset create_bitset(const size_t finalizers_count, const std::optional } else if (strong_votes.has_value()) return savanna::bitset(finalizers_count, strong_votes.value()); - else return savanna::bitset(finalizers_count, weak_votes.value()); } From ad567097241147a738b90de66ee7e039435d5473 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 9 Dec 2024 08:35:28 -0600 Subject: [PATCH 69/75] Adjustments to the finality violation contract to support conditional merging of strong and weak votes when evaluating responsibility in finality violation proofs --- .../finality_violation/finality_violation.cpp | 38 +++++++++++++----- .../finality_violation/finality_violation.hpp | 6 +++ .../finality_violation.wasm | Bin 53201 -> 53317 bytes 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index eeffc01218..985bde9044 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -1,8 +1,14 @@ #include "finality_violation.hpp" savanna::bitset merge_bitsets(const savanna::bitset& bitset_1, const savanna::bitset& bitset_2){ + + //check that bitsets are of the same size check(bitset_1.size()==bitset_2.size(), "cannot merge bitsets of different sizes"); + + //create a new bitset of the same size as the input bitsets savanna::bitset result_bitset(bitset_1.size()); + + //merge the bitsets by setting the bits that are set in either of the input bitsets into the result bitset for (size_t i = 0 ; i < bitset_1.size(); i++){ if (bitset_1.test(i) || bitset_2.test(i)) result_bitset.set(i); } @@ -11,10 +17,12 @@ savanna::bitset merge_bitsets(const savanna::bitset& bitset_1, const savanna::bi savanna::bitset create_bitset(const size_t finalizers_count, const std::optional>& strong_votes, const std::optional>& weak_votes){ + //check that at least one set of votes is present check(strong_votes.has_value() || weak_votes.has_value(), "must have at least one set of votes to create a bitset"); - savanna::bitset result_bitset(finalizers_count); + savanna::bitset result_bitset(finalizers_count); + //if both strong and weak votes are present, merge them if (strong_votes.has_value() && weak_votes.has_value()){ savanna::bitset strong_bitset(finalizers_count, strong_votes.value()); @@ -23,24 +31,32 @@ savanna::bitset create_bitset(const size_t finalizers_count, const std::optional return merge_bitsets(strong_bitset, weak_bitset); } + //if only strong votes are present, use them else if (strong_votes.has_value()) return savanna::bitset(finalizers_count, strong_votes.value()); + //if only weak votes are present, use them else return savanna::bitset(finalizers_count, weak_votes.value()); } -std::pair check_bitsets(const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2){ +std::pair check_bitsets(const finalizer_policy_input& finalizer_policy, const finality_proof& high_proof, const finality_proof& low_proof, const bool high_proof_strong_votes_only = false, const bool low_proof_strong_votes_only = false){ + + std::optional> hsv = high_proof.active_policy_qc.strong_votes; + std::optional> lsv = low_proof.active_policy_qc.strong_votes; - std::optional> sv1 = proof_1.active_policy_qc.strong_votes; - std::optional> sv2 = proof_2.active_policy_qc.strong_votes; + std::optional> hwv = high_proof.active_policy_qc.weak_votes; + std::optional> lwv = low_proof.active_policy_qc.weak_votes; - std::optional> wv1 = proof_1.active_policy_qc.weak_votes; - std::optional> wv2 = proof_2.active_policy_qc.weak_votes; + //if bitset verification applies only to strong votes, remove weak votes + if (high_proof_strong_votes_only) hwv = std::nullopt; + if (low_proof_strong_votes_only) lwv = std::nullopt; - savanna::bitset proof_1_bitset = create_bitset(finalizer_policy.finalizers.size(), sv1, wv1); - savanna::bitset proof_2_bitset = create_bitset(finalizer_policy.finalizers.size(), sv2, wv2); + //create bitsets + savanna::bitset high_proof_bitset = create_bitset(finalizer_policy.finalizers.size(), hsv, hwv); + savanna::bitset low_proof_bitset = create_bitset(finalizer_policy.finalizers.size(), lsv, lwv); - auto result = bitset::compare(proof_1_bitset, proof_2_bitset); + //compare bitsets + auto result = bitset::compare(high_proof_bitset, low_proof_bitset); return result; @@ -117,7 +133,7 @@ std::pair finality_violation::rule2( const finalizer //Proof of rule #2 finality violation - auto result = check_bitsets(finalizer_policy, high_proof, low_proof); + auto result = check_bitsets(finalizer_policy, high_proof, low_proof, false, true); return {result.first.to_string(), result.second.to_string()}; @@ -156,7 +172,7 @@ std::pair finality_violation::rule3( const finalizer //Proof of rule #3 finality violation - auto result = check_bitsets(finalizer_policy, high_proof, low_proof); + auto result = check_bitsets(finalizer_policy, high_proof, low_proof, true, false); return {result.first.to_string(), result.second.to_string()}; diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index 131ac86a4e..e013970859 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -28,6 +28,12 @@ CONTRACT finality_violation : public contract { const finality_proof& low_proof, const std::vector& reversible_blocks_digests); + + + // compare two bitsets + static std::pair compare_qc(const quorum_certificate_input& qc1, const quorum_certificate_input& qc2); + + //For testing purposes, to verify that smart contract merkle tree implementation matches Spring merkle tree implementation ACTION testmroot(const checksum256& root, const std::vector& reversible_blocks_digests); }; \ No newline at end of file diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index e30fa480bc2e1ba10c01220c9a6c95c3fc8eeced..2714de87c40eabca8c361b9f33dfdaceb3275d1d 100755 GIT binary patch delta 1016 zcmZuv&1%$86h8Odm}D4gv#417fzudh24*0OGA%NUb7h2Cj4;v>1iR9)(2RY7ZX$J- zT?i333i<{Wvk*kVjeA{)+u{p|PoU@C8^<_ylXG*vbME((e0eE9y^^mVOL0D*$pid; z_{RBmbM?>{PId*<2~0yb0&*2xg&lJ9AGa=L{L;od;Hnqe9v5XSfjr=fRa z3CL5~a0$sJFIW%#HjennWvmN%slI^6XK0tg8m}oa;61fN0_z%G^3F4mE2}?e8xW7PEVFc~tZr$bU+Nmx6|8wyQ^%Hs zJHAQg#lP~lIKpe&tC_C_`#K^=Gjl|>HYR-JvY`w49MP5z=c$S435NbOL>m#Kgf^lY zArEfUm00tcn`7#fF*U6eGAS*{kTL7{x*eT!`JVOAYa=l!mq?fV?D%r#5ocgIJd(^2 zX%20ZIhu98)p&5&2%p^;lTezkKvG+fL0szS=vnhioSDy3Wj3nJHuX-Bl2F-78uBLq z5-|o-74I|f8B%^GX>gxaE`lEoNE+0?0$<_=53Dk%usMd!_Jc})W!`T=lXYg=(*H8` zVhcLP=xLTMJ}mKGg`i&{*eUT*1t0&3;RMnMmPseUhJ!BX6T2%Ocs@jqi{$ArZ7SlLlhZgA|uRVgegs-P`harF`@0=O{Qfp^VuI+eBIXc`}#NM+DDNuCXOCf*wLVhBN?%5WsuLJZF81 zp%drBDL{#iyWmYkFrp)cLmbi~4Zn1g>t6yy!Ae9rAZu&1)ZZFWcTtmy%HCUl`&c)mQ%jH Date: Sun, 15 Dec 2024 08:09:41 -0600 Subject: [PATCH 70/75] Fixed savanna finality_violation contract evaluation of rule 2 and rule 3 --- unittests/svnn_finality_violation_tests.cpp | 12 ++- .../finality_violation/finality_violation.abi | 52 +++++++++++- .../finality_violation/finality_violation.cpp | 80 ++++++++++-------- .../finality_violation/finality_violation.hpp | 4 +- .../finality_violation.wasm | Bin 53317 -> 57058 bytes 5 files changed, 106 insertions(+), 42 deletions(-) diff --git a/unittests/svnn_finality_violation_tests.cpp b/unittests/svnn_finality_violation_tests.cpp index 3e8d86ac70..0039eb3dc6 100644 --- a/unittests/svnn_finality_violation_tests.cpp +++ b/unittests/svnn_finality_violation_tests.cpp @@ -137,7 +137,17 @@ mvo prepare_rule_2_3_proof( const finalizer_policy& active_finalizer_policy, ("strong_votes", finality_proof::finalizers_string(low_qc.active_policy_sig.strong_votes.value())) ) ) - ("reversible_blocks_digests", digests); + ("reversible_proof_of_inclusion", mvo() + ("target_block_index", 0) + ("final_block_index", 0) + ("target", mvo() + ("block_num", 0) + ("timestamp", low_qc_block.timestamp) + ("finality_digest", low_qc_block.finality_digest) + ("parent_timestamp", low_qc_block.parent_timestamp) + ) + ("merkle_branches", finality_proof::generate_proof_of_inclusion(digests, 0)) + ); } diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi index 65df7e736c..3e262e8eee 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.abi +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.abi @@ -45,6 +45,28 @@ } ] }, + { + "name": "block_ref_data", + "base": "", + "fields": [ + { + "name": "block_num", + "type": "uint32" + }, + { + "name": "timestamp", + "type": "block_timestamp_type" + }, + { + "name": "finality_digest", + "type": "checksum256" + }, + { + "name": "parent_timestamp", + "type": "block_timestamp_type" + } + ] + }, { "name": "finality_proof", "base": "", @@ -157,6 +179,28 @@ } ] }, + { + "name": "reversible_proof_of_inclusion", + "base": "", + "fields": [ + { + "name": "target_block_index", + "type": "uint64" + }, + { + "name": "final_block_index", + "type": "uint64" + }, + { + "name": "target", + "type": "block_ref_data" + }, + { + "name": "merkle_branches", + "type": "checksum256[]" + } + ] + }, { "name": "rule1", "base": "", @@ -192,8 +236,8 @@ "type": "finality_proof" }, { - "name": "reversible_blocks_digests", - "type": "checksum256[]" + "name": "reversible_proof_of_inclusion", + "type": "reversible_proof_of_inclusion" } ] }, @@ -214,8 +258,8 @@ "type": "finality_proof" }, { - "name": "reversible_blocks_digests", - "type": "checksum256[]" + "name": "reversible_proof_of_inclusion", + "type": "reversible_proof_of_inclusion" } ] }, diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp index 985bde9044..d6edc43376 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.cpp @@ -63,7 +63,7 @@ std::pair check_bitsets(const finalizer_policy } //Verify QCs presented as proof -void check_qcs( const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2){ +std::pair check_qcs( const finalizer_policy_input& finalizer_policy, const finality_proof& proof_1, const finality_proof& proof_2){ //Verify we have our level 3 commitments for both proofs check(proof_1.qc_block.level_3_commitments.has_value(), "level 3 commitments structure must be present in both proofs to prove a finality violation"); @@ -79,6 +79,8 @@ void check_qcs( const finalizer_policy_input& finalizer_policy, const finality_p _check_qc(proof_1.active_policy_qc, digest_1, finalizer_policy, false, false); _check_qc(proof_2.active_policy_qc, digest_2, finalizer_policy, false, false); + return {digest_1, digest_2}; + } //Rule #1 : Do not vote on different blocks with the same timestamp @@ -105,34 +107,39 @@ std::pair finality_violation::rule1(const finalizer_po std::pair finality_violation::rule2( const finalizer_policy_input& finalizer_policy, const finality_proof& high_proof, const finality_proof& low_proof, - const std::vector& reversible_blocks_digests){ + const reversible_proof_of_inclusion& proof_of_inclusion){ //Verify QCs - check_qcs(finalizer_policy, high_proof, low_proof); + auto finalizer_digests = check_qcs(finalizer_policy, high_proof, low_proof); //Compare timestamps block_timestamp high_proof_timestamp = high_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp low_proof_timestamp = low_proof.qc_block.level_3_commitments.value().timestamp; - block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - - //A time range conflict has occured if the low proof timestamp is contained within the high proof time range - bool time_range_conflict = high_proof_last_claim_timestamp < low_proof_timestamp && low_proof_timestamp < high_proof_timestamp; - check(time_range_conflict, "proofs must demonstrate a conflicting time range"); + block_timestamp high_proof_parent_timestamp = proof_of_inclusion.target.parent_timestamp; - //Compute the merkle root of the reversible digests, and verify that it matches the commitment of the high proof - check(get_merkle_root(reversible_blocks_digests) == high_proof.qc_block.level_3_commitments->reversible_blocks_mroot, "reversible_blocks_digests merkle root does not match reversible_blocks_mroot"); + //Verify that the proof of inclusion resolves to the reversible blocks mroot of the high proof + check(proof_of_inclusion.root() == high_proof.qc_block.level_3_commitments.value().reversible_blocks_mroot, "proof of inclusion must resolve to the reversible blocks mroot of the high proof"); - //Compute the finality digest of the low proof - checksum256 computed_digest = block_finality_data_internal(low_proof.qc_block).finality_digest(); + //A time range conflict has occured if the high proof timestamp is greater than or equal to the low proof timestamp and the high proof parent timestamp is less than the low proof timestamp + bool time_range_conflict = high_proof_parent_timestamp < low_proof_timestamp && high_proof_timestamp >= low_proof_timestamp; + check(time_range_conflict, "proofs must demonstrate a conflicting time range"); - //Verify that the computed digest for the low proof doesn't appear in the list of reversible block digests committed to by the high proof - auto f_itr = std::find(reversible_blocks_digests.begin(), reversible_blocks_digests.end(), computed_digest); + bool finality_violation = false; - check(f_itr==reversible_blocks_digests.end(), "finality digest of low block exists in reversible_blocks_digests vector"); + //If the timestamp for the submitted reversible blocks leaf node is strictly greater than low_proof_timestamp, we know that the low proof block is not an ancestor of the high proof block + //and therefore, a rule 2 violation has occurred. + if(proof_of_inclusion.target.timestamp > low_proof_timestamp) finality_violation = true; + else if(proof_of_inclusion.target.timestamp == low_proof_timestamp) { + //If the timestamp for the submitted reversible blocks leaf node is exactly equal to low_proof_timestamp, we need to compare the finality digest of the low proof block to the finality digest of the submitted reversible blocks leaf node, + //to check that they are not the same. If they are the same, the submitted proof is not correct. But if they are different, then we know that the low proof block is not an ancestor of the high proof block + check(finalizer_digests.second != proof_of_inclusion.target.finality_digest, "finality digest of low proof must be different from the finality digest of the submitted reversible blocks leaf node"); + finality_violation = true; + } + + check(finality_violation, "proofs must demonstrate a finality violation"); //Proof of rule #2 finality violation - auto result = check_bitsets(finalizer_policy, high_proof, low_proof, false, true); return {result.first.to_string(), result.second.to_string()}; @@ -143,34 +150,37 @@ std::pair finality_violation::rule2( const finalizer std::pair finality_violation::rule3( const finalizer_policy_input& finalizer_policy, const finality_proof& high_proof, const finality_proof& low_proof, - const std::vector& reversible_blocks_digests){ + const reversible_proof_of_inclusion& proof_of_inclusion){ //Verify QCs - check_qcs(finalizer_policy, high_proof, low_proof); + auto finalizer_digests = check_qcs(finalizer_policy, high_proof, low_proof); //Compare timestamps - block_timestamp low_proof_timestamp = low_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp high_proof_timestamp = high_proof.qc_block.level_3_commitments.value().timestamp; block_timestamp low_proof_last_claim_timestamp = low_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - block_timestamp high_proof_last_claim_timestamp = high_proof.qc_block.level_3_commitments.value().latest_qc_claim_timestamp; - //If the low proof timestamp is less than the high proof timestamp, but the high proof last QC claim timestamp is lower than or equal to the low proof last QC claim, the lock was violated - bool lock_violation = high_proof_last_claim_timestamp <= low_proof_last_claim_timestamp && low_proof_timestamp < high_proof_timestamp ; - - check(lock_violation, "proofs must demonstrate a lock violation"); - - //Compute the merkle root of the reversible digests, and verify that it matches the commitment of the high proof - check(get_merkle_root(reversible_blocks_digests) == high_proof.qc_block.level_3_commitments->reversible_blocks_mroot, "reversible_blocks_digests merkle root does not match reversible_blocks_mroot"); - - //Compute the finality digest of the low proof - checksum256 computed_digest = block_finality_data_internal(low_proof.qc_block).finality_digest(); - - //Verify that the computed digest for the low proof doesn't appear in the list of reversible block digests committed to by the high proof - auto f_itr = std::find(reversible_blocks_digests.begin(), reversible_blocks_digests.end(), computed_digest); + block_timestamp high_proof_parent_timestamp = proof_of_inclusion.target.parent_timestamp; - check(f_itr==reversible_blocks_digests.end(), "finality digest of low block exists in reversible_blocks_digests vector"); + //Verify that the proof of inclusion resolves to the reversible blocks mroot of the high proof + check(proof_of_inclusion.root() == high_proof.qc_block.level_3_commitments.value().reversible_blocks_mroot, "proof of inclusion must resolve to the reversible blocks mroot of the high proof"); - //Proof of rule #3 finality violation + //A lock violation has occured if the high proof timestamp is greater than or equal to the low proof last claim timestamp and the high proof parent timestamp is less than the low proof last claim timestamp + bool lock_violation = high_proof_timestamp >= low_proof_last_claim_timestamp && high_proof_parent_timestamp < low_proof_last_claim_timestamp; + check(lock_violation, "proofs must demonstrate a lock violation"); + + bool finality_violation = false; + + //If the timestamp for the submitted reversible blocks leaf node is strictly greater than low_proof_last_claim_timestamp, we know that the low proof block is not an ancestor of the high proof block + //and therefore, a rule 3 violation has occurred. + if(proof_of_inclusion.target.timestamp > low_proof_last_claim_timestamp) finality_violation = true; + else if(proof_of_inclusion.target.timestamp == low_proof_last_claim_timestamp) { + //If the timestamp for the submitted reversible blocks leaf node is exactly equal to low_proof_timestamp, we need to compare the finality digest of the low proof block to the finality digest of the submitted reversible blocks leaf node, + //to check that they are not the same. If they are the same, the submitted proof is not correct. But if they are different, then we know that the low proof block is not an ancestor of the high proof block + check(finalizer_digests.second != proof_of_inclusion.target.finality_digest, "finality digest of low proof must be different from the finality digest of the submitted reversible blocks leaf node"); + finality_violation = true; + } + + check(finality_violation, "proofs must demonstrate a finality violation"); auto result = check_bitsets(finalizer_policy, high_proof, low_proof, true, false); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp index e013970859..2126c0d0ad 100644 --- a/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp +++ b/unittests/test-contracts/savanna/finality_violation/finality_violation.hpp @@ -20,13 +20,13 @@ CONTRACT finality_violation : public contract { std::pair rule2( const finalizer_policy_input& finalizer_policy, const finality_proof& high_proof, const finality_proof& low_proof, - const std::vector& reversible_blocks_digests); + const reversible_proof_of_inclusion& reversible_proof_of_inclusion); [[eosio::action]] std::pair rule3( const finalizer_policy_input& finalizer_policy, const finality_proof& high_proof, const finality_proof& low_proof, - const std::vector& reversible_blocks_digests); + const reversible_proof_of_inclusion& reversible_proof_of_inclusion); diff --git a/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm b/unittests/test-contracts/savanna/finality_violation/finality_violation.wasm index 2714de87c40eabca8c361b9f33dfdaceb3275d1d..24f213f545e26506eeb63707a4664f5848c0ebe5 100755 GIT binary patch delta 11336 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 delta 8197 zcmeHL4RBS(6~4Ro-gj^Ef?V6!Jd%j}9x#1@Ac7basODm!14{itFd|hj37VH+B#AMg z@}h!8i;8Se1gvrVfmW=9>mU9`3u+aVsQgrFNx@OtPCIn8Q)+dl-`RWLAAydxGo4Py zFx)+7_nh6c=bZ2C?%V&OzO_>?(B?GHW16P1^=!c11uMIrSj+i>HhG?Y>O>uXAFvMG?j