From 8831b8458b38ac3ab1c69d3e146845ce27cceae5 Mon Sep 17 00:00:00 2001 From: cronicc Date: Sat, 4 Dec 2021 17:04:51 +0000 Subject: [PATCH 01/58] Add 'merkleTree' and 'scTxsCommitment' to 'getblocktemplate' --- qa/rpc-tests/sc_cert_getblocktemplate.py | 6 ++++++ src/rpc/mining.cpp | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/qa/rpc-tests/sc_cert_getblocktemplate.py b/qa/rpc-tests/sc_cert_getblocktemplate.py index 73d9600fb3..5955a9e9fe 100755 --- a/qa/rpc-tests/sc_cert_getblocktemplate.py +++ b/qa/rpc-tests/sc_cert_getblocktemplate.py @@ -198,6 +198,12 @@ def run_test(self): assert(len(self.nodes[i].getblocktemplate()['certificates']) == 1) assert(len(self.nodes[i].getblocktemplate()['transactions']) == 1) + # Check that `getblocktemplate` "merkleTree" and "scTxsCommitment" match `getblockmerkleroots` + for i in range(0, NUMB_OF_NODES): + gbt = self.nodes[i].getblocktemplate() + roots = self.nodes[i].getblockmerkleroots([gbt['coinbasetxn']['data']] + [x['data'] for x in gbt['transactions']], [x['data'] for x in gbt['certificates']]) + assert_equal(gbt['merkleTree'], roots['merkleTree']) + assert_equal(gbt['scTxsCommitment'], roots['scTxsCommitment']) if __name__ == '__main__': sc_cert_base().main() diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 8d970f1e4e..0aa1d1a7e3 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -443,6 +443,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"version\" : n, (numeric) the block version\n" + " \"merkleTree\" : \"xxxx\", (string) Merkleroot calculated on transactions and certificates. (Note: If you construct coinbasetxn outside of zend please use \"getblockmerkleroots\" RPC to calculate \"merkleTree\" and \"scTxsCommitment\".)\n" + " \"scTxsCommitment\" : \"xxxx\", (string) scTxsCommitment calculated on certificates.\n" " \"previousblockhash\" : \"xxxx\", (string) the hash of current highest block\n" " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" " {\n" @@ -711,8 +713,15 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) } UniValue result(UniValue::VOBJ); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + if (certSupported) { + CCoinsViewCache view(pcoinsTip); + pblock->hashScTxsCommitment = pblock->BuildScTxsCommitment(view); + } result.pushKV("capabilities", aCaps); result.pushKV("version", pblock->nVersion); + result.pushKV("merkleTree", pblock->hashMerkleRoot.ToString()); + result.pushKV("scTxsCommitment", pblock->hashScTxsCommitment.ToString()); result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); result.pushKV("transactions", transactions); if (certSupported) From 3fe17d94dd05f675741539017cb2368e34141bf0 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Wed, 29 Dec 2021 16:06:54 +0100 Subject: [PATCH 02/58] Fixed an issue with the validation of custom fields The validation is performed by checking that a field element doesn't use more bits than declared during sidechain creation. The function performing such validation was handling bits with wrong endianness. --- src/gtest/test_libzendoo.cpp | 23 +++++++++++++++++++++++ src/sc/sidechaintypes.cpp | 4 ++-- src/util.cpp | 21 ++++++++------------- src/util.h | 2 +- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/gtest/test_libzendoo.cpp b/src/gtest/test_libzendoo.cpp index 0210ac4a28..80e048cd51 100644 --- a/src/gtest/test_libzendoo.cpp +++ b/src/gtest/test_libzendoo.cpp @@ -1973,4 +1973,27 @@ TEST(CctpLibrary, TestInvalidProofVkWhenOversized) EXPECT_FALSE(vkInvalid.IsValid()); //TODO: Might be useful to test the same behaviour with bit vector +} + +TEST(CctpLibrary, TestCustomFieldsValidation) +{ + for (uint8_t i = 1; i < CHAR_BIT; i++) + { + for (uint8_t j = 1; j > 0; j++) + { + std::vector rawBytes = { j }; + FieldElementCertificateField certField = FieldElementCertificateField(rawBytes); + FieldElementCertificateFieldConfig config = FieldElementCertificateFieldConfig(i); + CFieldElement fe = certField.GetFieldElement(config); + + if (j < 1 << i) + { + EXPECT_TRUE(fe.IsValid()); + } + else + { + EXPECT_FALSE(fe.IsValid()); + } + } + } } \ No newline at end of file diff --git a/src/sc/sidechaintypes.cpp b/src/sc/sidechaintypes.cpp index c9ad74a4eb..d418331e27 100644 --- a/src/sc/sidechaintypes.cpp +++ b/src/sc/sidechaintypes.cpp @@ -595,7 +595,7 @@ const CFieldElement& FieldElementCertificateField::GetFieldElement(const FieldEl { // check null bits in the last byte are as expected unsigned char lastByte = vRawData.back(); - int numbOfZeroBits = getTrailingZeroBitsInByte(lastByte); + int numbOfZeroBits = getLeadingZeroBitsInByte(lastByte); if (numbOfZeroBits < (CHAR_BIT - rem)) { LogPrint("sc", "%s():%d - ERROR: wrong number of null bits in last byte[0x%x]: %d vs %d\n", @@ -605,7 +605,7 @@ const CFieldElement& FieldElementCertificateField::GetFieldElement(const FieldEl } std::vector extendedRawData = vRawData; - extendedRawData.insert(extendedRawData.begin(), CFieldElement::ByteSize()-vRawData.size(), 0x0); + extendedRawData.insert(extendedRawData.end(), CFieldElement::ByteSize() - vRawData.size(), 0x0); fieldElement.SetByteArray(extendedRawData); if (fieldElement.IsValid()) diff --git a/src/util.cpp b/src/util.cpp index eb68fa89f7..f76f62cbc0 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -17,6 +17,7 @@ #include "utiltime.h" #include +#include #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) #include @@ -934,21 +935,15 @@ int GetNumCores() return boost::thread::physical_concurrency(); } -int getTrailingZeroBitsInByte(unsigned char inputByte) +int getLeadingZeroBitsInByte(unsigned char inputByte) { - // output: c will count inputByte's trailing zero bits, - // so if inputByte is 1101000 (base 2), then c will be 3 - int c = CHAR_BIT; + // Behavior of __builtin_clz() is undefined for zero input, so we need to handle this case explicitly. + if (inputByte == 0) + return 8; - if (inputByte) - { - inputByte = (inputByte ^ (inputByte - 1)) >> 1; // Set inputByte's trailing 0s to 1s and zero rest - for (c = 0; inputByte; c++) - { - inputByte >>= 1; - } - } - return c; + // __builtin_clz() returns the number of leading zeros for an unsigned int, so we need to perform a + // conversion to use it for an unsigned char. + return __builtin_clz(inputByte) % (sizeof(unsigned int) * 8 - CHAR_BIT); } int getBytesFromBits(int nbits, int& reminder) diff --git a/src/util.h b/src/util.h index ec27d3515a..818c273ef8 100644 --- a/src/util.h +++ b/src/util.h @@ -296,7 +296,7 @@ std::string dbg_blk_unlinked(); std::string dbg_blk_candidates(); std::string dbg_blk_global_tips(); -int getTrailingZeroBitsInByte(unsigned char inputByte); +int getLeadingZeroBitsInByte(unsigned char inputByte); int getBytesFromBits(int nbits, int& reminder); #endif // BITCOIN_UTIL_H From f3c672ddcec5816f2ddd930c9df1d416f3b1b4a3 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Thu, 30 Dec 2021 10:56:13 +0100 Subject: [PATCH 03/58] Fixed failing Python tests due to malformed custom fields --- qa/rpc-tests/sc_big_block.py | 2 +- qa/rpc-tests/sc_cert_customfields.py | 20 ++++++++++---------- qa/rpc-tests/sc_rpc_cmds_json_output.py | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/qa/rpc-tests/sc_big_block.py b/qa/rpc-tests/sc_big_block.py index f7cbf0902d..f96d7ca38b 100755 --- a/qa/rpc-tests/sc_big_block.py +++ b/qa/rpc-tests/sc_big_block.py @@ -177,7 +177,7 @@ def advance_sidechains_epoch(num_of_scs): vCfe = ["ab000100"] vCmt = [BIT_VECTOR_BUF] - fe1 = "00000000000000000000000000000000000000000000000000000000" + "ab000100" + fe1 = "ab000100" + "00000000000000000000000000000000000000000000000000000000" fe2 = BIT_VECTOR_FE proofCfeArray = [fe1, fe2] diff --git a/qa/rpc-tests/sc_cert_customfields.py b/qa/rpc-tests/sc_cert_customfields.py index dece5bae17..d1e1dd01a2 100755 --- a/qa/rpc-tests/sc_cert_customfields.py +++ b/qa/rpc-tests/sc_cert_customfields.py @@ -346,7 +346,7 @@ def run_test(self): vCmt = [] # serialized fe for the proof has 32 byte size - fe1 = "000000000000000000000000000000000000000000000000000000000000" + "0100" + fe1 = "0100" + "000000000000000000000000000000000000000000000000000000000000" scProof3 = mcTest.create_test_proof( 'sc2', scid2_swapped, epoch_number_1, 10, MBTR_SC_FEE, FT_SC_FEE, epoch_cum_tree_hash_1, constant2, [addr_node1], [bwt_amount], @@ -426,9 +426,9 @@ def run_test(self): # with the one declared during sidechain creation. vCmt = [BIT_VECTOR_BUF_NOT_POW2] - fe1 = "00000000000000000000000000000000000000000000000000000000" + "ab000100" - fe2 = "0000000000000000000000000000000000000000000000000000" + "ccccdddd0000" - fe3 = "000000000000000000000000000000000000000000000000000000000000" + "0100" + fe1 = "ab000100" + "00000000000000000000000000000000000000000000000000000000" + fe2 = "ccccdddd0000" + "0000000000000000000000000000000000000000000000000000" + fe3 = "0100" + "000000000000000000000000000000000000000000000000000000000000" fe4 = BIT_VECTOR_FE scProof3 = mcTest.create_test_proof( @@ -466,9 +466,9 @@ def run_test(self): # Such a bit vector should be rejected from the node. vCmt = [BIT_VECTOR_BUF_HUGE] - fe1 = "00000000000000000000000000000000000000000000000000000000" + "ab000100" - fe2 = "0000000000000000000000000000000000000000000000000000" + "ccccdddd0000" - fe3 = "000000000000000000000000000000000000000000000000000000000000" + "0100" + fe1 = "ab000100" + "00000000000000000000000000000000000000000000000000000000" + fe2 = "ccccdddd0000" + "0000000000000000000000000000000000000000000000000000" + fe3 = "0100" + "000000000000000000000000000000000000000000000000000000000000" fe4 = BIT_VECTOR_FE scProof3 = mcTest.create_test_proof( @@ -505,9 +505,9 @@ def run_test(self): # this is a compressed buffer which will yield a valid field element for the proof (see below) vCmt = [BIT_VECTOR_BUF] - fe1 = "00000000000000000000000000000000000000000000000000000000" + "ab000100" - fe2 = "0000000000000000000000000000000000000000000000000000" + "ccccdddd0000" - fe3 = "000000000000000000000000000000000000000000000000000000000000" + "0100" + fe1 = "ab000100" + "00000000000000000000000000000000000000000000000000000000" + fe2 = "ccccdddd0000" + "0000000000000000000000000000000000000000000000000000" + fe3 = "0100" + "000000000000000000000000000000000000000000000000000000000000" fe4 = BIT_VECTOR_FE scProof3 = mcTest.create_test_proof( diff --git a/qa/rpc-tests/sc_rpc_cmds_json_output.py b/qa/rpc-tests/sc_rpc_cmds_json_output.py index 12b99ffb40..f2d4406f9b 100755 --- a/qa/rpc-tests/sc_rpc_cmds_json_output.py +++ b/qa/rpc-tests/sc_rpc_cmds_json_output.py @@ -326,7 +326,7 @@ def dump_json_getblocktemplate(fileName, nodeid=0): vCmt = [] # serialized fe for the proof has 32 byte size - fe1 = "000000000000000000000000000000000000000000000000000000000000" + "0100" + fe1 = "0100" + "000000000000000000000000000000000000000000000000000000000000" quality = 72 scProof3 = certMcTest.create_test_proof( @@ -370,9 +370,9 @@ def dump_json_getblocktemplate(fileName, nodeid=0): # this is a compressed buffer which will yield a valid field element for the proof (see below) vCmt = [BIT_VECTOR_BUF] - fe1 = "00000000000000000000000000000000000000000000000000000000" + "ab000100" - fe2 = "0000000000000000000000000000000000000000000000000000" + "ccccdddd0000" - fe3 = "000000000000000000000000000000000000000000000000000000000000" + "0100" + fe1 = "ab000100" + "00000000000000000000000000000000000000000000000000000000" + fe2 = "ccccdddd0000" + "0000000000000000000000000000000000000000000000000000" + fe3 = "0100" + "000000000000000000000000000000000000000000000000000000000000" fe4 = BIT_VECTOR_FE quality = 18 From 7267da211e5e0fad639ca9cea7d20a0c6658daf8 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Mon, 10 Jan 2022 12:28:31 +0100 Subject: [PATCH 04/58] Increased unit test coverage for custom fields validation --- src/gtest/test_libzendoo.cpp | 114 ++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/src/gtest/test_libzendoo.cpp b/src/gtest/test_libzendoo.cpp index 80e048cd51..9b6b4023de 100644 --- a/src/gtest/test_libzendoo.cpp +++ b/src/gtest/test_libzendoo.cpp @@ -19,6 +19,15 @@ using namespace blockchain_test_utils; +// Random uint used to generate a random custom field +const uint32_t RANDOM_CUSTOM_FIELD_SEED = 1641809674; + +// Random custom field generated from the seed above +const uint8_t RANDOM_CUSTOM_FIELD[] = {0xbe, 0x61, 0x16, 0xab, 0x27, 0xee, 0xab, 0xbc, + 0x09, 0x35, 0xb3, 0xe2, 0x1b, 0xc3, 0xcf, 0xcd, + 0x3f, 0x06, 0xac, 0xb3, 0x8a, 0x5c, 0xeb, 0xd4, + 0x42, 0xf4, 0x96, 0xd8, 0xbf, 0xd3, 0x8e, 0x7d}; + static CMutableTransaction CreateDefaultTx() { // Create a tx with a sc creation, a fwt, a bwtr and a csw @@ -1975,6 +1984,16 @@ TEST(CctpLibrary, TestInvalidProofVkWhenOversized) //TODO: Might be useful to test the same behaviour with bit vector } +TEST(CctpLibrary, TestRandomCustomFieldGeneration) +{ + srand(RANDOM_CUSTOM_FIELD_SEED); + + for (int i = 0; i < 32; i++) + { + ASSERT_EQ(rand() % 256, RANDOM_CUSTOM_FIELD[i]); + } +} + TEST(CctpLibrary, TestCustomFieldsValidation) { for (uint8_t i = 1; i < CHAR_BIT; i++) @@ -1996,4 +2015,97 @@ TEST(CctpLibrary, TestCustomFieldsValidation) } } } -} \ No newline at end of file +} + +/** + * @brief Test the validation of a full (32 bytes) random custom field + * iteratively changing the last byte. + */ +TEST(CctpLibrary, TestFullCustomFieldValidation) +{ + std::vector rawBytes(std::begin(RANDOM_CUSTOM_FIELD), std::end(RANDOM_CUSTOM_FIELD)); + + for (uint8_t i = 1; i < CHAR_BIT; i++) + { + for (uint8_t j = 1; j > 0; j++) + { + rawBytes.back() = j; + FieldElementCertificateField certField = FieldElementCertificateField(rawBytes); + FieldElementCertificateFieldConfig config = FieldElementCertificateFieldConfig(i + 31 * CHAR_BIT); + CFieldElement fe = certField.GetFieldElement(config); + + // The Field Element is valid if it matches the configuration (j < 1 << i) + // and if doesn't exceed the modulus, thus the last two bits cannot be set (j < 1 << 6). + if (j < 1 << i && j < 1 << 6) + { + EXPECT_TRUE(fe.IsValid()); + } + else + { + EXPECT_FALSE(fe.IsValid()); + } + } + } +} + +/** + * @brief Test the validation of a random custom field + * with a size equivalent to a long integer (64 bits, 8 bytes) + * iteratively changing the last byte. + */ +TEST(CctpLibrary, TestLongIntCustomFieldValidation) +{ + std::vector rawBytes(std::begin(RANDOM_CUSTOM_FIELD), std::end(RANDOM_CUSTOM_FIELD)); + rawBytes.resize(8); + + for (uint8_t i = 1; i < CHAR_BIT; i++) + { + for (uint8_t j = 1; j > 0; j++) + { + rawBytes.back() = j; + FieldElementCertificateField certField = FieldElementCertificateField(rawBytes); + FieldElementCertificateFieldConfig config = FieldElementCertificateFieldConfig(i + 7 * CHAR_BIT); + CFieldElement fe = certField.GetFieldElement(config); + + if (j < 1 << i) + { + EXPECT_TRUE(fe.IsValid()); + } + else + { + EXPECT_FALSE(fe.IsValid()); + } + } + } +} + +/** + * @brief Test the validation of a random custom field + * with a size equivalent to an integer (32 bits, 4 bytes) + * iteratively changing the last byte. + */ +TEST(CctpLibrary, TestIntCustomFieldValidation) +{ + std::vector rawBytes(std::begin(RANDOM_CUSTOM_FIELD), std::end(RANDOM_CUSTOM_FIELD)); + rawBytes.resize(4); + + for (uint8_t i = 1; i < CHAR_BIT; i++) + { + for (uint8_t j = 1; j > 0; j++) + { + rawBytes.back() = j; + FieldElementCertificateField certField = FieldElementCertificateField(rawBytes); + FieldElementCertificateFieldConfig config = FieldElementCertificateFieldConfig(i + 3 * CHAR_BIT); + CFieldElement fe = certField.GetFieldElement(config); + + if (j < 1 << i) + { + EXPECT_TRUE(fe.IsValid()); + } + else + { + EXPECT_FALSE(fe.IsValid()); + } + } + } +} From db1a66f2005b9348c04ee9b06c1aef8907622837 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Mon, 10 Jan 2022 12:51:41 +0100 Subject: [PATCH 05/58] Implemented a "custom" function to compute trailing zero bits in a byte This has been done to increase compatibility and avoid using a built-in function available only on specific architecture. --- src/gtest/test_libzendoo.cpp | 10 ++++++++++ src/util.cpp | 21 +++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/gtest/test_libzendoo.cpp b/src/gtest/test_libzendoo.cpp index 9b6b4023de..54c018c25c 100644 --- a/src/gtest/test_libzendoo.cpp +++ b/src/gtest/test_libzendoo.cpp @@ -1994,6 +1994,16 @@ TEST(CctpLibrary, TestRandomCustomFieldGeneration) } } +TEST(CctpLibrary, TestGetLeadingZeros) +{ + ASSERT_EQ(8, getLeadingZeroBitsInByte(0)); + + for (uint8_t n = 1; n > 0; n++) + { + ASSERT_EQ(__builtin_clz(n) % (sizeof(unsigned int) * 8 - CHAR_BIT), getLeadingZeroBitsInByte(n)); + } +} + TEST(CctpLibrary, TestCustomFieldsValidation) { for (uint8_t i = 1; i < CHAR_BIT; i++) diff --git a/src/util.cpp b/src/util.cpp index f76f62cbc0..0f6c64bd3d 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -935,15 +935,24 @@ int GetNumCores() return boost::thread::physical_concurrency(); } +/** + * @brief Get the Leading Zero Bits in a byte + * (e.g 00000100 => 5 trailing zero bits). + * + * @param inputByte the byte to be checked + * @return int The number of trailing zero bits found. + */ int getLeadingZeroBitsInByte(unsigned char inputByte) { - // Behavior of __builtin_clz() is undefined for zero input, so we need to handle this case explicitly. - if (inputByte == 0) - return 8; + int nonZeroBits = 0; + + while (inputByte > 0) + { + inputByte >>= 1; + nonZeroBits++; + } - // __builtin_clz() returns the number of leading zeros for an unsigned int, so we need to perform a - // conversion to use it for an unsigned char. - return __builtin_clz(inputByte) % (sizeof(unsigned int) * 8 - CHAR_BIT); + return CHAR_BIT - nonZeroBits; } int getBytesFromBits(int nbits, int& reminder) From 304694d2ecefdfe23ee5750d1c44e1b4562ca5ba Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Thu, 13 Jan 2022 18:03:30 +0100 Subject: [PATCH 06/58] Added a unit test for the GetBytesFromBits() utility function Also added a comment to explain the reason behind the implementation of the TestRandomCustomFieldGeneration unit test. --- src/gtest/test_libzendoo.cpp | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/gtest/test_libzendoo.cpp b/src/gtest/test_libzendoo.cpp index 54c018c25c..d03dea2e0a 100644 --- a/src/gtest/test_libzendoo.cpp +++ b/src/gtest/test_libzendoo.cpp @@ -17,6 +17,8 @@ #include #include // for MC_CRYPTO_LIB_MOCKED +#include + using namespace blockchain_test_utils; // Random uint used to generate a random custom field @@ -1984,6 +1986,15 @@ TEST(CctpLibrary, TestInvalidProofVkWhenOversized) //TODO: Might be useful to test the same behaviour with bit vector } +/** + * @brief Check the generation of the RANDOM_CUSTOM_FIELD + * This test is only meant to show how the random custom field has been generated. + * We initialized random generator with random seeds until we got a sequence of bytes + * so that the first byte has the most significant bit set. + * + * This is done to avoid that the tests related to the validation of the custom fields pass + * even though the should fail (for instance, due to the usage of the wrong endianness). + */ TEST(CctpLibrary, TestRandomCustomFieldGeneration) { srand(RANDOM_CUSTOM_FIELD_SEED); @@ -1992,6 +2003,9 @@ TEST(CctpLibrary, TestRandomCustomFieldGeneration) { ASSERT_EQ(rand() % 256, RANDOM_CUSTOM_FIELD[i]); } + + // Check that the 8th bit of the first byte is set + ASSERT_EQ(RANDOM_CUSTOM_FIELD[0] & 0x80, 0x80); } TEST(CctpLibrary, TestGetLeadingZeros) @@ -2004,6 +2018,41 @@ TEST(CctpLibrary, TestGetLeadingZeros) } } +/** + * @brief Check the correctness of the GetBytesFromBits utility function + * + * In order to keep the test fast, the iteration is limited to the first 2^16 numbers. + */ +TEST(CctpLibrary, TestGetBytesFromBits) +{ + int reminder = 0; + + // Check that the function works properly with the "0" input + ASSERT_EQ(0, getBytesFromBits(0, reminder)); + ASSERT_EQ(0, reminder); + + for (uint16_t n = 1; n > 0; n++) + { + // Allocate a set of n bits + boost::dynamic_bitset bitset(n); + + // Convert the bit set to byte vector + std::vector bytes; + boost::to_block_range(bitset, std::back_inserter(bytes)); + + ASSERT_EQ(bytes.size(), getBytesFromBits(n, reminder)); + + if (reminder == 0) + { + ASSERT_EQ(0, n % CHAR_BIT); + } + else + { + ASSERT_EQ(reminder, n - (bytes.size() - 1) * CHAR_BIT); + } + } +} + TEST(CctpLibrary, TestCustomFieldsValidation) { for (uint8_t i = 1; i < CHAR_BIT; i++) From bb69ac6b356d43ec8011be38408b5f230837d3dd Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Fri, 14 Jan 2022 15:52:43 +0100 Subject: [PATCH 07/58] Removed UT failing on Mac OS due to different rand() implementation Also added a check to TestGetBytesFromBits for negative values. --- src/gtest/test_libzendoo.cpp | 50 ++++++++++++------------------------ 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/src/gtest/test_libzendoo.cpp b/src/gtest/test_libzendoo.cpp index d03dea2e0a..03bf99d496 100644 --- a/src/gtest/test_libzendoo.cpp +++ b/src/gtest/test_libzendoo.cpp @@ -21,14 +21,16 @@ using namespace blockchain_test_utils; -// Random uint used to generate a random custom field -const uint32_t RANDOM_CUSTOM_FIELD_SEED = 1641809674; - -// Random custom field generated from the seed above -const uint8_t RANDOM_CUSTOM_FIELD[] = {0xbe, 0x61, 0x16, 0xab, 0x27, 0xee, 0xab, 0xbc, - 0x09, 0x35, 0xb3, 0xe2, 0x1b, 0xc3, 0xcf, 0xcd, - 0x3f, 0x06, 0xac, 0xb3, 0x8a, 0x5c, 0xeb, 0xd4, - 0x42, 0xf4, 0x96, 0xd8, 0xbf, 0xd3, 0x8e, 0x7d}; +/** + * @brief Custom field randomly generated from seed 1641809674 using the rand() function. + * Also the seed has been chosen randomly so that the first byte of the custom field + * has the most significant bit set (to avoid that unit tests pass due to wrong endianness + * even when they should not). + */ +const uint8_t TEST_CUSTOM_FIELD[] = {0xbe, 0x61, 0x16, 0xab, 0x27, 0xee, 0xab, 0xbc, + 0x09, 0x35, 0xb3, 0xe2, 0x1b, 0xc3, 0xcf, 0xcd, + 0x3f, 0x06, 0xac, 0xb3, 0x8a, 0x5c, 0xeb, 0xd4, + 0x42, 0xf4, 0x96, 0xd8, 0xbf, 0xd3, 0x8e, 0x7d}; static CMutableTransaction CreateDefaultTx() { @@ -1986,28 +1988,6 @@ TEST(CctpLibrary, TestInvalidProofVkWhenOversized) //TODO: Might be useful to test the same behaviour with bit vector } -/** - * @brief Check the generation of the RANDOM_CUSTOM_FIELD - * This test is only meant to show how the random custom field has been generated. - * We initialized random generator with random seeds until we got a sequence of bytes - * so that the first byte has the most significant bit set. - * - * This is done to avoid that the tests related to the validation of the custom fields pass - * even though the should fail (for instance, due to the usage of the wrong endianness). - */ -TEST(CctpLibrary, TestRandomCustomFieldGeneration) -{ - srand(RANDOM_CUSTOM_FIELD_SEED); - - for (int i = 0; i < 32; i++) - { - ASSERT_EQ(rand() % 256, RANDOM_CUSTOM_FIELD[i]); - } - - // Check that the 8th bit of the first byte is set - ASSERT_EQ(RANDOM_CUSTOM_FIELD[0] & 0x80, 0x80); -} - TEST(CctpLibrary, TestGetLeadingZeros) { ASSERT_EQ(8, getLeadingZeroBitsInByte(0)); @@ -2031,6 +2011,10 @@ TEST(CctpLibrary, TestGetBytesFromBits) ASSERT_EQ(0, getBytesFromBits(0, reminder)); ASSERT_EQ(0, reminder); + // Check that the function works properly with a negative input + ASSERT_EQ(0, getBytesFromBits(-1, reminder)); + ASSERT_EQ(0, reminder); + for (uint16_t n = 1; n > 0; n++) { // Allocate a set of n bits @@ -2082,7 +2066,7 @@ TEST(CctpLibrary, TestCustomFieldsValidation) */ TEST(CctpLibrary, TestFullCustomFieldValidation) { - std::vector rawBytes(std::begin(RANDOM_CUSTOM_FIELD), std::end(RANDOM_CUSTOM_FIELD)); + std::vector rawBytes(std::begin(TEST_CUSTOM_FIELD), std::end(TEST_CUSTOM_FIELD)); for (uint8_t i = 1; i < CHAR_BIT; i++) { @@ -2114,7 +2098,7 @@ TEST(CctpLibrary, TestFullCustomFieldValidation) */ TEST(CctpLibrary, TestLongIntCustomFieldValidation) { - std::vector rawBytes(std::begin(RANDOM_CUSTOM_FIELD), std::end(RANDOM_CUSTOM_FIELD)); + std::vector rawBytes(std::begin(TEST_CUSTOM_FIELD), std::end(TEST_CUSTOM_FIELD)); rawBytes.resize(8); for (uint8_t i = 1; i < CHAR_BIT; i++) @@ -2145,7 +2129,7 @@ TEST(CctpLibrary, TestLongIntCustomFieldValidation) */ TEST(CctpLibrary, TestIntCustomFieldValidation) { - std::vector rawBytes(std::begin(RANDOM_CUSTOM_FIELD), std::end(RANDOM_CUSTOM_FIELD)); + std::vector rawBytes(std::begin(TEST_CUSTOM_FIELD), std::end(TEST_CUSTOM_FIELD)); rawBytes.resize(4); for (uint8_t i = 1; i < CHAR_BIT; i++) From 46bf7cf292268776c997466017b60351238ff4e7 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Fri, 14 Jan 2022 19:00:34 +0100 Subject: [PATCH 08/58] Added a flag to getblocktemplate to include merkle roots in the JSON --- qa/rpc-tests/sc_cert_getblocktemplate.py | 10 +++++++++- src/rpc/client.cpp | 10 ++++++++++ src/rpc/mining.cpp | 21 +++++++++++++++++---- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/qa/rpc-tests/sc_cert_getblocktemplate.py b/qa/rpc-tests/sc_cert_getblocktemplate.py index 5955a9e9fe..22ad54b88b 100755 --- a/qa/rpc-tests/sc_cert_getblocktemplate.py +++ b/qa/rpc-tests/sc_cert_getblocktemplate.py @@ -198,9 +198,17 @@ def run_test(self): assert(len(self.nodes[i].getblocktemplate()['certificates']) == 1) assert(len(self.nodes[i].getblocktemplate()['transactions']) == 1) - # Check that `getblocktemplate` "merkleTree" and "scTxsCommitment" match `getblockmerkleroots` for i in range(0, NUMB_OF_NODES): + # Check that `getblocktemplate` doesn't include "merkleTree" and "scTxsCommitment" if not explicitly requested gbt = self.nodes[i].getblocktemplate() + assert_false('merkleTree' in gbt) + assert_false('scTxsCommitment' in gbt) + gbt = self.nodes[i].getblocktemplate({}, False) + assert_false('merkleTree' in gbt) + assert_false('scTxsCommitment' in gbt) + + # Check that `getblocktemplate` "merkleTree" and "scTxsCommitment" match `getblockmerkleroots` + gbt = self.nodes[i].getblocktemplate({}, True) roots = self.nodes[i].getblockmerkleroots([gbt['coinbasetxn']['data']] + [x['data'] for x in gbt['transactions']], [x['data'] for x in gbt['certificates']]) assert_equal(gbt['merkleTree'], roots['merkleTree']) assert_equal(gbt['scTxsCommitment'], roots['scTxsCommitment']) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index c183a9bb00..3a8e24fc49 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -21,6 +21,15 @@ class CRPCConvertParam int paramIdx; //! 0-based idx of param to convert }; +/** + * @brief A list of RPC command parameters that need to be converted. + * + * In particular, this list must include any parameter that is not a string, + * otherwhise the related command would not work if used from the zen-cli. + * + * Note that JSON object arguments must be included in this list. + * + */ static const CRPCConvertParam vRPCConvertParams[] = { { "stop", 0 }, @@ -62,6 +71,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listaccounts", 1 }, { "walletpassphrase", 1 }, { "getblocktemplate", 0 }, + { "getblocktemplate", 1 }, { "listsinceblock", 1 }, { "listsinceblock", 2 }, { "listsinceblock", 3 }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 0aa1d1a7e3..c9f6eb353a 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -422,7 +422,7 @@ static UniValue BIP22ValidationResult(const CValidationState& state) UniValue getblocktemplate(const UniValue& params, bool fHelp) { - if (fHelp || params.size() > 1) + if (fHelp || params.size() > 2) throw runtime_error( "getblocktemplate ( \"jsonrequestobject\" )\n" "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" @@ -430,7 +430,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" "\nArguments:\n" - "1. \"jsonrequestobject\" (string, optional) a json object in the following spec\n" + "1. \"jsonrequestobject\" (string, optional) a json object (it can also be empty) in the following spec\n" " {\n" " \"mode\":\"template\" (string, optional) this must be set to \"template\" or omitted\n" " \"capabilities\":[ (array, optional) a list of strings\n" @@ -439,6 +439,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) " ]\n" " }\n" "\n" + "2. \"includeMerkleRoots\" (boolean, optional, default=false) If true, include \"merkleTree\" and \"scTxsCommitment\" fields in the result object. \n" "\nResult:\n" "{\n" @@ -498,6 +499,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) #endif } + bool includeMerkleRoots = false; + int nHeight = chainActive.Height() + 1; bool certSupported = ForkManager::getInstance().areSidechainsSupported(nHeight); @@ -560,6 +563,11 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Horizen is downloading blocks..."); + if (params.size() > 1) + { + includeMerkleRoots = params[1].get_bool(); + } + static unsigned int nTransactionsUpdatedLast; if (!lpval.isNull()) @@ -720,8 +728,13 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) } result.pushKV("capabilities", aCaps); result.pushKV("version", pblock->nVersion); - result.pushKV("merkleTree", pblock->hashMerkleRoot.ToString()); - result.pushKV("scTxsCommitment", pblock->hashScTxsCommitment.ToString()); + + if (includeMerkleRoots) + { + result.pushKV("merkleTree", pblock->hashMerkleRoot.ToString()); + result.pushKV("scTxsCommitment", pblock->hashScTxsCommitment.ToString()); + } + result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); result.pushKV("transactions", transactions); if (certSupported) From c8f4d937640caee9607d0d1e817c1daf2e9b1e9c Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Fri, 28 Jan 2022 18:12:37 +0100 Subject: [PATCH 09/58] Optimized computation of Merkle trees in getblocktemplate RPC command --- src/rpc/mining.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index c9f6eb353a..89118f7c50 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -721,16 +721,19 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) } UniValue result(UniValue::VOBJ); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); - if (certSupported) { - CCoinsViewCache view(pcoinsTip); - pblock->hashScTxsCommitment = pblock->BuildScTxsCommitment(view); - } + result.pushKV("capabilities", aCaps); result.pushKV("version", pblock->nVersion); if (includeMerkleRoots) { + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + + if (certSupported) { + CCoinsViewCache view(pcoinsTip); + pblock->hashScTxsCommitment = pblock->BuildScTxsCommitment(view); + } + result.pushKV("merkleTree", pblock->hashMerkleRoot.ToString()); result.pushKV("scTxsCommitment", pblock->hashScTxsCommitment.ToString()); } From 8547479b942b95b7914190af0309b28061dbe315 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Mon, 31 Jan 2022 18:45:02 +0100 Subject: [PATCH 10/58] Prepared the management of Fork 9 (sidechain version) --- src/Makefile.am | 1 + src/zen/forkmanager.cpp | 6 ++++++ src/zen/forkmanager.h | 4 ++++ src/zen/forks/fork.h | 5 +++++ src/zen/forks/fork0_originalfork.h | 5 +++++ src/zen/forks/fork9_sidechainversionfork.cpp | 13 +++++++++++++ src/zen/forks/fork9_sidechainversionfork.h | 20 ++++++++++++++++++++ 7 files changed, 54 insertions(+) create mode 100644 src/zen/forks/fork9_sidechainversionfork.cpp create mode 100644 src/zen/forks/fork9_sidechainversionfork.h diff --git a/src/Makefile.am b/src/Makefile.am index b200e6000e..56027b3af9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -618,6 +618,7 @@ libzencash_a_SOURCES = \ zen/forks/fork6_timeblockfork.cpp\ zen/forks/fork7_replayprotectionfixfork.cpp\ zen/forks/fork8_sidechainfork.cpp\ + zen/forks/fork9_sidechainversionfork.cpp\ zen/tlsmanager.cpp\ zen/delay.cpp \ zen/websocket_server.cpp diff --git a/src/zen/forkmanager.cpp b/src/zen/forkmanager.cpp index c64f425814..a1bb1b27cd 100644 --- a/src/zen/forkmanager.cpp +++ b/src/zen/forkmanager.cpp @@ -13,6 +13,7 @@ #include "forks/fork6_timeblockfork.h" #include "forks/fork7_replayprotectionfixfork.h" #include "forks/fork8_sidechainfork.h" +#include "forks/fork9_sidechainversionfork.h" namespace zen { @@ -169,6 +170,10 @@ bool ForkManager::isFutureTimeStampActive(int height) const { return getForkAtHeight(height)->isFutureTimeStampActive(height, currentNetwork); } +uint8_t ForkManager::getMaxSidechainVersion(int height) const { + return getForkAtHeight(height)->getMaxSidechainVersion(); +} + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// PRIVATE MEMBERS /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -188,6 +193,7 @@ ForkManager::ForkManager() { registerFork(new TimeBlockFork()); registerFork(new ReplayProtectionFixFork()); registerFork(new SidechainFork()); + registerFork(new SidechainVersionFork()); } /** diff --git a/src/zen/forkmanager.h b/src/zen/forkmanager.h index 92d2a6d310..76530151c5 100644 --- a/src/zen/forkmanager.h +++ b/src/zen/forkmanager.h @@ -110,6 +110,10 @@ class ForkManager */ bool isFutureTimeStampActive(int height) const; + /** + * @brief Get the maximum allowed sidechain version for a specific block height + */ + uint8_t getMaxSidechainVersion(int height) const; private: diff --git a/src/zen/forks/fork.h b/src/zen/forks/fork.h index 76894d886a..98b4e97555 100644 --- a/src/zen/forks/fork.h +++ b/src/zen/forks/fork.h @@ -124,6 +124,11 @@ class Fork */ virtual bool isFutureTimeStampActive(int height, CBaseChainParams::Network network) const=0; + /** + * @brief Get the maximum allowed sidechain version for a specific block height + */ + virtual uint8_t getMaxSidechainVersion() const = 0; + protected: /** diff --git a/src/zen/forks/fork0_originalfork.h b/src/zen/forks/fork0_originalfork.h index 2ee5b4b0a1..d26a6a2dee 100644 --- a/src/zen/forks/fork0_originalfork.h +++ b/src/zen/forks/fork0_originalfork.h @@ -88,6 +88,11 @@ class OriginalFork : public Fork * @brief returns true if the contextualcheckblockheader uses the MAX_FUTURE_BLOCK_TIME_MTP check blocktime, */ inline virtual bool isFutureTimeStampActive(int height, CBaseChainParams::Network network) const { return false; } + + /** + * @brief Get the maximum allowed sidechain version for a specific block height + */ + inline virtual uint8_t getMaxSidechainVersion() const { return 0; }; }; } diff --git a/src/zen/forks/fork9_sidechainversionfork.cpp b/src/zen/forks/fork9_sidechainversionfork.cpp new file mode 100644 index 0000000000..20d361e801 --- /dev/null +++ b/src/zen/forks/fork9_sidechainversionfork.cpp @@ -0,0 +1,13 @@ +#include "fork9_sidechainversionfork.h" + +namespace zen { + +SidechainVersionFork::SidechainVersionFork() +{ + setHeightMap({{CBaseChainParams::Network::MAIN,1100000}, + {CBaseChainParams::Network::REGTEST,450}, + {CBaseChainParams::Network::TESTNET,1000000}}); + +} + +} \ No newline at end of file diff --git a/src/zen/forks/fork9_sidechainversionfork.h b/src/zen/forks/fork9_sidechainversionfork.h new file mode 100644 index 0000000000..c54260cc64 --- /dev/null +++ b/src/zen/forks/fork9_sidechainversionfork.h @@ -0,0 +1,20 @@ +#ifndef _SIDECHAINVERSIONFORK_H +#define _SIDECHAINVERSIONFORK_H + +#include "fork8_sidechainfork.h" + +namespace zen { + +class SidechainVersionFork : public SidechainFork +{ +public: + SidechainVersionFork(); + + /** + * @brief Get the maximum allowed sidechain version for a specific block height + */ + inline virtual uint8_t getMaxSidechainVersion() const { return 1; } +}; + +} +#endif // _SIDECHAINVERSIONFORK_H From 53145c0c7cf0dc08e17561ceb55d4fb439ecc615 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Mon, 31 Jan 2022 18:46:20 +0100 Subject: [PATCH 11/58] Added the sidechain version in CTxScCreationOut and in ScFixedParameters --- src/primitives/transaction.h | 23 ++++++++++++++++++++++- src/sc/sidechaintypes.h | 8 ++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index bf2655fd08..846a7882dd 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -585,6 +585,7 @@ friend class CTransaction; void GenerateScId(const uint256& txHash, unsigned int pos) const; public: + int8_t version; int withdrawalEpochLength; std::vector customData; boost::optional constant; @@ -610,7 +611,27 @@ friend class CTransaction; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(withdrawalEpochLength); + + // The first serialization field is a 4 bytes element containing the SC Creation version in the first byte + // and the withdrawalEpochLenght in the remaining 3 bytes. The first implementation didn't have the version + // field, but only the withdrawalEpochLenght, so the version was implicitly set to 0 anyway. + if (ser_action.ForRead()) + { + int withdrawalEpochLengthAndVersion; + READWRITE(withdrawalEpochLengthAndVersion); + + // Get the most significant byte + version = withdrawalEpochLengthAndVersion >> 24; + + // Get the leasst significant 3 bytes + withdrawalEpochLength = withdrawalEpochLengthAndVersion & 0x00FFFFFF; + } + else + { + int withdrawalEpochLengthAndVersion = (version << 24) | withdrawalEpochLength; + READWRITE(withdrawalEpochLengthAndVersion); + } + READWRITE(nValue); READWRITE(address); READWRITE(customData); diff --git a/src/sc/sidechaintypes.h b/src/sc/sidechaintypes.h index ac369be52a..81db3669a6 100644 --- a/src/sc/sidechaintypes.h +++ b/src/sc/sidechaintypes.h @@ -481,6 +481,7 @@ bool IsUndefinedProvingSystemType(const std::string& str); struct ScFixedParameters { + uint8_t version; int withdrawalEpochLength; // all creation data follows... std::vector customData; @@ -495,6 +496,7 @@ struct ScFixedParameters bool IsNull() const { return ( + version == 0 && withdrawalEpochLength == -1 && customData.empty() && constant == boost::none && @@ -519,12 +521,13 @@ struct ScFixedParameters READWRITE(mainchainBackwardTransferRequestDataLength); } - ScFixedParameters(): withdrawalEpochLength(-1), mainchainBackwardTransferRequestDataLength(0) + ScFixedParameters(): version(0), withdrawalEpochLength(-1), mainchainBackwardTransferRequestDataLength(0) {} inline bool operator==(const ScFixedParameters& rhs) const { - return (withdrawalEpochLength == rhs.withdrawalEpochLength) && + return (version == rhs.version) && + (withdrawalEpochLength == rhs.withdrawalEpochLength) && (customData == rhs.customData) && (constant == rhs.constant) && (wCertVk == rhs.wCertVk) && @@ -536,6 +539,7 @@ struct ScFixedParameters inline bool operator!=(const ScFixedParameters& rhs) const { return !(*this == rhs); } inline ScFixedParameters& operator=(const ScFixedParameters& cp) { + version = cp.version; withdrawalEpochLength = cp.withdrawalEpochLength; customData = cp.customData; constant = cp.constant; From 16d9a33a621affd45a9eae83067d297d5c9124b5 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Tue, 1 Feb 2022 18:52:40 +0100 Subject: [PATCH 12/58] Added the sidechain version as a parameter for custom fields validation --- src/gtest/test_forkmanager.cpp | 18 + src/gtest/test_libzendoo.cpp | 1048 +++++++++--------- src/primitives/transaction.cpp | 4 +- src/primitives/transaction.h | 16 +- src/sc/proofverifier.cpp | 4 +- src/sc/sidechain.cpp | 4 +- src/sc/sidechainTxsCommitmentBuilder.cpp | 4 +- src/sc/sidechaintypes.cpp | 34 +- src/sc/sidechaintypes.h | 10 +- src/util.cpp | 17 + src/util.h | 1 + src/zen/forkmanager.cpp | 5 + src/zen/forkmanager.h | 7 +- src/zen/forks/fork9_sidechainversionfork.cpp | 1 + 14 files changed, 644 insertions(+), 529 deletions(-) diff --git a/src/gtest/test_forkmanager.cpp b/src/gtest/test_forkmanager.cpp index 94239b3a45..5ce95f81d0 100644 --- a/src/gtest/test_forkmanager.cpp +++ b/src/gtest/test_forkmanager.cpp @@ -1,6 +1,7 @@ #include #include "zen/forkmanager.h" #include "chainparams.h" +#include "zen/forks/fork9_sidechainversionfork.h" using namespace zen; TEST(ForkManager, TestCommunityFundRewardTestnet) { @@ -286,3 +287,20 @@ TEST(ForkManager, SidechainForkMainnet) { EXPECT_EQ(ForkManager::getInstance().getNewBlockVersion(1047624), BLOCK_VERSION_SC_SUPPORT); EXPECT_EQ(ForkManager::getInstance().getNewBlockVersion(1047625), BLOCK_VERSION_SC_SUPPORT); } + +TEST(ForkManager, SidechainVersionForkMainnet) { + SelectParams(CBaseChainParams::MAIN); + + // TODO: set proper fork height value. + int sidechainVersionForkHeight = 1100000; + EXPECT_EQ(ForkManager::getInstance().getMaxSidechainVersion(0), 0); + EXPECT_EQ(ForkManager::getInstance().getMaxSidechainVersion(sidechainVersionForkHeight - 1), 0); + EXPECT_EQ(ForkManager::getInstance().getMaxSidechainVersion(sidechainVersionForkHeight), 1); + EXPECT_EQ(ForkManager::getInstance().getMaxSidechainVersion(sidechainVersionForkHeight + 1), 1); +} + +TEST(ForkManager, HighestFork) { + SelectParams(CBaseChainParams::MAIN); + const Fork* highestFork = ForkManager::getInstance().getHighestFork(); + EXPECT_EQ(typeid(*highestFork), typeid(SidechainVersionFork)); +} \ No newline at end of file diff --git a/src/gtest/test_libzendoo.cpp b/src/gtest/test_libzendoo.cpp index 03bf99d496..f2ed852e13 100644 --- a/src/gtest/test_libzendoo.cpp +++ b/src/gtest/test_libzendoo.cpp @@ -32,7 +32,7 @@ const uint8_t TEST_CUSTOM_FIELD[] = {0xbe, 0x61, 0x16, 0xab, 0x27, 0xee, 0xab, 0 0x3f, 0x06, 0xac, 0xb3, 0x8a, 0x5c, 0xeb, 0xd4, 0x42, 0xf4, 0x96, 0xd8, 0xbf, 0xd3, 0x8e, 0x7d}; -static CMutableTransaction CreateDefaultTx() +static CMutableTransaction CreateDefaultTx(uint8_t sidechainVersion) { // Create a tx with a sc creation, a fwt, a bwtr and a csw @@ -40,6 +40,7 @@ static CMutableTransaction CreateDefaultTx() mtx.nVersion = SC_TX_VERSION; //--- mtx.vsc_ccout.resize(1); + mtx.vsc_ccout[0].version = sidechainVersion; mtx.vsc_ccout[0].nValue = CAmount(12000); mtx.vsc_ccout[0].withdrawalEpochLength = 150; mtx.vsc_ccout[0].wCertVk = CScVKey{SAMPLE_CERT_DARLIN_VK}; @@ -909,8 +910,11 @@ TEST(CctpLibrary, BitVectorCertificateFieldNull) const BitVectorCertificateFieldConfig cfg(1024, 2048); BitVectorCertificateField bvField; - const CFieldElement& fe = bvField.GetFieldElement(cfg); - EXPECT_FALSE(fe.IsValid()); + for (uint8_t sidechainVersion = 0; sidechainVersion <= ForkManager::getInstance().getHighestFork()->getMaxSidechainVersion(); sidechainVersion++) + { + const CFieldElement& fe = bvField.GetFieldElement(cfg, sidechainVersion); + EXPECT_FALSE(fe.IsValid()); + } } TEST(CctpLibrary, BitVectorCertificateFieldUnsuppComprAlgo) @@ -921,8 +925,11 @@ TEST(CctpLibrary, BitVectorCertificateFieldUnsuppComprAlgo) const BitVectorCertificateFieldConfig cfg(1024, 2048); BitVectorCertificateField bvField(bvVec); - const CFieldElement& fe = bvField.GetFieldElement(cfg); - EXPECT_FALSE(fe.IsValid()); + for (uint8_t sidechainVersion = 0; sidechainVersion <= ForkManager::getInstance().getHighestFork()->getMaxSidechainVersion(); sidechainVersion++) + { + const CFieldElement& fe = bvField.GetFieldElement(cfg, sidechainVersion); + EXPECT_FALSE(fe.IsValid()); + } } TEST(CctpLibrary, BitVectorCertificateFieldBadSize) @@ -945,8 +952,12 @@ TEST(CctpLibrary, BitVectorCertificateFieldBadSize) const BitVectorCertificateFieldConfig cfg(1024, 2048); BitVectorCertificateField bvField(bvVec); - const CFieldElement& fe = bvField.GetFieldElement(cfg); - EXPECT_FALSE(fe.IsValid()); + for (uint8_t sidechainVersion = 0; sidechainVersion <= ForkManager::getInstance().getHighestFork()->getMaxSidechainVersion(); sidechainVersion++) + { + const CFieldElement& fe = bvField.GetFieldElement(cfg, sidechainVersion); + EXPECT_FALSE(fe.IsValid()); + } + zendoo_free_bws(bws_ret1); } @@ -987,8 +998,12 @@ TEST(CctpLibrary, BitVectorCertificateFieldFullGzip) const BitVectorCertificateFieldConfig cfg(bitVectorSizeBits, maxCompressedSizeBytes); BitVectorCertificateField bvField(bvVec); - const CFieldElement& fe = bvField.GetFieldElement(cfg); - EXPECT_TRUE(fe.IsValid()); + for (uint8_t sidechainVersion = 0; sidechainVersion <= ForkManager::getInstance().getHighestFork()->getMaxSidechainVersion(); sidechainVersion++) + { + const CFieldElement& fe = bvField.GetFieldElement(cfg, sidechainVersion); + EXPECT_TRUE(fe.IsValid()); + } + zendoo_free_bws(bws_ret1); delete [] buffer; } @@ -1030,580 +1045,593 @@ TEST(CctpLibrary, BitVectorCertificateFieldFullBzip2) const BitVectorCertificateFieldConfig cfg(bitVectorSizeBits, maxCompressedSizeBytes); BitVectorCertificateField bvField(bvVec); - const CFieldElement& fe = bvField.GetFieldElement(cfg); - EXPECT_TRUE(fe.IsValid()); + for (uint8_t sidechainVersion = 0; sidechainVersion <= ForkManager::getInstance().getHighestFork()->getMaxSidechainVersion(); sidechainVersion++) + { + const CFieldElement& fe = bvField.GetFieldElement(cfg, sidechainVersion); + EXPECT_TRUE(fe.IsValid()); + } + zendoo_free_bws(bws_ret1); delete [] buffer; } TEST(CctpLibrary, CommitmentTreeBuilding) { - printf("Creating a commitment tree ...\n"); - CctpErrorCode ret_code = CctpErrorCode::OK; - const CFieldElement& dummyFe = CFieldElement{SAMPLE_FIELD}; - - commitment_tree_t* ct = zendoo_commitment_tree_create(); - ASSERT_TRUE(ct != nullptr); + for (uint8_t sidechainVersion = 0; sidechainVersion <= ForkManager::getInstance().getHighestFork()->getMaxSidechainVersion(); sidechainVersion++) + { + printf("Creating a commitment tree ...\n"); + CctpErrorCode ret_code = CctpErrorCode::OK; + const CFieldElement& dummyFe = CFieldElement{SAMPLE_FIELD}; - unsigned char field_bytes[CFieldElement::ByteSize()] = {}; + commitment_tree_t* ct = zendoo_commitment_tree_create(); + ASSERT_TRUE(ct != nullptr); - printf("\nChecking commitment tree with a nullptr obj ...\n"); - field_t* fe_null = zendoo_commitment_tree_get_commitment(nullptr, &ret_code); - ASSERT_TRUE(ret_code != CctpErrorCode::OK); - ASSERT_TRUE(fe_null == nullptr); + unsigned char field_bytes[CFieldElement::ByteSize()] = {}; - printf("\nChecking initial commitment tree ...\n"); - field_t* fe0 = zendoo_commitment_tree_get_commitment(ct, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - ASSERT_TRUE(fe0 != nullptr); + printf("\nChecking commitment tree with a nullptr obj ...\n"); + field_t* fe_null = zendoo_commitment_tree_get_commitment(nullptr, &ret_code); + ASSERT_TRUE(ret_code != CctpErrorCode::OK); + ASSERT_TRUE(fe_null == nullptr); - const CFieldElement& emptyFe = CFieldElement{EMPTY_COMMITMENT_TREE_FIELD}; - wrappedFieldPtr fe_empty_ptr = emptyFe.GetFieldElement(); - ASSERT_TRUE(memcmp(fe_empty_ptr.get(), fe0, Sidechain::SC_FE_SIZE_IN_BYTES) == 0); + printf("\nChecking initial commitment tree ...\n"); + field_t* fe0 = zendoo_commitment_tree_get_commitment(ct, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + ASSERT_TRUE(fe0 != nullptr); - zendoo_serialize_field(fe0, field_bytes, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - printf("ct = ["); - for (int i = 0; i < sizeof(field_bytes); i++) - printf("%02x", ((unsigned char*)field_bytes)[i]); - printf("]\n"); + const CFieldElement& emptyFe = CFieldElement{EMPTY_COMMITMENT_TREE_FIELD}; + wrappedFieldPtr fe_empty_ptr = emptyFe.GetFieldElement(); + ASSERT_TRUE(memcmp(fe_empty_ptr.get(), fe0, Sidechain::SC_FE_SIZE_IN_BYTES) == 0); - CTransaction tx = CreateDefaultTx(); + zendoo_serialize_field(fe0, field_bytes, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + printf("ct = ["); + for (int i = 0; i < sizeof(field_bytes); i++) + printf("%02x", ((unsigned char*)field_bytes)[i]); + printf("]\n"); - const uint256& tx_hash = tx.GetHash(); - BufferWithSize bws_tx_hash(tx_hash.begin(), tx_hash.size()); + CTransaction tx = CreateDefaultTx(sidechainVersion); - printf("tx hash=[%s] ...\n", tx_hash.ToString().c_str()); + const uint256& tx_hash = tx.GetHash(); + BufferWithSize bws_tx_hash(tx_hash.begin(), tx_hash.size()); - uint32_t out_idx = 0; + printf("tx hash=[%s] ...\n", tx_hash.ToString().c_str()); - for (const CTxScCreationOut& ccout : tx.GetVscCcOut() ) - { - wrappedFieldPtr sptrScId = CFieldElement(ccout.GetScId()).GetFieldElement(); - field_t* scid_fe = sptrScId.get(); + uint32_t out_idx = 0; - const uint256& pub_key = ccout.address; - BufferWithSize bws_pk(pub_key.begin(), pub_key.size()); - - std::unique_ptr bws_fe_cfg(nullptr); - if (!ccout.vFieldElementCertificateFieldConfig.empty()) - { - bws_fe_cfg.reset(new BufferWithSize( - (const unsigned char*)&ccout.vFieldElementCertificateFieldConfig[0], - (size_t)ccout.vFieldElementCertificateFieldConfig.size() - )); - } - - int bvcfg_size = ccout.vBitVectorCertificateFieldConfig.size(); - std::unique_ptr bvcfg(new BitVectorElementsConfig[bvcfg_size]); - int i = 0; - for (auto entry: ccout.vBitVectorCertificateFieldConfig) + for (const CTxScCreationOut& ccout : tx.GetVscCcOut() ) { - bvcfg[i].bit_vector_size_bits = entry.getBitVectorSizeBits(); - bvcfg[i].max_compressed_byte_size = entry.getMaxCompressedSizeBytes(); - i++; + wrappedFieldPtr sptrScId = CFieldElement(ccout.GetScId()).GetFieldElement(); + field_t* scid_fe = sptrScId.get(); + + const uint256& pub_key = ccout.address; + BufferWithSize bws_pk(pub_key.begin(), pub_key.size()); + + std::unique_ptr bws_fe_cfg(nullptr); + if (!ccout.vFieldElementCertificateFieldConfig.empty()) + { + bws_fe_cfg.reset(new BufferWithSize( + (const unsigned char*)&ccout.vFieldElementCertificateFieldConfig[0], + (size_t)ccout.vFieldElementCertificateFieldConfig.size() + )); + } + + int bvcfg_size = ccout.vBitVectorCertificateFieldConfig.size(); + std::unique_ptr bvcfg(new BitVectorElementsConfig[bvcfg_size]); + int i = 0; + for (auto entry: ccout.vBitVectorCertificateFieldConfig) + { + bvcfg[i].bit_vector_size_bits = entry.getBitVectorSizeBits(); + bvcfg[i].max_compressed_byte_size = entry.getMaxCompressedSizeBytes(); + i++; + } + // mc crypto lib wants a null ptr if we have no fields + if (bvcfg_size == 0) + bvcfg.reset(); + + std::unique_ptr bws_custom_data(nullptr); + if (!ccout.customData.empty()) + { + bws_custom_data.reset(new BufferWithSize( + (unsigned char*)(&ccout.customData[0]), + ccout.customData.size() + )); + } + + wrappedFieldPtr sptrConstant(nullptr); + if(ccout.constant.is_initialized()) + { + sptrConstant = ccout.constant->GetFieldElement(); + } + field_t* constant_fe = sptrConstant.get(); + + BufferWithSize bws_cert_vk(ccout.wCertVk.GetDataBuffer(), ccout.wCertVk.GetDataSize()); + + BufferWithSize bws_csw_vk(nullptr, 0); + if(ccout.wCeasedVk.is_initialized()) + { + bws_csw_vk.data = ccout.wCeasedVk->GetDataBuffer(); + bws_csw_vk.len = ccout.wCeasedVk->GetDataSize(); + } + + printf("Adding a sc creation to the commitment tree ...\n"); + bool ret = zendoo_commitment_tree_add_scc(ct, + scid_fe, + ccout.nValue, + &bws_pk, + &bws_tx_hash, + out_idx, + ccout.withdrawalEpochLength, + ccout.mainchainBackwardTransferRequestDataLength, + bws_fe_cfg.get(), + bvcfg.get(), + bvcfg_size, + ccout.mainchainBackwardTransferRequestScFee, + ccout.forwardTransferScFee, + bws_custom_data.get(), + constant_fe, + &bws_cert_vk, + &bws_csw_vk, + &ret_code + ); + ASSERT_TRUE(ret == true); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + + out_idx++; } - // mc crypto lib wants a null ptr if we have no fields - if (bvcfg_size == 0) - bvcfg.reset(); - - std::unique_ptr bws_custom_data(nullptr); - if (!ccout.customData.empty()) + + printf("\nChecking commitment tree after sc add ...\n"); + field_t* fe1 = zendoo_commitment_tree_get_commitment(ct, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + ASSERT_TRUE(fe1 != nullptr); + ASSERT_TRUE(memcmp(fe0, fe1, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); + + zendoo_serialize_field(fe1, field_bytes, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + printf("ct = ["); + for (int i = 0; i < sizeof(field_bytes); i++) + printf("%02x", ((unsigned char*)field_bytes)[i]); + printf("]\n"); + + for (const CTxForwardTransferOut& ccout : tx.GetVftCcOut() ) { - bws_custom_data.reset(new BufferWithSize( - (unsigned char*)(&ccout.customData[0]), - ccout.customData.size() - )); + wrappedFieldPtr sptrScId = CFieldElement(ccout.GetScId()).GetFieldElement(); + field_t* scid_fe = sptrScId.get(); + + const uint256& fwt_pub_key = ccout.address; + BufferWithSize bws_fwt_pk((unsigned char*)fwt_pub_key.begin(), fwt_pub_key.size()); + + const uint160& fwt_mc_return_address = ccout.mcReturnAddress; + BufferWithSize bws_fwt_return_address((unsigned char*)fwt_mc_return_address.begin(), fwt_mc_return_address.size()); + + printf("Adding a fwt to the commitment tree ...\n"); + bool ret = zendoo_commitment_tree_add_fwt(ct, + scid_fe, + ccout.nValue, + &bws_fwt_pk, + &bws_fwt_return_address, + &bws_tx_hash, + out_idx, + &ret_code + ); + ASSERT_TRUE(ret == true); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + + out_idx++; } - - wrappedFieldPtr sptrConstant(nullptr); - if(ccout.constant.is_initialized()) + + printf("\nChecking commitment tree after fwt add ...\n"); + field_t* fe2 = zendoo_commitment_tree_get_commitment(ct, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + ASSERT_TRUE(fe2 != nullptr); + ASSERT_TRUE(memcmp(fe1, fe2, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); + + zendoo_serialize_field(fe2, field_bytes, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + printf("ct = ["); + for (int i = 0; i < sizeof(field_bytes); i++) + printf("%02x", ((unsigned char*)field_bytes)[i]); + printf("]\n"); + + for (const CBwtRequestOut& ccout : tx.GetVBwtRequestOut() ) { - sptrConstant = ccout.constant->GetFieldElement(); + wrappedFieldPtr sptrScId = CFieldElement(ccout.GetScId()).GetFieldElement(); + field_t* scid_fe = sptrScId.get(); + + int sc_req_data_len = ccout.vScRequestData.size(); + std::unique_ptr sc_req_data(new const field_t*[sc_req_data_len]); + int i = 0; + std::vector vSptr; + for (auto entry: ccout.vScRequestData) + { + wrappedFieldPtr sptrFe = entry.GetFieldElement(); + sc_req_data[i] = sptrFe.get(); + vSptr.push_back(sptrFe); + i++; + } + + const uint160& bwtr_pk_hash = ccout.mcDestinationAddress; + BufferWithSize bws_bwtr_pk_hash(bwtr_pk_hash.begin(), bwtr_pk_hash.size()); + + printf("Negative: adding a bwtr with swapped args to the commitment tree: expecting failure ...\n"); + bool ret = zendoo_commitment_tree_add_bwtr(ct, + scid_fe, + ccout.scFee, + sc_req_data.get(), + sc_req_data_len, + &bws_tx_hash, // swapped + &bws_bwtr_pk_hash, // swapped + out_idx, + &ret_code + ); + ASSERT_FALSE(ret == true); + ASSERT_TRUE(ret_code != CctpErrorCode::OK); + + printf("Adding a bwtr to the commitment tree ...\n"); + ret = zendoo_commitment_tree_add_bwtr(ct, + scid_fe, + ccout.scFee, + sc_req_data.get(), + sc_req_data_len, + &bws_bwtr_pk_hash, + &bws_tx_hash, + out_idx, + &ret_code + ); + ASSERT_TRUE(ret == true); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + + out_idx++; } - field_t* constant_fe = sptrConstant.get(); - - BufferWithSize bws_cert_vk(ccout.wCertVk.GetDataBuffer(), ccout.wCertVk.GetDataSize()); - - BufferWithSize bws_csw_vk(nullptr, 0); - if(ccout.wCeasedVk.is_initialized()) + + printf("\nChecking commitment tree after bwtr add ...\n"); + field_t* fe3 = zendoo_commitment_tree_get_commitment(ct, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + ASSERT_TRUE(fe3 != nullptr); + ASSERT_TRUE(memcmp(fe2, fe3, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); + + zendoo_serialize_field(fe3, field_bytes, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + printf("ct = ["); + for (int i = 0; i < sizeof(field_bytes); i++) + printf("%02x", ((unsigned char*)field_bytes)[i]); + printf("]\n"); + + for (const CTxCeasedSidechainWithdrawalInput& ccin : tx.GetVcswCcIn() ) { - bws_csw_vk.data = ccout.wCeasedVk->GetDataBuffer(); - bws_csw_vk.len = ccout.wCeasedVk->GetDataSize(); + wrappedFieldPtr sptrScId = CFieldElement(ccin.scId).GetFieldElement(); + field_t* scid_fe = sptrScId.get(); + + const uint160& csw_pk_hash = ccin.pubKeyHash; + BufferWithSize bws_csw_pk_hash(csw_pk_hash.begin(), csw_pk_hash.size()); + + wrappedFieldPtr sptrNullifier = ccin.nullifier.GetFieldElement(); + + printf("Adding a csw to the commitment tree ...\n"); + bool ret = zendoo_commitment_tree_add_csw(ct, + scid_fe, + ccin.nValue, + sptrNullifier.get(), + &bws_csw_pk_hash, + &ret_code + ); + ASSERT_TRUE(ret == true); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + + out_idx++; } - printf("Adding a sc creation to the commitment tree ...\n"); - bool ret = zendoo_commitment_tree_add_scc(ct, - scid_fe, - ccout.nValue, - &bws_pk, - &bws_tx_hash, - out_idx, - ccout.withdrawalEpochLength, - ccout.mainchainBackwardTransferRequestDataLength, - bws_fe_cfg.get(), - bvcfg.get(), - bvcfg_size, - ccout.mainchainBackwardTransferRequestScFee, - ccout.forwardTransferScFee, - bws_custom_data.get(), - constant_fe, - &bws_cert_vk, - &bws_csw_vk, - &ret_code - ); - ASSERT_TRUE(ret == true); + printf("\nChecking commitment tree after csw add ...\n"); + field_t* fe4 = zendoo_commitment_tree_get_commitment(ct, &ret_code); ASSERT_TRUE(ret_code == CctpErrorCode::OK); - - out_idx++; - } + ASSERT_TRUE(fe4 != nullptr); + ASSERT_TRUE(memcmp(fe3, fe4, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); - printf("\nChecking commitment tree after sc add ...\n"); - field_t* fe1 = zendoo_commitment_tree_get_commitment(ct, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - ASSERT_TRUE(fe1 != nullptr); - ASSERT_TRUE(memcmp(fe0, fe1, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); - - zendoo_serialize_field(fe1, field_bytes, &ret_code); + zendoo_serialize_field(fe4, field_bytes, &ret_code); ASSERT_TRUE(ret_code == CctpErrorCode::OK); - printf("ct = ["); - for (int i = 0; i < sizeof(field_bytes); i++) - printf("%02x", ((unsigned char*)field_bytes)[i]); - printf("]\n"); + printf("ct = ["); + for (int i = 0; i < sizeof(field_bytes); i++) + printf("%02x", ((unsigned char*)field_bytes)[i]); + printf("]\n"); - for (const CTxForwardTransferOut& ccout : tx.GetVftCcOut() ) - { - wrappedFieldPtr sptrScId = CFieldElement(ccout.GetScId()).GetFieldElement(); + CScCertificate cert = CreateDefaultCert(); + + printf("Adding a cert to the commitment tree ...\n"); + wrappedFieldPtr sptrScId = CFieldElement(cert.GetScId()).GetFieldElement(); field_t* scid_fe = sptrScId.get(); + + int epoch_number = cert.epochNumber; + int quality = cert.quality; - const uint256& fwt_pub_key = ccout.address; - BufferWithSize bws_fwt_pk((unsigned char*)fwt_pub_key.begin(), fwt_pub_key.size()); - - const uint160& fwt_mc_return_address = ccout.mcReturnAddress; - BufferWithSize bws_fwt_return_address((unsigned char*)fwt_mc_return_address.begin(), fwt_mc_return_address.size()); - - printf("Adding a fwt to the commitment tree ...\n"); - bool ret = zendoo_commitment_tree_add_fwt(ct, - scid_fe, - ccout.nValue, - &bws_fwt_pk, - &bws_fwt_return_address, - &bws_tx_hash, - out_idx, - &ret_code - ); - ASSERT_TRUE(ret == true); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - - out_idx++; - } + const backward_transfer_t* bt_list = nullptr; + std::vector vbt_list; + for(int pos = cert.nFirstBwtPos; pos < cert.GetVout().size(); ++pos) + { + const CTxOut& out = cert.GetVout()[pos]; + const auto& bto = CBackwardTransferOut(out); + backward_transfer_t x; + x.amount = bto.nValue; + memcpy(x.pk_dest, bto.pubKeyHash.begin(), sizeof(x.pk_dest)); + vbt_list.push_back(x); + } - printf("\nChecking commitment tree after fwt add ...\n"); - field_t* fe2 = zendoo_commitment_tree_get_commitment(ct, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - ASSERT_TRUE(fe2 != nullptr); - ASSERT_TRUE(memcmp(fe1, fe2, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); + if (!vbt_list.empty()) + bt_list = (const backward_transfer_t*)vbt_list.data(); - zendoo_serialize_field(fe2, field_bytes, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - printf("ct = ["); - for (int i = 0; i < sizeof(field_bytes); i++) - printf("%02x", ((unsigned char*)field_bytes)[i]); - printf("]\n"); + size_t bt_list_len = vbt_list.size(); - for (const CBwtRequestOut& ccout : tx.GetVBwtRequestOut() ) - { - wrappedFieldPtr sptrScId = CFieldElement(ccout.GetScId()).GetFieldElement(); - field_t* scid_fe = sptrScId.get(); + int custom_fields_len = cert.vFieldElementCertificateField.size() + cert.vBitVectorCertificateField.size(); - int sc_req_data_len = ccout.vScRequestData.size(); - std::unique_ptr sc_req_data(new const field_t*[sc_req_data_len]); + FieldElementCertificateFieldConfig fieldConfig; + BitVectorCertificateFieldConfig bitVectorConfig; + + std::unique_ptr custom_fields(new const field_t*[custom_fields_len]); int i = 0; std::vector vSptr; - for (auto entry: ccout.vScRequestData) + for (auto entry: cert.vFieldElementCertificateField) { - wrappedFieldPtr sptrFe = entry.GetFieldElement(); - sc_req_data[i] = sptrFe.get(); + CFieldElement fe{entry.GetFieldElement(fieldConfig, sidechainVersion)}; + assert(fe.IsValid()); + wrappedFieldPtr sptrFe = fe.GetFieldElement(); + custom_fields[i] = sptrFe.get(); vSptr.push_back(sptrFe); i++; } - const uint160& bwtr_pk_hash = ccout.mcDestinationAddress; - BufferWithSize bws_bwtr_pk_hash(bwtr_pk_hash.begin(), bwtr_pk_hash.size()); + int j = 0; + for (auto entry: cert.vBitVectorCertificateField) + { + CFieldElement fe{entry.GetFieldElement(bitVectorConfig, sidechainVersion)}; + assert(fe.IsValid()); + wrappedFieldPtr sptrFe = fe.GetFieldElement(); + custom_fields[i+j] = sptrFe.get(); + vSptr.push_back(sptrFe); + j++; + } - printf("Negative: adding a bwtr with swapped args to the commitment tree: expecting failure ...\n"); - bool ret = zendoo_commitment_tree_add_bwtr(ct, - scid_fe, - ccout.scFee, - sc_req_data.get(), - sc_req_data_len, - &bws_tx_hash, // swapped - &bws_bwtr_pk_hash, // swapped - out_idx, - &ret_code - ); - ASSERT_FALSE(ret == true); - ASSERT_TRUE(ret_code != CctpErrorCode::OK); - - printf("Adding a bwtr to the commitment tree ...\n"); - ret = zendoo_commitment_tree_add_bwtr(ct, + // mc crypto lib wants a null ptr if we have no fields + if (custom_fields_len == 0) + { + custom_fields.reset(); + ASSERT_EQ(custom_fields.get(), nullptr); + } + + wrappedFieldPtr sptrCum = cert.endEpochCumScTxCommTreeRoot.GetFieldElement(); + + bool ret = zendoo_commitment_tree_add_cert(ct, scid_fe, - ccout.scFee, - sc_req_data.get(), - sc_req_data_len, - &bws_bwtr_pk_hash, - &bws_tx_hash, - out_idx, + epoch_number, + quality, + bt_list, + bt_list_len, + custom_fields.get(), + custom_fields_len, + sptrCum.get(), + cert.forwardTransferScFee, + cert.mainchainBackwardTransferRequestScFee, &ret_code ); ASSERT_TRUE(ret == true); ASSERT_TRUE(ret_code == CctpErrorCode::OK); - - out_idx++; - } - - printf("\nChecking commitment tree after bwtr add ...\n"); - field_t* fe3 = zendoo_commitment_tree_get_commitment(ct, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - ASSERT_TRUE(fe3 != nullptr); - ASSERT_TRUE(memcmp(fe2, fe3, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); - - zendoo_serialize_field(fe3, field_bytes, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - printf("ct = ["); - for (int i = 0; i < sizeof(field_bytes); i++) - printf("%02x", ((unsigned char*)field_bytes)[i]); - printf("]\n"); - - for (const CTxCeasedSidechainWithdrawalInput& ccin : tx.GetVcswCcIn() ) - { - wrappedFieldPtr sptrScId = CFieldElement(ccin.scId).GetFieldElement(); - field_t* scid_fe = sptrScId.get(); - const uint160& csw_pk_hash = ccin.pubKeyHash; - BufferWithSize bws_csw_pk_hash(csw_pk_hash.begin(), csw_pk_hash.size()); - - wrappedFieldPtr sptrNullifier = ccin.nullifier.GetFieldElement(); - - printf("Adding a csw to the commitment tree ...\n"); - bool ret = zendoo_commitment_tree_add_csw(ct, - scid_fe, - ccin.nValue, - sptrNullifier.get(), - &bws_csw_pk_hash, - &ret_code - ); - ASSERT_TRUE(ret == true); + printf("\nChecking commitment tree after cert add ...\n"); + field_t* fe5 = zendoo_commitment_tree_get_commitment(ct, &ret_code); ASSERT_TRUE(ret_code == CctpErrorCode::OK); - - out_idx++; - } - - printf("\nChecking commitment tree after csw add ...\n"); - field_t* fe4 = zendoo_commitment_tree_get_commitment(ct, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - ASSERT_TRUE(fe4 != nullptr); - ASSERT_TRUE(memcmp(fe3, fe4, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); - - zendoo_serialize_field(fe4, field_bytes, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - printf("ct = ["); - for (int i = 0; i < sizeof(field_bytes); i++) - printf("%02x", ((unsigned char*)field_bytes)[i]); - printf("]\n"); - - CScCertificate cert = CreateDefaultCert(); - - printf("Adding a cert to the commitment tree ...\n"); - wrappedFieldPtr sptrScId = CFieldElement(cert.GetScId()).GetFieldElement(); - field_t* scid_fe = sptrScId.get(); - - int epoch_number = cert.epochNumber; - int quality = cert.quality; - - const backward_transfer_t* bt_list = nullptr; - std::vector vbt_list; - for(int pos = cert.nFirstBwtPos; pos < cert.GetVout().size(); ++pos) - { - const CTxOut& out = cert.GetVout()[pos]; - const auto& bto = CBackwardTransferOut(out); - backward_transfer_t x; - x.amount = bto.nValue; - memcpy(x.pk_dest, bto.pubKeyHash.begin(), sizeof(x.pk_dest)); - vbt_list.push_back(x); - } - - if (!vbt_list.empty()) - bt_list = (const backward_transfer_t*)vbt_list.data(); - - size_t bt_list_len = vbt_list.size(); - - int custom_fields_len = cert.vFieldElementCertificateField.size() + cert.vBitVectorCertificateField.size(); - - FieldElementCertificateFieldConfig fieldConfig; - BitVectorCertificateFieldConfig bitVectorConfig; - - std::unique_ptr custom_fields(new const field_t*[custom_fields_len]); - int i = 0; - std::vector vSptr; - for (auto entry: cert.vFieldElementCertificateField) - { - CFieldElement fe{entry.GetFieldElement(fieldConfig)}; - assert(fe.IsValid()); - wrappedFieldPtr sptrFe = fe.GetFieldElement(); - custom_fields[i] = sptrFe.get(); - vSptr.push_back(sptrFe); - i++; - } - - int j = 0; - for (auto entry: cert.vBitVectorCertificateField) - { - CFieldElement fe{entry.GetFieldElement(bitVectorConfig)}; - assert(fe.IsValid()); - wrappedFieldPtr sptrFe = fe.GetFieldElement(); - custom_fields[i+j] = sptrFe.get(); - vSptr.push_back(sptrFe); - j++; - } + ASSERT_TRUE(fe5 != nullptr); + ASSERT_TRUE(memcmp(fe4, fe5, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); - // mc crypto lib wants a null ptr if we have no fields - if (custom_fields_len == 0) - { - custom_fields.reset(); - ASSERT_EQ(custom_fields.get(), nullptr); + zendoo_serialize_field(fe5, field_bytes, &ret_code); + ASSERT_TRUE(ret_code == CctpErrorCode::OK); + printf("ct = ["); + for (int i = 0; i < sizeof(field_bytes); i++) + printf("%02x", ((unsigned char*)field_bytes)[i]); + printf("]\n"); + + printf("Deleting a nullptr commitment tree ...\n"); + zendoo_commitment_tree_delete(nullptr); + + printf("Deleting the commitment tree ...\n"); + zendoo_commitment_tree_delete(ct); + + zendoo_field_free(fe0); + zendoo_field_free(fe1); + zendoo_field_free(fe2); + zendoo_field_free(fe3); + zendoo_field_free(fe4); + zendoo_field_free(fe5); } - - wrappedFieldPtr sptrCum = cert.endEpochCumScTxCommTreeRoot.GetFieldElement(); - - bool ret = zendoo_commitment_tree_add_cert(ct, - scid_fe, - epoch_number, - quality, - bt_list, - bt_list_len, - custom_fields.get(), - custom_fields_len, - sptrCum.get(), - cert.forwardTransferScFee, - cert.mainchainBackwardTransferRequestScFee, - &ret_code - ); - ASSERT_TRUE(ret == true); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - - printf("\nChecking commitment tree after cert add ...\n"); - field_t* fe5 = zendoo_commitment_tree_get_commitment(ct, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - ASSERT_TRUE(fe5 != nullptr); - ASSERT_TRUE(memcmp(fe4, fe5, Sidechain::SC_FE_SIZE_IN_BYTES) != 0); - - zendoo_serialize_field(fe5, field_bytes, &ret_code); - ASSERT_TRUE(ret_code == CctpErrorCode::OK); - printf("ct = ["); - for (int i = 0; i < sizeof(field_bytes); i++) - printf("%02x", ((unsigned char*)field_bytes)[i]); - printf("]\n"); - - printf("Deleting a nullptr commitment tree ...\n"); - zendoo_commitment_tree_delete(nullptr); - - printf("Deleting the commitment tree ...\n"); - zendoo_commitment_tree_delete(ct); - - zendoo_field_free(fe0); - zendoo_field_free(fe1); - zendoo_field_free(fe2); - zendoo_field_free(fe3); - zendoo_field_free(fe4); - zendoo_field_free(fe5); } TEST(CctpLibrary, CommitmentTreeBuilding_Negative) { - printf("Creating a commitment tree ...\n"); - CctpErrorCode ret_code = CctpErrorCode::OK; + for (uint8_t sidechainVersion = 0; sidechainVersion <= ForkManager::getInstance().getHighestFork()->getMaxSidechainVersion(); sidechainVersion++) + { + printf("Creating a commitment tree ...\n"); + CctpErrorCode ret_code = CctpErrorCode::OK; - commitment_tree_t* ct = zendoo_commitment_tree_create(); - ASSERT_TRUE(ct != nullptr); + commitment_tree_t* ct = zendoo_commitment_tree_create(); + ASSERT_TRUE(ct != nullptr); - unsigned char field_bytes[CFieldElement::ByteSize()] = {}; + unsigned char field_bytes[CFieldElement::ByteSize()] = {}; - CTransaction tx = CreateDefaultTx(); + CTransaction tx = CreateDefaultTx(sidechainVersion); - const uint256& tx_hash = tx.GetHash(); - BufferWithSize bws_tx_hash(tx_hash.begin(), tx_hash.size()); + const uint256& tx_hash = tx.GetHash(); + BufferWithSize bws_tx_hash(tx_hash.begin(), tx_hash.size()); - uint32_t out_idx = 0; + uint32_t out_idx = 0; - for (const CTxScCreationOut& ccout : tx.GetVscCcOut() ) - { - wrappedFieldPtr sptrScId = CFieldElement(ccout.GetScId()).GetFieldElement(); - field_t* scid_fe = sptrScId.get(); + for (const CTxScCreationOut& ccout : tx.GetVscCcOut() ) + { + wrappedFieldPtr sptrScId = CFieldElement(ccout.GetScId()).GetFieldElement(); + field_t* scid_fe = sptrScId.get(); - CAmount crAmount = ccout.nValue; + CAmount crAmount = ccout.nValue; - const uint256& pub_key = ccout.address; - BufferWithSize bws_pk(pub_key.begin(), pub_key.size()); + const uint256& pub_key = ccout.address; + BufferWithSize bws_pk(pub_key.begin(), pub_key.size()); - uint32_t epoch_len = ccout.withdrawalEpochLength; - uint8_t mbtr_len = ccout.mainchainBackwardTransferRequestDataLength; + uint32_t epoch_len = ccout.withdrawalEpochLength; + uint8_t mbtr_len = ccout.mainchainBackwardTransferRequestDataLength; - std::unique_ptr bws_fe_cfg(nullptr); - std::unique_ptr dum(nullptr); - size_t l = ccout.vFieldElementCertificateFieldConfig.size(); - if (l > 0) - { - dum.reset(new uint8_t[l]); - for (int i = 0; i < l; i++) - dum[i] = ccout.vFieldElementCertificateFieldConfig[i].getBitSize(); - - bws_fe_cfg.reset(new BufferWithSize(dum.get(), l)); - } + std::unique_ptr bws_fe_cfg(nullptr); + std::unique_ptr dum(nullptr); + size_t l = ccout.vFieldElementCertificateFieldConfig.size(); + if (l > 0) + { + dum.reset(new uint8_t[l]); + for (int i = 0; i < l; i++) + dum[i] = ccout.vFieldElementCertificateFieldConfig[i].getBitSize(); + + bws_fe_cfg.reset(new BufferWithSize(dum.get(), l)); + } - int bvcfg_size = ccout.vBitVectorCertificateFieldConfig.size(); - std::unique_ptr bvcfg(new BitVectorElementsConfig[bvcfg_size]); - int i = 0; - for (auto entry: ccout.vBitVectorCertificateFieldConfig) - { - bvcfg[i].bit_vector_size_bits = entry.getBitVectorSizeBits(); - bvcfg[i].max_compressed_byte_size = entry.getMaxCompressedSizeBytes(); - i++; - } - // mc crypto lib wants a null ptr if we have no fields - if (bvcfg_size == 0) - bvcfg.reset(); + int bvcfg_size = ccout.vBitVectorCertificateFieldConfig.size(); + std::unique_ptr bvcfg(new BitVectorElementsConfig[bvcfg_size]); + int i = 0; + for (auto entry: ccout.vBitVectorCertificateFieldConfig) + { + bvcfg[i].bit_vector_size_bits = entry.getBitVectorSizeBits(); + bvcfg[i].max_compressed_byte_size = entry.getMaxCompressedSizeBytes(); + i++; + } + // mc crypto lib wants a null ptr if we have no fields + if (bvcfg_size == 0) + bvcfg.reset(); - std::unique_ptr bws_custom_data(nullptr); - if (!ccout.customData.empty()) - { - bws_custom_data.reset(new BufferWithSize( - (unsigned char*)(&ccout.customData[0]), - ccout.customData.size() - )); - } - - wrappedFieldPtr sptrConstant(nullptr); - if(ccout.constant.is_initialized()) - { - sptrConstant = ccout.constant->GetFieldElement(); - } - field_t* constant_fe = sptrConstant.get(); - - BufferWithSize bws_cert_vk(ccout.wCertVk.GetDataBuffer(), ccout.wCertVk.GetDataSize()); - - std::unique_ptr bws_csw_vk(nullptr); - if(ccout.wCeasedVk.is_initialized()) - { - bws_csw_vk.reset(new BufferWithSize( - ccout.wCeasedVk->GetDataBuffer(), - ccout.wCeasedVk->GetDataSize() - )); + std::unique_ptr bws_custom_data(nullptr); + if (!ccout.customData.empty()) + { + bws_custom_data.reset(new BufferWithSize( + (unsigned char*)(&ccout.customData[0]), + ccout.customData.size() + )); + } + + wrappedFieldPtr sptrConstant(nullptr); + if(ccout.constant.is_initialized()) + { + sptrConstant = ccout.constant->GetFieldElement(); + } + field_t* constant_fe = sptrConstant.get(); + + BufferWithSize bws_cert_vk(ccout.wCertVk.GetDataBuffer(), ccout.wCertVk.GetDataSize()); + + std::unique_ptr bws_csw_vk(nullptr); + if(ccout.wCeasedVk.is_initialized()) + { + bws_csw_vk.reset(new BufferWithSize( + ccout.wCeasedVk->GetDataBuffer(), + ccout.wCeasedVk->GetDataSize() + )); + } + + bool ret = false; + + printf("Adding a sc creation to the commitment tree - using null ptr obj ...\n"); + ret = zendoo_commitment_tree_add_scc(nullptr, // null ptr obj + scid_fe, + crAmount, + &bws_pk, + &bws_tx_hash, + out_idx, + epoch_len, + mbtr_len, + bws_fe_cfg.get(), + bvcfg.get(), + bvcfg_size, + ccout.mainchainBackwardTransferRequestScFee, + ccout.forwardTransferScFee, + bws_custom_data.get(), + constant_fe, + &bws_cert_vk, + bws_csw_vk.get(), + &ret_code + ); + ASSERT_TRUE(ret == false); + ASSERT_TRUE(ret_code == CctpErrorCode::NullPtr); + + printf("Adding a sc creation to the commitment tree - using null ptr params ...\n"); + BufferWithSize bws_bad(nullptr, sizeof(uint256)); + ret = zendoo_commitment_tree_add_scc(ct, + scid_fe, + crAmount, + &bws_pk, + &bws_tx_hash, + out_idx, + epoch_len, + mbtr_len, + &bws_bad, // bad params + bvcfg.get(), + bvcfg_size, + ccout.mainchainBackwardTransferRequestScFee, + ccout.forwardTransferScFee, + bws_custom_data.get(), + constant_fe, + &bws_cert_vk, + bws_csw_vk.get(), + &ret_code + ); + ASSERT_TRUE(ret == false); + ASSERT_TRUE(ret_code == CctpErrorCode::InvalidBufferData); + + printf("Adding a sc creation to the commitment tree - using null ptr buff as a param ...\n"); + ret = zendoo_commitment_tree_add_scc(ct, + scid_fe, + crAmount, + nullptr, // null ptr + &bws_tx_hash, + out_idx, + epoch_len, + mbtr_len, + bws_fe_cfg.get(), + bvcfg.get(), + bvcfg_size, + ccout.mainchainBackwardTransferRequestScFee, + ccout.forwardTransferScFee, + bws_custom_data.get(), + constant_fe, + &bws_cert_vk, + bws_csw_vk.get(), + &ret_code + ); + ASSERT_TRUE(ret == false); + ASSERT_TRUE(ret_code == CctpErrorCode::NullPtr); + + out_idx++; } - bool ret = false; - - printf("Adding a sc creation to the commitment tree - using null ptr obj ...\n"); - ret = zendoo_commitment_tree_add_scc(nullptr, // null ptr obj - scid_fe, - crAmount, - &bws_pk, - &bws_tx_hash, - out_idx, - epoch_len, - mbtr_len, - bws_fe_cfg.get(), - bvcfg.get(), - bvcfg_size, - ccout.mainchainBackwardTransferRequestScFee, - ccout.forwardTransferScFee, - bws_custom_data.get(), - constant_fe, - &bws_cert_vk, - bws_csw_vk.get(), - &ret_code - ); - ASSERT_TRUE(ret == false); - ASSERT_TRUE(ret_code == CctpErrorCode::NullPtr); - - printf("Adding a sc creation to the commitment tree - using null ptr params ...\n"); - BufferWithSize bws_bad(nullptr, sizeof(uint256)); - ret = zendoo_commitment_tree_add_scc(ct, - scid_fe, - crAmount, - &bws_pk, - &bws_tx_hash, - out_idx, - epoch_len, - mbtr_len, - &bws_bad, // bad params - bvcfg.get(), - bvcfg_size, - ccout.mainchainBackwardTransferRequestScFee, - ccout.forwardTransferScFee, - bws_custom_data.get(), - constant_fe, - &bws_cert_vk, - bws_csw_vk.get(), - &ret_code - ); - ASSERT_TRUE(ret == false); - ASSERT_TRUE(ret_code == CctpErrorCode::InvalidBufferData); - - printf("Adding a sc creation to the commitment tree - using null ptr buff as a param ...\n"); - ret = zendoo_commitment_tree_add_scc(ct, - scid_fe, - crAmount, - nullptr, // null ptr - &bws_tx_hash, - out_idx, - epoch_len, - mbtr_len, - bws_fe_cfg.get(), - bvcfg.get(), - bvcfg_size, - ccout.mainchainBackwardTransferRequestScFee, - ccout.forwardTransferScFee, - bws_custom_data.get(), - constant_fe, - &bws_cert_vk, - bws_csw_vk.get(), - &ret_code - ); - ASSERT_TRUE(ret == false); - ASSERT_TRUE(ret_code == CctpErrorCode::NullPtr); - - out_idx++; + printf("Deleting the commitment tree ...\n"); + zendoo_commitment_tree_delete(ct); } - - printf("Deleting the commitment tree ...\n"); - zendoo_commitment_tree_delete(ct); } TEST(CctpLibrary, CommitmentTreeBuilding_Object) { - SidechainTxsCommitmentBuilder cmtObj; + for (uint8_t sidechainVersion = 0; sidechainVersion <= ForkManager::getInstance().getHighestFork()->getMaxSidechainVersion(); sidechainVersion++) + { + SidechainTxsCommitmentBuilder cmtObj; - uint256 cmt = cmtObj.getCommitment(); + uint256 cmt = cmtObj.getCommitment(); - printf("cmt = [%s]\n", cmt.ToString().c_str()); + printf("cmt = [%s]\n", cmt.ToString().c_str()); - CTransaction tx = CreateDefaultTx(); + CTransaction tx = CreateDefaultTx(sidechainVersion); - ASSERT_TRUE(cmtObj.add(tx)); + ASSERT_TRUE(cmtObj.add(tx)); - cmt = cmtObj.getCommitment(); - printf("cmt = [%s]\n", cmt.ToString().c_str()); + cmt = cmtObj.getCommitment(); + printf("cmt = [%s]\n", cmt.ToString().c_str()); - CScCertificate cert = CreateDefaultCert(); + CScCertificate cert = CreateDefaultCert(); - SelectParams(CBaseChainParams::REGTEST); - const BlockchainTestManager& testManager = BlockchainTestManager::GetInstance(); + SelectParams(CBaseChainParams::REGTEST); + const BlockchainTestManager& testManager = BlockchainTestManager::GetInstance(); - ASSERT_TRUE(cmtObj.add(cert, testManager.CoinsViewCache().get())); + ASSERT_TRUE(cmtObj.add(cert, testManager.CoinsViewCache().get())); - cmt = cmtObj.getCommitment(); - printf("cmt = [%s]\n", cmt.ToString().c_str()); + cmt = cmtObj.getCommitment(); + printf("cmt = [%s]\n", cmt.ToString().c_str()); + } } static unsigned char genericArr[37] = { @@ -2037,8 +2065,10 @@ TEST(CctpLibrary, TestGetBytesFromBits) } } -TEST(CctpLibrary, TestCustomFieldsValidation) +TEST(CctpLibrary, TestCustomFieldsValidation_v1) { + uint8_t sidechainVersion = 1; + for (uint8_t i = 1; i < CHAR_BIT; i++) { for (uint8_t j = 1; j > 0; j++) @@ -2046,7 +2076,7 @@ TEST(CctpLibrary, TestCustomFieldsValidation) std::vector rawBytes = { j }; FieldElementCertificateField certField = FieldElementCertificateField(rawBytes); FieldElementCertificateFieldConfig config = FieldElementCertificateFieldConfig(i); - CFieldElement fe = certField.GetFieldElement(config); + CFieldElement fe = certField.GetFieldElement(config, sidechainVersion); if (j < 1 << i) { @@ -2064,8 +2094,10 @@ TEST(CctpLibrary, TestCustomFieldsValidation) * @brief Test the validation of a full (32 bytes) random custom field * iteratively changing the last byte. */ -TEST(CctpLibrary, TestFullCustomFieldValidation) +TEST(CctpLibrary, TestFullCustomFieldValidation_v1) { + uint8_t sidechainVersion = 1; + std::vector rawBytes(std::begin(TEST_CUSTOM_FIELD), std::end(TEST_CUSTOM_FIELD)); for (uint8_t i = 1; i < CHAR_BIT; i++) @@ -2075,7 +2107,7 @@ TEST(CctpLibrary, TestFullCustomFieldValidation) rawBytes.back() = j; FieldElementCertificateField certField = FieldElementCertificateField(rawBytes); FieldElementCertificateFieldConfig config = FieldElementCertificateFieldConfig(i + 31 * CHAR_BIT); - CFieldElement fe = certField.GetFieldElement(config); + CFieldElement fe = certField.GetFieldElement(config, sidechainVersion); // The Field Element is valid if it matches the configuration (j < 1 << i) // and if doesn't exceed the modulus, thus the last two bits cannot be set (j < 1 << 6). @@ -2096,8 +2128,10 @@ TEST(CctpLibrary, TestFullCustomFieldValidation) * with a size equivalent to a long integer (64 bits, 8 bytes) * iteratively changing the last byte. */ -TEST(CctpLibrary, TestLongIntCustomFieldValidation) +TEST(CctpLibrary, TestLongIntCustomFieldValidation_v1) { + uint8_t sidechainVersion = 1; + std::vector rawBytes(std::begin(TEST_CUSTOM_FIELD), std::end(TEST_CUSTOM_FIELD)); rawBytes.resize(8); @@ -2108,7 +2142,7 @@ TEST(CctpLibrary, TestLongIntCustomFieldValidation) rawBytes.back() = j; FieldElementCertificateField certField = FieldElementCertificateField(rawBytes); FieldElementCertificateFieldConfig config = FieldElementCertificateFieldConfig(i + 7 * CHAR_BIT); - CFieldElement fe = certField.GetFieldElement(config); + CFieldElement fe = certField.GetFieldElement(config, sidechainVersion); if (j < 1 << i) { @@ -2127,8 +2161,10 @@ TEST(CctpLibrary, TestLongIntCustomFieldValidation) * with a size equivalent to an integer (32 bits, 4 bytes) * iteratively changing the last byte. */ -TEST(CctpLibrary, TestIntCustomFieldValidation) +TEST(CctpLibrary, TestIntCustomFieldValidation_v1) { + uint8_t sidechainVersion = 1; + std::vector rawBytes(std::begin(TEST_CUSTOM_FIELD), std::end(TEST_CUSTOM_FIELD)); rawBytes.resize(4); @@ -2139,7 +2175,7 @@ TEST(CctpLibrary, TestIntCustomFieldValidation) rawBytes.back() = j; FieldElementCertificateField certField = FieldElementCertificateField(rawBytes); FieldElementCertificateFieldConfig config = FieldElementCertificateFieldConfig(i + 3 * CHAR_BIT); - CFieldElement fe = certField.GetFieldElement(config); + CFieldElement fe = certField.GetFieldElement(config, sidechainVersion); if (j < 1 << i) { diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index d2a7da0eea..d463e73475 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -314,8 +314,8 @@ CTxScCreationOut::CTxScCreationOut( const CAmount& ftScFee, const CAmount& mbtrScFee, const Sidechain::ScFixedParameters& paramsIn) :CTxCrosschainOut(nValueIn, addressIn), generatedScId(), - withdrawalEpochLength(paramsIn.withdrawalEpochLength), customData(paramsIn.customData), constant(paramsIn.constant), - wCertVk(paramsIn.wCertVk), wCeasedVk(paramsIn.wCeasedVk), + version(paramsIn.version), withdrawalEpochLength(paramsIn.withdrawalEpochLength), customData(paramsIn.customData), + constant(paramsIn.constant), wCertVk(paramsIn.wCertVk), wCeasedVk(paramsIn.wCeasedVk), vFieldElementCertificateFieldConfig(paramsIn.vFieldElementCertificateFieldConfig), vBitVectorCertificateFieldConfig(paramsIn.vBitVectorCertificateFieldConfig), forwardTransferScFee(ftScFee), diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 846a7882dd..53970ce336 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -585,6 +585,19 @@ friend class CTransaction; void GenerateScId(const uint256& txHash, unsigned int pos) const; public: + /** + * @brief Version of the sidechain. + * The sidechain version has been introduced with hard fork 9 to increase the flexibility of the sidechain mechanism. + * Since the Sidechain Creation Output was at the beginning designed without the version, a single byty for such field + * has been taken from the WithdrawalEpochLength (before hard fork 9 its size was 4 bytes, after hard fork 9 it is 3 bytes). + * Due to the constraints of the consensus logic, we are sure that the most significant byte of the WithdrawalEpochLength was + * always zero before hard fork 9. + * + * Version 0: due to a bug in the validation of the certificate custom fields, they had to be treated in a special way + * (wrong endianness, see FieldElementCertificateField::GetFieldElement function for further details). + * + * Version 1: the validation of custom fields has been fixed. + */ int8_t version; int withdrawalEpochLength; std::vector customData; @@ -597,7 +610,8 @@ friend class CTransaction; CAmount mainchainBackwardTransferRequestScFee; uint8_t mainchainBackwardTransferRequestDataLength; - CTxScCreationOut(): withdrawalEpochLength(-1), + CTxScCreationOut(): version(0), + withdrawalEpochLength(-1), forwardTransferScFee(-1), mainchainBackwardTransferRequestScFee(-1), mainchainBackwardTransferRequestDataLength(0) { } diff --git a/src/sc/proofverifier.cpp b/src/sc/proofverifier.cpp index bd197c9dd9..0e24d55702 100644 --- a/src/sc/proofverifier.cpp +++ b/src/sc/proofverifier.cpp @@ -62,14 +62,14 @@ CCertProofVerifierInput CScProofVerifier::CertificateToVerifierItem(const CScCer for (int i = 0; i < certificate.vFieldElementCertificateField.size(); i++) { FieldElementCertificateField entry = certificate.vFieldElementCertificateField.at(i); - CFieldElement fe{entry.GetFieldElement(scFixedParams.vFieldElementCertificateFieldConfig.at(i))}; + CFieldElement fe{entry.GetFieldElement(scFixedParams.vFieldElementCertificateFieldConfig.at(i), scFixedParams.version)}; assert(fe.IsValid()); certData.vCustomFields.push_back(fe); } for (int i = 0; i < certificate.vBitVectorCertificateField.size(); i++) { BitVectorCertificateField entry = certificate.vBitVectorCertificateField.at(i); - CFieldElement fe{entry.GetFieldElement(scFixedParams.vBitVectorCertificateFieldConfig.at(i))}; + CFieldElement fe{entry.GetFieldElement(scFixedParams.vBitVectorCertificateFieldConfig.at(i), scFixedParams.version)}; assert(fe.IsValid()); certData.vCustomFields.push_back(fe); } diff --git a/src/sc/sidechain.cpp b/src/sc/sidechain.cpp index 910ca732d7..1a5419f581 100644 --- a/src/sc/sidechain.cpp +++ b/src/sc/sidechain.cpp @@ -479,7 +479,7 @@ bool Sidechain::checkCertCustomFields(const CSidechain& sidechain, const CScCert for (int i = 0; i < vCfe.size(); i++) { const FieldElementCertificateField& fe = vCfe.at(i); - if (!fe.IsValid(vCfeCfg.at(i))) + if (!fe.IsValid(vCfeCfg.at(i), sidechain.fixedParams.version)) { LogPrint("sc", "%s():%d - invalid custom field at pos %d\n", __func__, __LINE__, i); return false; @@ -489,7 +489,7 @@ bool Sidechain::checkCertCustomFields(const CSidechain& sidechain, const CScCert for (int i = 0; i < vCmt.size(); i++) { const BitVectorCertificateField& cmt = vCmt.at(i); - if (!cmt.IsValid(vCmtCfg.at(i))) + if (!cmt.IsValid(vCmtCfg.at(i), sidechain.fixedParams.version)) { LogPrint("sc", "%s():%d - invalid compr mkl tree field at pos %d\n", __func__, __LINE__, i); return false; diff --git a/src/sc/sidechainTxsCommitmentBuilder.cpp b/src/sc/sidechainTxsCommitmentBuilder.cpp index 5d0c12ff4a..fdae906731 100644 --- a/src/sc/sidechainTxsCommitmentBuilder.cpp +++ b/src/sc/sidechainTxsCommitmentBuilder.cpp @@ -254,7 +254,7 @@ bool SidechainTxsCommitmentBuilder::add_cert(const CScCertificate& cert, const S for (i = 0; i < cert.vFieldElementCertificateField.size(); i++) { FieldElementCertificateField entry = cert.vFieldElementCertificateField.at(i); - CFieldElement fe{entry.GetFieldElement(scFixedParams.vFieldElementCertificateFieldConfig.at(i))}; + CFieldElement fe{entry.GetFieldElement(scFixedParams.vFieldElementCertificateFieldConfig.at(i), scFixedParams.version)}; wrappedFieldPtr sptrFe = fe.GetFieldElement(); custom_fields[i] = sptrFe.get(); vSptr.push_back(sptrFe); @@ -263,7 +263,7 @@ bool SidechainTxsCommitmentBuilder::add_cert(const CScCertificate& cert, const S for (int j = 0; j < cert.vBitVectorCertificateField.size(); j++) { BitVectorCertificateField entry = cert.vBitVectorCertificateField.at(j); - CFieldElement fe{entry.GetFieldElement(scFixedParams.vBitVectorCertificateFieldConfig.at(j))}; + CFieldElement fe{entry.GetFieldElement(scFixedParams.vBitVectorCertificateFieldConfig.at(j), scFixedParams.version)}; wrappedFieldPtr sptrFe = fe.GetFieldElement(); custom_fields[i+j] = sptrFe.get(); vSptr.push_back(sptrFe); diff --git a/src/sc/sidechaintypes.cpp b/src/sc/sidechaintypes.cpp index d418331e27..9d72505050 100644 --- a/src/sc/sidechaintypes.cpp +++ b/src/sc/sidechaintypes.cpp @@ -549,12 +549,12 @@ FieldElementCertificateField& FieldElementCertificateField::operator=(const Fiel return *this; } -bool FieldElementCertificateField::IsValid(const FieldElementCertificateFieldConfig& cfg) const +bool FieldElementCertificateField::IsValid(const FieldElementCertificateFieldConfig& cfg, uint8_t sidechainVersion) const { - return !this->GetFieldElement(cfg).IsNull(); + return !this->GetFieldElement(cfg, sidechainVersion).IsNull(); } -const CFieldElement& FieldElementCertificateField::GetFieldElement(const FieldElementCertificateFieldConfig& cfg) const +const CFieldElement& FieldElementCertificateField::GetFieldElement(const FieldElementCertificateFieldConfig& cfg, uint8_t sidechainVersion) const { if (state != VALIDATION_STATE::NOT_INITIALIZED) { @@ -595,7 +595,17 @@ const CFieldElement& FieldElementCertificateField::GetFieldElement(const FieldEl { // check null bits in the last byte are as expected unsigned char lastByte = vRawData.back(); - int numbOfZeroBits = getLeadingZeroBitsInByte(lastByte); + int numbOfZeroBits = 0; + + if (sidechainVersion == 0) + { + numbOfZeroBits = getTrailingZeroBitsInByte(lastByte); + } + else + { + numbOfZeroBits = getLeadingZeroBitsInByte(lastByte); + } + if (numbOfZeroBits < (CHAR_BIT - rem)) { LogPrint("sc", "%s():%d - ERROR: wrong number of null bits in last byte[0x%x]: %d vs %d\n", @@ -605,7 +615,15 @@ const CFieldElement& FieldElementCertificateField::GetFieldElement(const FieldEl } std::vector extendedRawData = vRawData; - extendedRawData.insert(extendedRawData.end(), CFieldElement::ByteSize() - vRawData.size(), 0x0); + + if (sidechainVersion == 0) + { + extendedRawData.insert(extendedRawData.begin(), CFieldElement::ByteSize() - vRawData.size(), 0x0); + } + else + { + extendedRawData.insert(extendedRawData.end(), CFieldElement::ByteSize() - vRawData.size(), 0x0); + } fieldElement.SetByteArray(extendedRawData); if (fieldElement.IsValid()) @@ -642,12 +660,12 @@ BitVectorCertificateField& BitVectorCertificateField::operator=(const BitVectorC return *this; } -bool BitVectorCertificateField::IsValid(const BitVectorCertificateFieldConfig& cfg) const +bool BitVectorCertificateField::IsValid(const BitVectorCertificateFieldConfig& cfg, uint8_t sidechainVersion) const { - return !this->GetFieldElement(cfg).IsNull(); + return !this->GetFieldElement(cfg, sidechainVersion).IsNull(); } -const CFieldElement& BitVectorCertificateField::GetFieldElement(const BitVectorCertificateFieldConfig& cfg) const +const CFieldElement& BitVectorCertificateField::GetFieldElement(const BitVectorCertificateFieldConfig& cfg, uint8_t sidechainVersion) const { if (state != VALIDATION_STATE::NOT_INITIALIZED) { diff --git a/src/sc/sidechaintypes.h b/src/sc/sidechaintypes.h index 81db3669a6..f51bad3a99 100644 --- a/src/sc/sidechaintypes.h +++ b/src/sc/sidechaintypes.h @@ -369,7 +369,7 @@ class CustomCertificateField enum class VALIDATION_STATE {NOT_INITIALIZED, INVALID, VALID}; mutable VALIDATION_STATE state; mutable CFieldElement fieldElement; // memory only, lazy-initialized - virtual const CFieldElement& GetFieldElement(const T& cfg) const = 0; + virtual const CFieldElement& GetFieldElement(const T& cfg, uint8_t sidechainVersion) const = 0; public: CustomCertificateField(): state(VALIDATION_STATE::NOT_INITIALIZED) {}; @@ -397,8 +397,8 @@ class FieldElementCertificateField : public CustomCertificateField*>(&vRawData)); } - const CFieldElement& GetFieldElement(const FieldElementCertificateFieldConfig& cfg) const override; - bool IsValid(const FieldElementCertificateFieldConfig& cfg) const; + const CFieldElement& GetFieldElement(const FieldElementCertificateFieldConfig& cfg, uint8_t sidechainVersion) const override; + bool IsValid(const FieldElementCertificateFieldConfig& cfg, uint8_t sidechainVersion) const; }; class BitVectorCertificateField : public CustomCertificateField @@ -418,8 +418,8 @@ class BitVectorCertificateField : public CustomCertificateField*>(&vRawData)); } - const CFieldElement& GetFieldElement(const BitVectorCertificateFieldConfig& cfg) const override; - bool IsValid(const BitVectorCertificateFieldConfig& cfg) const; + const CFieldElement& GetFieldElement(const BitVectorCertificateFieldConfig& cfg, uint8_t sidechainVersion) const override; + bool IsValid(const BitVectorCertificateFieldConfig& cfg, uint8_t sidechainVersion) const; }; ////////////////////////// End of Custom Field types /////////////////////////// diff --git a/src/util.cpp b/src/util.cpp index 0f6c64bd3d..3cc0372a03 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -955,6 +955,23 @@ int getLeadingZeroBitsInByte(unsigned char inputByte) return CHAR_BIT - nonZeroBits; } +int getTrailingZeroBitsInByte(unsigned char inputByte) +{ + // output: c will count inputByte's trailing zero bits, + // so if inputByte is 1101000 (base 2), then c will be 3 + int c = CHAR_BIT; + + if (inputByte) + { + inputByte = (inputByte ^ (inputByte - 1)) >> 1; // Set inputByte's trailing 0s to 1s and zero rest + for (c = 0; inputByte; c++) + { + inputByte >>= 1; + } + } + return c; +} + int getBytesFromBits(int nbits, int& reminder) { reminder = 0; diff --git a/src/util.h b/src/util.h index 818c273ef8..96b9826b07 100644 --- a/src/util.h +++ b/src/util.h @@ -297,6 +297,7 @@ std::string dbg_blk_candidates(); std::string dbg_blk_global_tips(); int getLeadingZeroBitsInByte(unsigned char inputByte); +int getTrailingZeroBitsInByte(unsigned char inputByte); int getBytesFromBits(int nbits, int& reminder); #endif // BITCOIN_UTIL_H diff --git a/src/zen/forkmanager.cpp b/src/zen/forkmanager.cpp index a1bb1b27cd..95dde4a8b3 100644 --- a/src/zen/forkmanager.cpp +++ b/src/zen/forkmanager.cpp @@ -30,6 +30,11 @@ ForkManager& ForkManager::getInstance() { return instance; } +const Fork* ForkManager::getHighestFork() const +{ + return forks.back(); +} + /** * @brief selectNetwork is called by SelectParams in chainparams.cpp to select the current network * @param network the newly selected network diff --git a/src/zen/forkmanager.h b/src/zen/forkmanager.h index 76530151c5..45455b8f67 100644 --- a/src/zen/forkmanager.h +++ b/src/zen/forkmanager.h @@ -29,7 +29,12 @@ class ForkManager * @brief getInstance returns the ForkManager static instance. */ static ForkManager& getInstance(); - + + /** + * @brief Get the fork that activates later than all the other ones. + */ + const Fork* getHighestFork() const; + /** * @brief selectNetwork is called by SelectParams in chainparams.cpp to select the current network */ diff --git a/src/zen/forks/fork9_sidechainversionfork.cpp b/src/zen/forks/fork9_sidechainversionfork.cpp index 20d361e801..3b714f9caa 100644 --- a/src/zen/forks/fork9_sidechainversionfork.cpp +++ b/src/zen/forks/fork9_sidechainversionfork.cpp @@ -4,6 +4,7 @@ namespace zen { SidechainVersionFork::SidechainVersionFork() { + // TODO: set proper fork height values. setHeightMap({{CBaseChainParams::Network::MAIN,1100000}, {CBaseChainParams::Network::REGTEST,450}, {CBaseChainParams::Network::TESTNET,1000000}}); From b1c0f82ac7b2deac55ebadf3cbcf7f524e29a715 Mon Sep 17 00:00:00 2001 From: Paolo Tagliaferri Date: Thu, 3 Feb 2022 18:11:20 +0100 Subject: [PATCH 13/58] Sidechain version fork height management Modified the ContextualCheck() function to enforce that a sidechain creation transaction uses only the allowed version (before and after the Sidechain Version fork). This function is called both in AcceptTxToMemoryPool() and in AcceptBlock(). In order to implement some unit tests for all the scenarios, the test framework has been extended to call AcceptTxToMemoryPool() safely. --- src/gtest/test_checkblock.cpp | 102 +++++++++++++++++++++++++++ src/gtest/test_mempool.cpp | 59 ++++++++++++++++ src/gtest/tx_creation_utils.cpp | 118 ++++++++++++++++++++++++++++++++ src/gtest/tx_creation_utils.h | 10 ++- src/primitives/transaction.cpp | 20 +++++- 5 files changed, 305 insertions(+), 4 deletions(-) diff --git a/src/gtest/test_checkblock.cpp b/src/gtest/test_checkblock.cpp index a542bd9546..f97b047ca9 100644 --- a/src/gtest/test_checkblock.cpp +++ b/src/gtest/test_checkblock.cpp @@ -12,6 +12,9 @@ #include "zen/forks/fork4_nulltransactionfork.h" #include "zen/forks/fork5_shieldfork.h" #include "zen/forks/fork8_sidechainfork.h" + +#include "tx_creation_utils.h" + using namespace zen; TEST(CheckBlock, VersionTooLow) { @@ -826,3 +829,102 @@ TEST(ContextualCheckBlock, CoinbaseCommunityRewardAddress) { EXPECT_TRUE(ContextualCheckBlock(block, state_5, &indexPrev)); } +/** + * @brief Tests if a list of transactions are valid or not at a specified height. + * + * The check is performed in relation to the logic introduced by the SidechainVersionFork only. + * Please, note that the block forged to perform the test already includes a coinbase transaction. + * + * @param blockHeight The height of the block in which the transactions are included + * @param transactions The transactions to be included in the block + * @param shouldSucceed True if the test must succeed, false otherwise + */ +void TestSidechainCreationVersion(int blockHeight, std::vector transactions, bool shouldSucceed) +{ + CBlock prev; + CBlockIndex indexPrev {prev}; + indexPrev.nHeight = blockHeight - 1; + + CBlock block = blockchain_test_utils::BlockchainTestManager::GenerateValidBlock(blockHeight); + + for (auto& tx : transactions) + { + block.vtx.push_back(tx); + } + + CValidationState state; + + if (shouldSucceed) + { + EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev)); + } + else + { + EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev)); + EXPECT_EQ(state.GetDoS(), 100); + EXPECT_EQ(state.GetRejectCode(), CValidationState::Code::INVALID); + EXPECT_EQ(state.GetRejectReason(), std::string("bad-tx-sc-creation-wrong-version")); + EXPECT_FALSE(state.CorruptionPossible()); + } +} + +TEST(ContextualCheckBlock, SidechainCreationVersion) +{ + SelectParams(CBaseChainParams::MAIN); + + int sidechainVersionForkHeight = 1100000; + + // Create a Sidechain Creation transaction with version 0 + CMutableTransaction mtx_v0 = txCreationUtils::createNewSidechainTxWith(CAmount(10)); + + for (CTxScCreationOut& out : mtx_v0.vsc_ccout) + { + out.version = 0; + } + + // Create a Sidechain Creation transaction with version 1 + CMutableTransaction mtx_v1 = txCreationUtils::createNewSidechainTxWith(CAmount(10)); + + for (CTxScCreationOut& out : mtx_v1.vsc_ccout) + { + out.version = 1; + } + + // Test a block immediately before the sidechain version fork point; SC version 0 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight - 1, {mtx_v0, mtx_v0}, true); + + // Create a block immediately before the sidechain version fork point; SC version 1 must be rejected + TestSidechainCreationVersion(sidechainVersionForkHeight - 1, {mtx_v1, mtx_v1}, false); + + // Create a block immediately before the sidechain version fork point; SC version 1 must be rejected (even though the first transaction is OK) + TestSidechainCreationVersion(sidechainVersionForkHeight - 1, {mtx_v0, mtx_v1}, false); + + // Create a block immediately before the sidechain version fork point; SC version 1 must be rejected (even though the second transaction is OK) + TestSidechainCreationVersion(sidechainVersionForkHeight - 1, {mtx_v1, mtx_v0}, false); + + + // Test a block exactly at the sidechain version fork point; SC version 0 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight, {mtx_v0, mtx_v0}, true); + + // Create a block exactly at the sidechain version fork point; SC version 1 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight, {mtx_v1, mtx_v1}, true); + + // Create a block exactly at the sidechain version fork point; SC version 0 and 1 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight, {mtx_v0, mtx_v1}, true); + + // Create a block exactly at the sidechain version fork point; SC version 0 and 1 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight, {mtx_v1, mtx_v0}, true); + + + // Test a block after the sidechain version fork point; SC version 0 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight + 1, {mtx_v0, mtx_v0}, true); + + // Create a block after the sidechain version fork point; SC version 1 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight + 1, {mtx_v1, mtx_v1}, true); + + // Create a block after the sidechain version fork point; SC version 0 and 1 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight + 1, {mtx_v0, mtx_v1}, true); + + // Create a block after the sidechain version fork point; SC version 0 and 1 must be accepted + TestSidechainCreationVersion(sidechainVersionForkHeight + 1, {mtx_v1, mtx_v0}, true); +} diff --git a/src/gtest/test_mempool.cpp b/src/gtest/test_mempool.cpp index 4f00a89eab..c3d5d96c23 100644 --- a/src/gtest/test_mempool.cpp +++ b/src/gtest/test_mempool.cpp @@ -351,6 +351,65 @@ TEST(Mempool, SproutNegativeVersionTx) { boost::filesystem::remove_all(pathTemp.string(), ec); } +/** + * @brief Tests the mempool behavior in relation to the SidechainVersionFork. + */ +TEST(Mempool, SidechainVersionTest) +{ + SelectParams(CBaseChainParams::REGTEST); + int sidechainVersionForkHeight = 450; + blockchain_test_utils::BlockchainTestManager& testManager = blockchain_test_utils::BlockchainTestManager::GetInstance(); + + // Initialize the sidechain keys + testManager.GenerateSidechainTestParameters(ProvingSystem::CoboundaryMarlin, TestCircuitType::Certificate); + + // Create a Sidechain Creation transaction with version 0 + blockchain_test_utils::CTransactionCreationArguments args; + args.fGenerateValidInput = true; + args.nVersion = SC_TX_VERSION; + args.vsc_ccout.push_back(testManager.CreateScCreationOut(0, ProvingSystem::CoboundaryMarlin)); + CMutableTransaction mtx_v0 = testManager.CreateTransaction(args); + + // Create a Sidechain Creation transaction with version 1 + args.vsc_ccout[0] = testManager.CreateScCreationOut(1, ProvingSystem::CoboundaryMarlin); + CMutableTransaction mtx_v1 = testManager.CreateTransaction(args); + + // Go to the last block before the SidechainVersionFork + testManager.ExtendChainActiveToHeight(sidechainVersionForkHeight - 2); + + // Check that a Sidechain Creation transaction with version 0 is accepted + CValidationState state; + EXPECT_EQ(MempoolReturnValue::VALID, testManager.TestAcceptTxToMemoryPool(state, mtx_v0)); + + // Check that a Sidechain Creation transaction with version 1 is rejected + state = CValidationState(); + EXPECT_EQ(MempoolReturnValue::INVALID, testManager.TestAcceptTxToMemoryPool(state, mtx_v1)); + EXPECT_EQ(state.GetRejectReason(), "bad-tx-sc-creation-wrong-version"); + + // Go to the block of the SidechainVersionFork + testManager.ExtendChainActiveToHeight(sidechainVersionForkHeight - 1); + + // Check that a Sidechain Creation transaction with version 0 is accepted + state = CValidationState(); + EXPECT_EQ(MempoolReturnValue::VALID, testManager.TestAcceptTxToMemoryPool(state, mtx_v0)); + + // Check that a Sidechain Creation transaction with version 1 is accepted + state = CValidationState(); + EXPECT_EQ(MempoolReturnValue::VALID, testManager.TestAcceptTxToMemoryPool(state, mtx_v1)); + + // Go to the next block after the SidechainVersionFork + testManager.ExtendChainActiveToHeight(sidechainVersionForkHeight); + + // Check that a Sidechain Creation transaction with version 0 is accepted + state = CValidationState(); + EXPECT_EQ(MempoolReturnValue::VALID, testManager.TestAcceptTxToMemoryPool(state, mtx_v0)); + + // Check that a Sidechain Creation transaction with version 1 is accepted + state = CValidationState(); + EXPECT_EQ(MempoolReturnValue::VALID, testManager.TestAcceptTxToMemoryPool(state, mtx_v1)); +} + + /////////////////////////////////////////////////////////////////////////////// ////////////////////////////// ProcessMempoolMsg ////////////////////////////// diff --git a/src/gtest/tx_creation_utils.cpp b/src/gtest/tx_creation_utils.cpp index e81f2bafa7..8ac6f5dd15 100644 --- a/src/gtest/tx_creation_utils.cpp +++ b/src/gtest/tx_creation_utils.cpp @@ -15,6 +15,7 @@ #include