From 715b7283d892f3070f9450d2a7524e353afcb922 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Fri, 10 May 2024 02:41:49 -0700 Subject: [PATCH 01/22] Added dyn fee txn encoding and tests --- .gitignore | 3 + .../Eth/Transaction/DynamicFee.hpp | 427 ++++++++++++++++++ test/src/Common.hpp | 25 + test/src/Main.cpp | 2 +- test/src/TestEthTxnDynFee.cpp | 317 +++++++++++++ 5 files changed, 773 insertions(+), 1 deletion(-) create mode 100644 include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp create mode 100644 test/src/Common.hpp create mode 100644 test/src/TestEthTxnDynFee.cpp diff --git a/.gitignore b/.gitignore index 0de8b97..25c6807 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ build*/ # VS Code temporary folder .vscode/ + +# python virtual environment +venv/ diff --git a/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp b/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp new file mode 100644 index 0000000..2bad5cc --- /dev/null +++ b/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp @@ -0,0 +1,427 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#include + +#include "../../Exceptions.hpp" +#include "../../Internal/SimpleObj.hpp" +#include "../../Internal/SimpleRlp.hpp" + +#include "../DataTypes.hpp" +#include "../Keccak256.hpp" + + +namespace EclipseMonitor +{ +namespace Eth +{ +namespace Transaction +{ + + +using AccessListObj = Internal::Obj::List; + + + +inline void ValidateContractAddr( + const Internal::Obj::BytesBaseObj& addr +) +{ + if (addr.size() != 20) + { + throw Exception("Invalid access list address size"); + } +} + + +inline void ValidateAccessListStorageKey( + const Internal::Obj::BytesBaseObj& storageKey +) +{ + if (storageKey.size() != 32) + { + throw Exception("Invalid access list storage key size"); + } +} + + +inline void ValidateAccessListStorageKeys( + const Internal::Obj::ListBaseObj& storageKeys +) +{ + for (const auto& storageKey : storageKeys) + { + if (storageKey.GetCategory() != Internal::Obj::ObjCategory::Bytes) + { + throw Exception("Invalid access list storage key type"); + } + ValidateAccessListStorageKey(storageKey.AsBytes()); + } +} + + +/** + * @brief validate the tuples in the access list object; each tuple should + * contain two elements: address(bytes) and storageKeys(list[bytes]) + * reference: https://github.com/ethereum/eth-account/blob/8478a86a8d235acba0a33fcae5804887473c72de/eth_account/account.py#L666 + * + * @param accessTuple the tuple to be validated + */ +inline void ValidateAccessListTuple(const Internal::Obj::ListBaseObj& accessTuple) +{ + if (accessTuple.size() != 2) + { + throw Exception("Invalid access list tuple size"); + } + + if (accessTuple[0].GetCategory() != Internal::Obj::ObjCategory::Bytes) + { + throw Exception("Invalid access list address type"); + } + ValidateContractAddr(accessTuple[0].AsBytes()); + + if (accessTuple[1].GetCategory() != Internal::Obj::ObjCategory::List) + { + throw Exception("Invalid access list storage keys type"); + } + ValidateAccessListStorageKeys(accessTuple[1].AsList()); +} + + +/** + * @brief validate the access list object, which supposed to be a list of + * tuple[bytes, list[bytes]] + * + * @param accessList the access list object to be validated + */ +inline void ValidateAccessList(const AccessListObj& accessList) +{ + for (const auto& tuple : accessList) + { + if (tuple.GetCategory() != Internal::Obj::ObjCategory::List) + { + throw Exception("Invalid access list tuple type"); + } + ValidateAccessListTuple(tuple.AsList()); + } +} + + +/** + * @brief fields in a dynamic fee transaction object + * which are: chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, + * gas_limit, destination, amount, data, access_list + * source: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md + * + */ +using DynFeeTupleCore = std::tuple< + // 01. + std::pair, + Internal::Obj::Bytes>, + // 02. + std::pair, + Internal::Obj::Bytes>, + // 03. + std::pair, + Internal::Obj::Bytes>, + // 04. + std::pair, + Internal::Obj::Bytes>, + // 05. + std::pair, + Internal::Obj::Bytes>, + // 06. + std::pair, + Internal::Obj::Bytes>, + // 07. + std::pair, + Internal::Obj::Bytes>, + // 08. + std::pair, + Internal::Obj::Bytes>, + // 09. + std::pair, + AccessListObj> + >; + + +using DynFeeParserTupleCore = std::tuple< + // 01. + std::pair, + Internal::Rlp::BytesParser>, + // 02. + std::pair, + Internal::Rlp::BytesParser>, + // 03. + std::pair, + Internal::Rlp::BytesParser>, + // 04. + std::pair, + Internal::Rlp::BytesParser>, + // 05. + std::pair, + Internal::Rlp::BytesParser>, + // 06. + std::pair, + Internal::Rlp::BytesParser>, + // 07. + std::pair, + Internal::Rlp::BytesParser>, + // 08. + std::pair, + Internal::Rlp::BytesParser>, + // 09. + std::pair, + Internal::Rlp::ListParser> +>; + + +class DynFee : + public Internal::Obj::StaticDict +{ +public: // static members: + + using Self = DynFee; + using Base = Internal::Obj::StaticDict; + + static Self FromRlp(const Internal::Rlp::InputContainerType& rlp); + +public: + + using Base::Base; + + Internal::Rlp::OutputContainerType RlpSerialize() const + { + // validate the access list object + ValidateAccessList(get_AccessList()); + + // validate the destination address + ValidateContractAddr(get_Destination()); + + return Internal::Rlp::WriterGeneric::Write(*this); + } + + /** + * @brief Get the type flag, which is 0x02 for dynamic fee transaction + * + * @return the type flag in a byte + */ + uint8_t GetTypeFlag() const + { + return 0x02U; + } + + std::array Hash() const + { + auto rlp = RlpSerialize(); + + // prepend the type flag + rlp.insert(rlp.begin(), GetTypeFlag()); + + return Keccak256(rlp); + } + + // 01. ChainID + typename Base::template GetRef > + get_ChainID() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_ChainID() const + { + return Base::template get >(); + } + + void SetChainID(uint64_t chainID) + { + get_ChainID() = PrimitiveTypeTrait::ToBytes(chainID); + } + + uint64_t GetChainID() const + { + return PrimitiveTypeTrait::FromBytes(get_ChainID()); + } + + // 02. Nonce + typename Base::template GetRef > + get_Nonce() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_Nonce() const + { + return Base::template get >(); + } + + void SetNonce(uint64_t nonce) + { + get_Nonce() = PrimitiveTypeTrait::ToBytes(nonce); + } + + uint64_t GetNonce() const + { + return PrimitiveTypeTrait::FromBytes(get_Nonce()); + } + + // 03. MaxPriorFeePerGas + typename Base::template GetRef > + get_MaxPriorFeePerGas() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_MaxPriorFeePerGas() const + { + return Base::template get >(); + } + + void SetMaxPriorFeePerGas(uint64_t maxPriorFeePerGas) + { + get_MaxPriorFeePerGas() = PrimitiveTypeTrait::ToBytes(maxPriorFeePerGas); + } + + uint64_t GetMaxPriorFeePerGas() const + { + return PrimitiveTypeTrait::FromBytes(get_MaxPriorFeePerGas()); + } + + // 04. MaxFeePerGas + typename Base::template GetRef > + get_MaxFeePerGas() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_MaxFeePerGas() const + { + return Base::template get >(); + } + + void SetMaxFeePerGas(uint64_t maxFeePerGas) + { + get_MaxFeePerGas() = PrimitiveTypeTrait::ToBytes(maxFeePerGas); + } + + uint64_t GetMaxFeePerGas() const + { + return PrimitiveTypeTrait::FromBytes(get_MaxFeePerGas()); + } + + // 05. GasLimit + typename Base::template GetRef > + get_GasLimit() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_GasLimit() const + { + return Base::template get >(); + } + + void SetGasLimit(uint64_t gasLimit) + { + get_GasLimit() = PrimitiveTypeTrait::ToBytes(gasLimit); + } + + uint64_t GetGasLimit() const + { + return PrimitiveTypeTrait::FromBytes(get_GasLimit()); + } + + // 06. Destination + typename Base::template GetRef > + get_Destination() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_Destination() const + { + return Base::template get >(); + } + + // 07. Amount + typename Base::template GetRef > + get_Amount() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_Amount() const + { + return Base::template get >(); + } + + void SetAmount(uint64_t amount) + { + get_Amount() = PrimitiveTypeTrait::ToBytes(amount); + } + + uint64_t GetAmount() const + { + return PrimitiveTypeTrait::FromBytes(get_Amount()); + } + + // 08. Data + typename Base::template GetRef > + get_Data() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_Data() const + { + return Base::template get >(); + } + + // 09. AccessList + typename Base::template GetRef > + get_AccessList() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_AccessList() const + { + return Base::template get >(); + } + +}; // class DynFee + + + +using DynFeeParser = Internal::Rlp::StaticDictParserT< + DynFeeParserTupleCore, + false, + false, + DynFee +>; + + +inline DynFee::Self DynFee::FromRlp(const Internal::Rlp::InputContainerType& rlp) +{ + return DynFeeParser().Parse(rlp); +} + + +} // namespace Transaction +} // namespace Eth +} // namespace EclipseMonitor + diff --git a/test/src/Common.hpp b/test/src/Common.hpp new file mode 100644 index 0000000..9af6bc8 --- /dev/null +++ b/test/src/Common.hpp @@ -0,0 +1,25 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#define EXPECT_THROW_MSG(statement, exceptionType, expectedMsg) \ +{ \ + try \ + { \ + statement; \ + FAIL() << "Expecting exception of type " #exceptionType; \ + } \ + catch (const exceptionType& e) \ + { \ + EXPECT_STREQ(e.what(), expectedMsg); \ + } \ + catch (...) \ + { \ + FAIL() << "Expecting exception of type " #exceptionType; \ + } \ +} + diff --git a/test/src/Main.cpp b/test/src/Main.cpp index 90e1425..22ff8a3 100644 --- a/test/src/Main.cpp +++ b/test/src/Main.cpp @@ -12,7 +12,7 @@ namespace EclipseMonitor_Test int main(int argc, char** argv) { - constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 22; + constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 23; std::cout << "===== EclipseMonitor test program =====" << std::endl; std::cout << std::endl; diff --git a/test/src/TestEthTxnDynFee.cpp b/test/src/TestEthTxnDynFee.cpp new file mode 100644 index 0000000..899006a --- /dev/null +++ b/test/src/TestEthTxnDynFee.cpp @@ -0,0 +1,317 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + + +#include + +#include +#include + +#include + +#include "Common.hpp" + + +namespace EclipseMonitor_Test +{ + extern size_t g_numOfTestFile; +} + +using namespace EclipseMonitor_Test; + +using namespace EclipseMonitor::Eth; + + +GTEST_TEST(TestEthTxnDynFee, CountTestFile) +{ + static auto tmp = ++EclipseMonitor_Test::g_numOfTestFile; + (void)tmp; +} + + +GTEST_TEST(TestEthTxnDynFee, ValidateAccessList) +{ + { + Transaction::AccessListObj accessListEmpty; + EXPECT_NO_THROW(Transaction::ValidateAccessList(accessListEmpty)); + } + + { + Transaction::AccessListObj accessListEmptyStKeys = { + SimpleObjects::List({ + // address + SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }), + // storage keys + SimpleObjects::List() + }) + }; + EXPECT_NO_THROW(Transaction::ValidateAccessList(accessListEmptyStKeys)); + } + + { + Transaction::AccessListObj accessList = { + SimpleObjects::List({ + // address + SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }), + // storage keys + SimpleObjects::List({ + SimpleObjects::Bytes({ + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }) + }) + }) + }; + EXPECT_NO_THROW(Transaction::ValidateAccessList(accessList)); + } + + { + Transaction::AccessListObj accessListInvalidAddr = { + SimpleObjects::List({ + // address + SimpleObjects::String("0000000000000000000000000000000000000001"), + // storage keys + SimpleObjects::List() + }) + }; + EXPECT_THROW_MSG( + Transaction::ValidateAccessList(accessListInvalidAddr), + EclipseMonitor::Exception, + "Invalid access list address type" + ); + } + + { + Transaction::AccessListObj accessListInvalidAddr = { + SimpleObjects::List({ + // address + SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, + }), + // storage keys + SimpleObjects::List() + }) + }; + EXPECT_THROW_MSG( + Transaction::ValidateAccessList(accessListInvalidAddr), + EclipseMonitor::Exception, + "Invalid access list address size" + ); + } + + { + Transaction::AccessListObj accessListInvalidTuple = { + SimpleObjects::List({ + // address + SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }), + }) + }; + EXPECT_THROW_MSG( + Transaction::ValidateAccessList(accessListInvalidTuple), + EclipseMonitor::Exception, + "Invalid access list tuple size" + ); + } + + { + Transaction::AccessListObj accessListInvalidKeys = { + SimpleObjects::List({ + // address + SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }), + // storage keys + SimpleObjects::String("0100000000000000000000000000000000000000000000000000000000000000"), + }) + }; + EXPECT_THROW_MSG( + Transaction::ValidateAccessList(accessListInvalidKeys), + EclipseMonitor::Exception, + "Invalid access list storage keys type" + ); + } + + { + Transaction::AccessListObj accessListInvalidKey = { + SimpleObjects::List({ + // address + SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }), + // storage keys + SimpleObjects::List({ + SimpleObjects::String("0100000000000000000000000000000000000000000000000000000000000000"), + }) + }) + }; + EXPECT_THROW_MSG( + Transaction::ValidateAccessList(accessListInvalidKey), + EclipseMonitor::Exception, + "Invalid access list storage key type" + ); + } + + { + Transaction::AccessListObj accessListInvalidKey = { + SimpleObjects::List({ + // address + SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }), + // storage keys + SimpleObjects::List({ + SimpleObjects::Bytes({ + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }) + }) + }) + }; + EXPECT_THROW_MSG( + Transaction::ValidateAccessList(accessListInvalidKey), + EclipseMonitor::Exception, + "Invalid access list storage key size" + ); + } +} + + +GTEST_TEST(TestEthTxnDynFee, DeEncoding) +{ + { + uint64_t chainID = 1900ULL; + uint64_t nonce = 34ULL; + uint64_t maxPriorFeePerGas = 2000000000ULL; + uint64_t maxFeePerGas = 2000000000ULL; + uint64_t gasLimit = 100000ULL; + SimpleObjects::Bytes destination = { + // 0x09616C3d61b3331fc4109a9E41a8BDB7d9776609 + 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, 0x33U, 0x1fU, + 0xc4U, 0x10U, 0x9aU, 0x9eU, 0x41U, 0xa8U, 0xbdU, 0xb7U, + 0xd9U, 0x77U, 0x66U, 0x09U, + }; + uint64_t amount = 100000000000000ULL; + SimpleObjects::Bytes blkData = { + // 0x616263646566 + 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U, + }; + Transaction::AccessListObj accessList = { + SimpleObjects::List({ + // address + SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }), + // storage keys + SimpleObjects::List({ + SimpleObjects::Bytes({ + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }) + }) + }) + }; + + Transaction::DynFee dynFeeTxn; + dynFeeTxn.SetChainID(chainID); + dynFeeTxn.SetNonce(nonce); + dynFeeTxn.SetMaxPriorFeePerGas(maxPriorFeePerGas); + dynFeeTxn.SetMaxFeePerGas(maxFeePerGas); + dynFeeTxn.SetGasLimit(gasLimit); + dynFeeTxn.get_Destination() = destination; + dynFeeTxn.SetAmount(amount); + dynFeeTxn.get_Data() = blkData; + dynFeeTxn.get_AccessList() = accessList; + + EXPECT_EQ(dynFeeTxn.GetChainID(), chainID); + EXPECT_EQ(dynFeeTxn.GetNonce(), nonce); + EXPECT_EQ(dynFeeTxn.GetMaxPriorFeePerGas(), maxPriorFeePerGas); + EXPECT_EQ(dynFeeTxn.GetMaxFeePerGas(), maxFeePerGas); + EXPECT_EQ(dynFeeTxn.GetGasLimit(), gasLimit); + EXPECT_EQ(dynFeeTxn.get_Destination(), destination); + EXPECT_EQ(dynFeeTxn.GetAmount(), amount); + EXPECT_EQ(dynFeeTxn.get_Data(), blkData); + EXPECT_EQ(dynFeeTxn.get_AccessList(), accessList); + + std::vector expRlp = { + // f86f82076c2284773594008477359400830186a09409616c3d61b3331fc4109a + 0xf8U, 0x6fU, 0x82U, 0x07U, 0x6cU, 0x22U, 0x84U, 0x77U, + 0x35U, 0x94U, 0x00U, 0x84U, 0x77U, 0x35U, 0x94U, 0x00U, + 0x83U, 0x01U, 0x86U, 0xa0U, 0x94U, 0x09U, 0x61U, 0x6cU, + 0x3dU, 0x61U, 0xb3U, 0x33U, 0x1fU, 0xc4U, 0x10U, 0x9aU, + // 9e41a8bdb7d9776609865af3107a400086616263646566f838f7940000000000 + 0x9eU, 0x41U, 0xa8U, 0xbdU, 0xb7U, 0xd9U, 0x77U, 0x66U, + 0x09U, 0x86U, 0x5aU, 0xf3U, 0x10U, 0x7aU, 0x40U, 0x00U, + 0x86U, 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U, 0xf8U, + 0x38U, 0xf7U, 0x94U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // 000000000000000000000000000001e1a0010000000000000000000000000000 + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0xe1U, + 0xa0U, 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // 0000000000000000000000000000000000 + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, + }; + std::cout << "Expected RLP: " << + SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; + + auto rlp = dynFeeTxn.RlpSerialize(); + std::cout << "Generated RLP: " << + SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; + + EXPECT_EQ(rlp, expRlp); + + auto rlpDecoded = Transaction::DynFee::FromRlp(rlp); + + EXPECT_EQ(rlpDecoded.GetChainID(), chainID); + EXPECT_EQ(rlpDecoded.GetNonce(), nonce); + EXPECT_EQ(rlpDecoded.GetMaxPriorFeePerGas(), maxPriorFeePerGas); + EXPECT_EQ(rlpDecoded.GetMaxFeePerGas(), maxFeePerGas); + EXPECT_EQ(rlpDecoded.GetGasLimit(), gasLimit); + EXPECT_EQ(rlpDecoded.get_Destination(), destination); + EXPECT_EQ(rlpDecoded.GetAmount(), amount); + EXPECT_EQ(rlpDecoded.get_Data(), blkData); + EXPECT_EQ(rlpDecoded.get_AccessList(), accessList); + + std::array expHash = { + // d385a3379c2fbd2ccdda2cb84fa1202cfd0635ba0e422a1921ccf8361b24465c + 0xd3U, 0x85U, 0xa3U, 0x37U, 0x9cU, 0x2fU, 0xbdU, 0x2cU, + 0xcdU, 0xdaU, 0x2cU, 0xb8U, 0x4fU, 0xa1U, 0x20U, 0x2cU, + 0xfdU, 0x06U, 0x35U, 0xbaU, 0x0eU, 0x42U, 0x2aU, 0x19U, + 0x21U, 0xccU, 0xf8U, 0x36U, 0x1bU, 0x24U, 0x46U, 0x5cU, + }; + auto hash = dynFeeTxn.Hash(); + EXPECT_EQ(hash, rlpDecoded.Hash()); + EXPECT_EQ(hash, expHash); + } +} + From bbac91bd0b9cf001796106ace71d6ca240f37c8d Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Fri, 10 May 2024 17:13:39 -0700 Subject: [PATCH 02/22] Simplified eth txn access list obj and validators --- .../Eth/Transaction/AccessListObj.hpp | 194 ++++++++ .../Eth/Transaction/DynamicFee.hpp | 100 +--- test/src/TestEthTxnDynFee.cpp | 427 +++++++++++------- 3 files changed, 474 insertions(+), 247 deletions(-) create mode 100644 include/EclipseMonitor/Eth/Transaction/AccessListObj.hpp diff --git a/include/EclipseMonitor/Eth/Transaction/AccessListObj.hpp b/include/EclipseMonitor/Eth/Transaction/AccessListObj.hpp new file mode 100644 index 0000000..22f1e17 --- /dev/null +++ b/include/EclipseMonitor/Eth/Transaction/AccessListObj.hpp @@ -0,0 +1,194 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#include + +#include "../../Exceptions.hpp" +#include "../../Internal/SimpleObj.hpp" +#include "../../Internal/SimpleRlp.hpp" + + +namespace EclipseMonitor +{ +namespace Eth +{ +namespace Transaction +{ + + +using StorageKeys = Internal::Obj::ListT; + + +/** + * @brief Validate the given bytes is a smart contract (or wallet) address. + * It mainly checks the length of the given bytes is 20. + * + * @param addr The address bytes to validate. + */ +inline void ValidateContractAddr( + const Internal::Obj::BytesBaseObj& addr +) +{ + if (addr.size() != 20) + { + throw Exception("Invalid access list address size"); + } +} + + +/** + * @brief Validate the given storage keys list that comes from a access list. + * It mainly checks the length of the given by is 32. + * + * @param storageKey The storage key bytes to validate. + */ +inline void ValidateAccessListStorageKey( + const Internal::Obj::BytesBaseObj& storageKey +) +{ + if (storageKey.size() != 32) + { + throw Exception("Invalid access list storage key size"); + } +} + + +inline void ValidateAccessListStorageKeys(const StorageKeys& storageKeys) +{ + for (const auto& storageKey : storageKeys) + { + ValidateAccessListStorageKey(storageKey); + } +} + + +/** + * @brief The access list object should contain two elements: + * 1. address: bytes + * 2. storageKeys : list[bytes] + * reference: https://github.com/ethereum/eth-account/blob/8478a86a8d235acba0a33fcae5804887473c72de/eth_account/account.py#L666 + * + */ +using AccessListItemTupleCore = std::tuple< + // 01. + std::pair, + Internal::Obj::Bytes>, + // 02. + std::pair, + StorageKeys> +>; + + +using StorageKeyParser = Internal::Rlp::ListParserT< + Internal::Rlp::BytesParser, + Internal::Rlp::FailingParser< + Internal::Rlp::InputContainerType, + Internal::Rlp::ByteValType, + Internal::Rlp::RlpEncTypeCat::List, + Internal::Rlp::BytesObjType + >, + StorageKeys +>; + + +using AccessListItemParserTupleCore = std::tuple< + // 01. + std::pair, + Internal::Rlp::BytesParser>, + // 02. + std::pair, + StorageKeyParser> +>; + + +class AccessListItem : + public Internal::Obj::StaticDict +{ +public: // static members: + + using Self = AccessListItem; + using Base = Internal::Obj::StaticDict; + +public: + + using Base::Base; + + void Validate() const + { + // Validate the address + ValidateContractAddr(get_Address()); + + // validate the storage keys + ValidateAccessListStorageKeys(get_StorageKeys()); + } + + // 01. Address + typename Base::template GetRef > + get_Address() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_Address() const + { + return Base::template get >(); + } + + // 02. StorageKeys + typename Base::template GetRef > + get_StorageKeys() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_StorageKeys() const + { + return Base::template get >(); + } + +}; // class AccessListItem + + +using AccessListItemParser = Internal::Rlp::StaticDictParserT< + AccessListItemParserTupleCore, + false, + false, + AccessListItem +>; + + +using AccessListObj = Internal::Obj::ListT; + + +using AccessListObjParser = Internal::Rlp::ListParserT< + Internal::Rlp::FailingParser< + Internal::Rlp::InputContainerType, + Internal::Rlp::ByteValType, + Internal::Rlp::RlpEncTypeCat::Bytes, + AccessListItem + >, + AccessListItemParser, + AccessListObj +>; + + +inline void ValidateAccessListObj(const AccessListObj& accessList) +{ + for (const auto& accessListItem : accessList) + { + accessListItem.Validate(); + } +} + + +} // namespace Transaction +} // namespace Eth +} // namespace EclipseMonitor + diff --git a/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp b/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp index 2bad5cc..db5ba58 100644 --- a/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp +++ b/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp @@ -15,6 +15,8 @@ #include "../DataTypes.hpp" #include "../Keccak256.hpp" +#include "AccessListObj.hpp" + namespace EclipseMonitor { @@ -24,94 +26,6 @@ namespace Transaction { -using AccessListObj = Internal::Obj::List; - - - -inline void ValidateContractAddr( - const Internal::Obj::BytesBaseObj& addr -) -{ - if (addr.size() != 20) - { - throw Exception("Invalid access list address size"); - } -} - - -inline void ValidateAccessListStorageKey( - const Internal::Obj::BytesBaseObj& storageKey -) -{ - if (storageKey.size() != 32) - { - throw Exception("Invalid access list storage key size"); - } -} - - -inline void ValidateAccessListStorageKeys( - const Internal::Obj::ListBaseObj& storageKeys -) -{ - for (const auto& storageKey : storageKeys) - { - if (storageKey.GetCategory() != Internal::Obj::ObjCategory::Bytes) - { - throw Exception("Invalid access list storage key type"); - } - ValidateAccessListStorageKey(storageKey.AsBytes()); - } -} - - -/** - * @brief validate the tuples in the access list object; each tuple should - * contain two elements: address(bytes) and storageKeys(list[bytes]) - * reference: https://github.com/ethereum/eth-account/blob/8478a86a8d235acba0a33fcae5804887473c72de/eth_account/account.py#L666 - * - * @param accessTuple the tuple to be validated - */ -inline void ValidateAccessListTuple(const Internal::Obj::ListBaseObj& accessTuple) -{ - if (accessTuple.size() != 2) - { - throw Exception("Invalid access list tuple size"); - } - - if (accessTuple[0].GetCategory() != Internal::Obj::ObjCategory::Bytes) - { - throw Exception("Invalid access list address type"); - } - ValidateContractAddr(accessTuple[0].AsBytes()); - - if (accessTuple[1].GetCategory() != Internal::Obj::ObjCategory::List) - { - throw Exception("Invalid access list storage keys type"); - } - ValidateAccessListStorageKeys(accessTuple[1].AsList()); -} - - -/** - * @brief validate the access list object, which supposed to be a list of - * tuple[bytes, list[bytes]] - * - * @param accessList the access list object to be validated - */ -inline void ValidateAccessList(const AccessListObj& accessList) -{ - for (const auto& tuple : accessList) - { - if (tuple.GetCategory() != Internal::Obj::ObjCategory::List) - { - throw Exception("Invalid access list tuple type"); - } - ValidateAccessListTuple(tuple.AsList()); - } -} - - /** * @brief fields in a dynamic fee transaction object * which are: chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, @@ -177,7 +91,7 @@ using DynFeeParserTupleCore = std::tuple< Internal::Rlp::BytesParser>, // 09. std::pair, - Internal::Rlp::ListParser> + AccessListObjParser> >; @@ -195,14 +109,18 @@ class DynFee : using Base::Base; - Internal::Rlp::OutputContainerType RlpSerialize() const + void Validate() const { // validate the access list object - ValidateAccessList(get_AccessList()); + ValidateAccessListObj(get_AccessList()); // validate the destination address ValidateContractAddr(get_Destination()); + } + Internal::Rlp::OutputContainerType RlpSerialize() const + { + Validate(); return Internal::Rlp::WriterGeneric::Write(*this); } diff --git a/test/src/TestEthTxnDynFee.cpp b/test/src/TestEthTxnDynFee.cpp index 899006a..eb8d1a4 100644 --- a/test/src/TestEthTxnDynFee.cpp +++ b/test/src/TestEthTxnDynFee.cpp @@ -31,168 +31,127 @@ GTEST_TEST(TestEthTxnDynFee, CountTestFile) } -GTEST_TEST(TestEthTxnDynFee, ValidateAccessList) +static Transaction::AccessListObj GetSampleAcList_1Addr_1Key() { - { - Transaction::AccessListObj accessListEmpty; - EXPECT_NO_THROW(Transaction::ValidateAccessList(accessListEmpty)); - } + Transaction::AccessListItem accessListItem; + accessListItem.get_Address() = SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }); + accessListItem.get_StorageKeys().push_back(SimpleObjects::Bytes({ + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + })); + Transaction::AccessListObj accessList = { + accessListItem + }; + return accessList; +} - { - Transaction::AccessListObj accessListEmptyStKeys = { - SimpleObjects::List({ - // address - SimpleObjects::Bytes({ - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x01U, - }), - // storage keys - SimpleObjects::List() - }) - }; - EXPECT_NO_THROW(Transaction::ValidateAccessList(accessListEmptyStKeys)); - } - { - Transaction::AccessListObj accessList = { - SimpleObjects::List({ - // address - SimpleObjects::Bytes({ - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x01U, - }), - // storage keys - SimpleObjects::List({ - SimpleObjects::Bytes({ - 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - }) - }) - }) - }; - EXPECT_NO_THROW(Transaction::ValidateAccessList(accessList)); - } +static Transaction::AccessListObj GetSampleAcList_1Addr_0Key() +{ + Transaction::AccessListItem accessListItem; + accessListItem.get_Address() = SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }); + Transaction::AccessListObj accessList = { + accessListItem + }; + return accessList; +} + + +static Transaction::AccessListObj GetSampleAcList_0Addr_0Key() +{ + Transaction::AccessListObj accessList; + return accessList; +} - { - Transaction::AccessListObj accessListInvalidAddr = { - SimpleObjects::List({ - // address - SimpleObjects::String("0000000000000000000000000000000000000001"), - // storage keys - SimpleObjects::List() - }) - }; - EXPECT_THROW_MSG( - Transaction::ValidateAccessList(accessListInvalidAddr), - EclipseMonitor::Exception, - "Invalid access list address type" - ); - } +static Transaction::AccessListObj GetSampleAcList_1LongAddr() +{ + Transaction::AccessListItem accessListItem; + accessListItem.get_Address() = SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, + }); + Transaction::AccessListObj accessList = { + accessListItem + }; + return accessList; +} + + +static Transaction::AccessListObj GetSampleAcList_1Addr_1LongKey() +{ + Transaction::AccessListItem accessListItem; + accessListItem.get_Address() = SimpleObjects::Bytes({ + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x01U, + }); + accessListItem.get_StorageKeys().push_back(SimpleObjects::Bytes({ + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + })); + Transaction::AccessListObj accessList = { + accessListItem + }; + return accessList; +} + + +GTEST_TEST(TestEthTxnDynFee, ValidateAccessList) +{ { - Transaction::AccessListObj accessListInvalidAddr = { - SimpleObjects::List({ - // address - SimpleObjects::Bytes({ - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, - }), - // storage keys - SimpleObjects::List() - }) - }; - EXPECT_THROW_MSG( - Transaction::ValidateAccessList(accessListInvalidAddr), - EclipseMonitor::Exception, - "Invalid access list address size" + Transaction::AccessListObj accessListEmpty = + GetSampleAcList_0Addr_0Key(); + EXPECT_NO_THROW( + Transaction::ValidateAccessListObj(accessListEmpty) ); } { - Transaction::AccessListObj accessListInvalidTuple = { - SimpleObjects::List({ - // address - SimpleObjects::Bytes({ - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x01U, - }), - }) - }; - EXPECT_THROW_MSG( - Transaction::ValidateAccessList(accessListInvalidTuple), - EclipseMonitor::Exception, - "Invalid access list tuple size" + Transaction::AccessListObj accessListEmptyStKeys = + GetSampleAcList_1Addr_0Key(); + EXPECT_NO_THROW( + Transaction::ValidateAccessListObj(accessListEmptyStKeys) ); } { - Transaction::AccessListObj accessListInvalidKeys = { - SimpleObjects::List({ - // address - SimpleObjects::Bytes({ - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x01U, - }), - // storage keys - SimpleObjects::String("0100000000000000000000000000000000000000000000000000000000000000"), - }) - }; - EXPECT_THROW_MSG( - Transaction::ValidateAccessList(accessListInvalidKeys), - EclipseMonitor::Exception, - "Invalid access list storage keys type" + Transaction::AccessListObj accessListEmptyStKeys = + GetSampleAcList_1Addr_1Key(); + EXPECT_NO_THROW( + Transaction::ValidateAccessListObj(accessListEmptyStKeys) ); } { - Transaction::AccessListObj accessListInvalidKey = { - SimpleObjects::List({ - // address - SimpleObjects::Bytes({ - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x01U, - }), - // storage keys - SimpleObjects::List({ - SimpleObjects::String("0100000000000000000000000000000000000000000000000000000000000000"), - }) - }) - }; + Transaction::AccessListObj accessListInvalidAddr = + GetSampleAcList_1LongAddr(); EXPECT_THROW_MSG( - Transaction::ValidateAccessList(accessListInvalidKey), + Transaction::ValidateAccessListObj(accessListInvalidAddr), EclipseMonitor::Exception, - "Invalid access list storage key type" + "Invalid access list address size" ); } { - Transaction::AccessListObj accessListInvalidKey = { - SimpleObjects::List({ - // address - SimpleObjects::Bytes({ - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x01U, - }), - // storage keys - SimpleObjects::List({ - SimpleObjects::Bytes({ - 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - }) - }) - }) - }; + Transaction::AccessListObj accessListInvalidKey = + GetSampleAcList_1Addr_1LongKey(); EXPECT_THROW_MSG( - Transaction::ValidateAccessList(accessListInvalidKey), + Transaction::ValidateAccessListObj(accessListInvalidKey), EclipseMonitor::Exception, "Invalid access list storage key size" ); @@ -219,25 +178,7 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) // 0x616263646566 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U, }; - Transaction::AccessListObj accessList = { - SimpleObjects::List({ - // address - SimpleObjects::Bytes({ - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x01U, - }), - // storage keys - SimpleObjects::List({ - SimpleObjects::Bytes({ - 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, - }) - }) - }) - }; + Transaction::AccessListObj accessList = GetSampleAcList_1Addr_1Key(); Transaction::DynFee dynFeeTxn; dynFeeTxn.SetChainID(chainID); @@ -313,5 +254,179 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) EXPECT_EQ(hash, rlpDecoded.Hash()); EXPECT_EQ(hash, expHash); } + + + { + uint64_t chainID = 634ULL; + uint64_t nonce = 814370ULL; + uint64_t maxPriorFeePerGas = 98765432154321ULL; + uint64_t maxFeePerGas = 987654321ULL; + uint64_t gasLimit = 123456ULL; + SimpleObjects::Bytes destination = { + // 0x09616C3d61b3331fc4109a9E41a8BDB7d9776609 + 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, 0x33U, 0x1fU, + 0xc4U, 0x10U, 0x9aU, 0x9eU, 0x41U, 0xa8U, 0xbdU, 0xb7U, + 0xd9U, 0x77U, 0x66U, 0x09U, + }; + uint64_t amount = 28957ULL; + SimpleObjects::Bytes blkData = { + // 0x9879ab123d274ef5 + 0x98U, 0x79U, 0xabU, 0x12U, 0x3dU, 0x27U, 0x4eU, 0xf5U, + }; + Transaction::AccessListObj accessList = GetSampleAcList_1Addr_0Key(); + + Transaction::DynFee dynFeeTxn; + dynFeeTxn.SetChainID(chainID); + dynFeeTxn.SetNonce(nonce); + dynFeeTxn.SetMaxPriorFeePerGas(maxPriorFeePerGas); + dynFeeTxn.SetMaxFeePerGas(maxFeePerGas); + dynFeeTxn.SetGasLimit(gasLimit); + dynFeeTxn.get_Destination() = destination; + dynFeeTxn.SetAmount(amount); + dynFeeTxn.get_Data() = blkData; + dynFeeTxn.get_AccessList() = accessList; + + EXPECT_EQ(dynFeeTxn.GetChainID(), chainID); + EXPECT_EQ(dynFeeTxn.GetNonce(), nonce); + EXPECT_EQ(dynFeeTxn.GetMaxPriorFeePerGas(), maxPriorFeePerGas); + EXPECT_EQ(dynFeeTxn.GetMaxFeePerGas(), maxFeePerGas); + EXPECT_EQ(dynFeeTxn.GetGasLimit(), gasLimit); + EXPECT_EQ(dynFeeTxn.get_Destination(), destination); + EXPECT_EQ(dynFeeTxn.GetAmount(), amount); + EXPECT_EQ(dynFeeTxn.get_Data(), blkData); + EXPECT_EQ(dynFeeTxn.get_AccessList(), accessList); + + std::vector expRlp = { + // f85082027a830c6d228659d39e7fe8d1843ade68b18301e2409409616c3d61b3 + 0xf8U, 0x50U, 0x82U, 0x02U, 0x7aU, 0x83U, 0x0cU, 0x6dU, + 0x22U, 0x86U, 0x59U, 0xd3U, 0x9eU, 0x7fU, 0xe8U, 0xd1U, + 0x84U, 0x3aU, 0xdeU, 0x68U, 0xb1U, 0x83U, 0x01U, 0xe2U, + 0x40U, 0x94U, 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, + // 331fc4109a9e41a8bdb7d977660982711d889879ab123d274ef5d7d694000000 + 0x33U, 0x1fU, 0xc4U, 0x10U, 0x9aU, 0x9eU, 0x41U, 0xa8U, + 0xbdU, 0xb7U, 0xd9U, 0x77U, 0x66U, 0x09U, 0x82U, 0x71U, + 0x1dU, 0x88U, 0x98U, 0x79U, 0xabU, 0x12U, 0x3dU, 0x27U, + 0x4eU, 0xf5U, 0xd7U, 0xd6U, 0x94U, 0x00U, 0x00U, 0x00U, + // 0000000000000000000000000000000001c0 + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x01U, 0xc0U, + }; + std::cout << "Expected RLP: " << + SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; + + auto rlp = dynFeeTxn.RlpSerialize(); + std::cout << "Generated RLP: " << + SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; + + EXPECT_EQ(rlp, expRlp); + + auto rlpDecoded = Transaction::DynFee::FromRlp(rlp); + + EXPECT_EQ(rlpDecoded.GetChainID(), chainID); + EXPECT_EQ(rlpDecoded.GetNonce(), nonce); + EXPECT_EQ(rlpDecoded.GetMaxPriorFeePerGas(), maxPriorFeePerGas); + EXPECT_EQ(rlpDecoded.GetMaxFeePerGas(), maxFeePerGas); + EXPECT_EQ(rlpDecoded.GetGasLimit(), gasLimit); + EXPECT_EQ(rlpDecoded.get_Destination(), destination); + EXPECT_EQ(rlpDecoded.GetAmount(), amount); + EXPECT_EQ(rlpDecoded.get_Data(), blkData); + EXPECT_EQ(rlpDecoded.get_AccessList(), accessList); + + std::array expHash = { + // ac8785206b5b48c55ec9ccd796282dad8d8669cb91dde26e4ce424ccbdcbd0e0 + 0xacU, 0x87U, 0x85U, 0x20U, 0x6bU, 0x5bU, 0x48U, 0xc5U, + 0x5eU, 0xc9U, 0xccU, 0xd7U, 0x96U, 0x28U, 0x2dU, 0xadU, + 0x8dU, 0x86U, 0x69U, 0xcbU, 0x91U, 0xddU, 0xe2U, 0x6eU, + 0x4cU, 0xe4U, 0x24U, 0xccU, 0xbdU, 0xcbU, 0xd0U, 0xe0U, + }; + auto hash = dynFeeTxn.Hash(); + EXPECT_EQ(hash, rlpDecoded.Hash()); + EXPECT_EQ(hash, expHash); + } + + + { + uint64_t chainID = 3948ULL; + uint64_t nonce = 25169ULL; + uint64_t maxPriorFeePerGas = 2956132109347ULL; + uint64_t maxFeePerGas = 14067925928ULL; + uint64_t gasLimit = 2563498ULL; + SimpleObjects::Bytes destination = { + // 0x09616C3d61b3331fc4109a9E41a8BDB7d9776609 + 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, 0x33U, 0x1fU, + 0xc4U, 0x10U, 0x9aU, 0x9eU, 0x41U, 0xa8U, 0xbdU, 0xb7U, + 0xd9U, 0x77U, 0x66U, 0x09U, + }; + uint64_t amount = 1532891978124ULL; + SimpleObjects::Bytes blkData = { + // 0x5789a7b3fe8d + 0x57U, 0x89U, 0xa7U, 0xb3U, 0xfeU, 0x8dU, + }; + + Transaction::DynFee dynFeeTxn; + dynFeeTxn.SetChainID(chainID); + dynFeeTxn.SetNonce(nonce); + dynFeeTxn.SetMaxPriorFeePerGas(maxPriorFeePerGas); + dynFeeTxn.SetMaxFeePerGas(maxFeePerGas); + dynFeeTxn.SetGasLimit(gasLimit); + dynFeeTxn.get_Destination() = destination; + dynFeeTxn.SetAmount(amount); + dynFeeTxn.get_Data() = blkData; + + EXPECT_EQ(dynFeeTxn.GetChainID(), chainID); + EXPECT_EQ(dynFeeTxn.GetNonce(), nonce); + EXPECT_EQ(dynFeeTxn.GetMaxPriorFeePerGas(), maxPriorFeePerGas); + EXPECT_EQ(dynFeeTxn.GetMaxFeePerGas(), maxFeePerGas); + EXPECT_EQ(dynFeeTxn.GetGasLimit(), gasLimit); + EXPECT_EQ(dynFeeTxn.get_Destination(), destination); + EXPECT_EQ(dynFeeTxn.GetAmount(), amount); + EXPECT_EQ(dynFeeTxn.get_Data(), blkData); + EXPECT_EQ(dynFeeTxn.get_AccessList(), GetSampleAcList_0Addr_0Key()); + + std::vector expRlp = { + // f83b820f6c8262518602b047344c238503468383a883271daa9409616c3d61b3 + 0xf8U, 0x3bU, 0x82U, 0x0fU, 0x6cU, 0x82U, 0x62U, 0x51U, + 0x86U, 0x02U, 0xb0U, 0x47U, 0x34U, 0x4cU, 0x23U, 0x85U, + 0x03U, 0x46U, 0x83U, 0x83U, 0xa8U, 0x83U, 0x27U, 0x1dU, + 0xaaU, 0x94U, 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, + // 331fc4109a9e41a8bdb7d9776609860164e77b598c865789a7b3fe8dc0 + 0x33U, 0x1fU, 0xc4U, 0x10U, 0x9aU, 0x9eU, 0x41U, 0xa8U, + 0xbdU, 0xb7U, 0xd9U, 0x77U, 0x66U, 0x09U, 0x86U, 0x01U, + 0x64U, 0xe7U, 0x7bU, 0x59U, 0x8cU, 0x86U, 0x57U, 0x89U, + 0xa7U, 0xb3U, 0xfeU, 0x8dU, 0xc0U, + }; + std::cout << "Expected RLP: " << + SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; + + auto rlp = dynFeeTxn.RlpSerialize(); + std::cout << "Generated RLP: " << + SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; + + EXPECT_EQ(rlp, expRlp); + + auto rlpDecoded = Transaction::DynFee::FromRlp(rlp); + + EXPECT_EQ(rlpDecoded.GetChainID(), chainID); + EXPECT_EQ(rlpDecoded.GetNonce(), nonce); + EXPECT_EQ(rlpDecoded.GetMaxPriorFeePerGas(), maxPriorFeePerGas); + EXPECT_EQ(rlpDecoded.GetMaxFeePerGas(), maxFeePerGas); + EXPECT_EQ(rlpDecoded.GetGasLimit(), gasLimit); + EXPECT_EQ(rlpDecoded.get_Destination(), destination); + EXPECT_EQ(rlpDecoded.GetAmount(), amount); + EXPECT_EQ(rlpDecoded.get_Data(), blkData); + EXPECT_EQ(rlpDecoded.get_AccessList(), GetSampleAcList_0Addr_0Key()); + + std::array expHash = { + // 26eaa1e0f350d89325edfd64b52dfd955fcdc56b85b157c50191d7abcf2f579a + 0x26U, 0xeaU, 0xa1U, 0xe0U, 0xf3U, 0x50U, 0xd8U, 0x93U, + 0x25U, 0xedU, 0xfdU, 0x64U, 0xb5U, 0x2dU, 0xfdU, 0x95U, + 0x5fU, 0xcdU, 0xc5U, 0x6bU, 0x85U, 0xb1U, 0x57U, 0xc5U, + 0x01U, 0x91U, 0xd7U, 0xabU, 0xcfU, 0x2fU, 0x57U, 0x9aU, + }; + auto hash = dynFeeTxn.Hash(); + EXPECT_EQ(hash, rlpDecoded.Hash()); + EXPECT_EQ(hash, expHash); + } } From 8a53ab2304c26eff6484d676a175a29a75bab902 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Wed, 15 May 2024 04:32:39 -0700 Subject: [PATCH 03/22] refactorred ABI Parser --- include/EclipseMonitor/Eth/AbiCommon.hpp | 373 ++++++++++++ include/EclipseMonitor/Eth/AbiParser.hpp | 717 ++++++++++------------- test/src/TestEthAbiParser.cpp | 10 +- 3 files changed, 693 insertions(+), 407 deletions(-) create mode 100644 include/EclipseMonitor/Eth/AbiCommon.hpp diff --git a/include/EclipseMonitor/Eth/AbiCommon.hpp b/include/EclipseMonitor/Eth/AbiCommon.hpp new file mode 100644 index 0000000..9d26548 --- /dev/null +++ b/include/EclipseMonitor/Eth/AbiCommon.hpp @@ -0,0 +1,373 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#include + +#include +#include + +#include "../Internal/SimpleObj.hpp" + +#include "AbiCommon.hpp" + + +namespace EclipseMonitor +{ +namespace Eth +{ + + +// ========== +// Constant values +// ========== + + +struct AbiParserConst +{ + static constexpr size_t sk_chunkSize() noexcept + { + return 32; + } +}; // struct AbiParserConst + + +// ========== +// Alias types for convenience +// ========== + + +using AbiUInt8 = + std::integral_constant< + Internal::Obj::RealNumType, + Internal::Obj::RealNumType::UInt8 + >; +using AbiUInt64 = + std::integral_constant< + Internal::Obj::RealNumType, + Internal::Obj::RealNumType::UInt64 + >; + +template +using AbiSize = std::integral_constant; + + +// ========== +// Utilities +// ========== + + +namespace EthInternal +{ + + +template< + Internal::Obj::RealNumType _RealNumType, + typename _PrimitiveType +> +struct RealNumTypeTraitsPrimitiveImpl +{ + using Primitive = _PrimitiveType; + + static constexpr size_t sk_consumedSize() noexcept + { + return sizeof(Primitive); + } +}; // struct RealNumTypeTraitsPrimitiveImpl + + +template +inline constexpr _T AbiCeilingDiv(_T a, _T b) noexcept +{ + return (a + (b - 1)) / b; +} + + +template +struct RealNumTypeTraits; + + +template<> +struct RealNumTypeTraits : + public RealNumTypeTraitsPrimitiveImpl< + Internal::Obj::RealNumType::UInt8, + uint8_t + > +{}; // struct RealNumTypeTraits + + +template<> +struct RealNumTypeTraits : + public RealNumTypeTraitsPrimitiveImpl< + Internal::Obj::RealNumType::UInt16, + uint16_t + > +{}; // struct RealNumTypeTraits + + +template<> +struct RealNumTypeTraits : + public RealNumTypeTraitsPrimitiveImpl< + Internal::Obj::RealNumType::UInt32, + uint32_t + > +{}; // struct RealNumTypeTraits + + +template<> +struct RealNumTypeTraits : + public RealNumTypeTraitsPrimitiveImpl< + Internal::Obj::RealNumType::UInt64, + uint64_t + > +{}; // struct RealNumTypeTraits + + +} // namespace EthInternal + + +// ========== +// Codec Basis +// ========== + + +namespace EthInternal +{ + + +template< + Internal::Obj::ObjCategory _DataType, + typename... _Args +> +struct AbiCodecImpl; + + +// ========== +// AbiCodecImpl for integer types +// ========== + + +template +struct AbiCodecImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant +> +{ + using RealNumTraits = RealNumTypeTraits<_RealNumType>; + + static constexpr size_t sk_consumedSize = RealNumTraits::sk_consumedSize(); + + static_assert( + sk_consumedSize <= AbiParserConst::sk_chunkSize(), + "ABI parser - integer type is too large" + ); + + static constexpr size_t sk_leadPadSize = + AbiParserConst::sk_chunkSize() - sk_consumedSize; + + using Primitive = typename RealNumTraits::Primitive; + + using IsDynamic = std::false_type; + static constexpr bool sk_isDynamic = IsDynamic::value; + +}; // struct AbiCodecImpl + + +// ========== +// AbiCodecImpl for bool types +// ========== + + +template<> +struct AbiCodecImpl< + Internal::Obj::ObjCategory::Bool +> : + public AbiCodecImpl< + Internal::Obj::ObjCategory::Integer, + AbiUInt8 + > +{ + using Base = AbiCodecImpl< + Internal::Obj::ObjCategory::Integer, + AbiUInt8 + >; + using Self = AbiCodecImpl; + + static constexpr size_t sk_consumedSize = Base::sk_consumedSize; + + static constexpr size_t sk_leadPadSize = Base::sk_leadPadSize; + + using Primitive = bool; + + using IsDynamic = std::false_type; + static constexpr bool sk_isDynamic = IsDynamic::value; + +}; // struct AbiCodecImpl + + +// ========== +// AbiCodecImpl for bytes types +// ========== + + +template<> +struct AbiCodecImpl< + Internal::Obj::ObjCategory::Bytes, + std::false_type // IsDynamic? - false +> +{ + + using Primitive = std::vector; + + using IsDynamic = std::false_type; + static constexpr bool sk_isDynamic = IsDynamic::value; + +}; // struct AbiCodecImpl + + +// ========== +// AbiCodecImpl for bytes types +// ========== + + +template<> +struct AbiCodecImpl< + Internal::Obj::ObjCategory::Bytes, + std::true_type // IsDynamic? - true +> +{ + + using Primitive = std::vector; + + using IsDynamic = std::true_type; + static constexpr bool sk_isDynamic = IsDynamic::value; + +}; // struct AbiCodecImpl + + +// ========== +// AbiCodecImpl for T[k] types, where T is static type +// ========== + + +template +struct AbiCodecImpl< + Internal::Obj::ObjCategory::List, + _ItemCodec, + std::false_type, // IsLenDynamic? - false + std::false_type // IsItemDynamic? - false +> +{ + using ItemCodec = _ItemCodec; + using ItemPrimitive = typename ItemCodec::Primitive; + using Primitive = std::vector; + + static_assert( + !ItemCodec::sk_isDynamic, + "ABI parser - The internal item should be a static type" + ); + + using IsDynamic = std::false_type; + static constexpr bool sk_isDynamic = IsDynamic::value; + +}; // struct AbiCodecImpl + + +// ========== +// AbiCodecImpl for T[k] types, where T is dynamic type +// ========== + + +template +struct AbiCodecImpl< + Internal::Obj::ObjCategory::List, + _ItemCodec, + std::false_type, // IsLenDynamic? - false + std::true_type // IsItemDynamic? - true +> +{ + using ItemCodec = _ItemCodec; + using ItemPrimitive = typename ItemCodec::Primitive; + using Primitive = std::vector; + + static_assert( + ItemCodec::sk_isDynamic, + "ABI parser - The internal item should be a dynamic type" + ); + + using IsDynamic = std::true_type; + static constexpr bool sk_isDynamic = IsDynamic::value; + +}; // struct AbiCodecImpl + + +// ========== +// AbiCodecImpl for T[] types, where T is static type +// ========== + + +/** + * @brief The codec implementation for a list with dynamic length. + * NOTE: T[] is a dynamic type, no matter what T is + * + * @tparam _ItemCodec The codec for the internal item + * @tparam _IsItemDyn The dynamic-property of the internal item + */ +template +struct AbiCodecDynLenListImpl +{ + using ItemCodec = _ItemCodec; + using ItemPrimitive = typename ItemCodec::Primitive; + using Primitive = std::vector; + + static_assert( + ( + (_IsItemDyn::value == ItemCodec::sk_isDynamic) && + std::is_same<_IsItemDyn, typename ItemCodec::IsDynamic>::value + ), + "ABI parser - The dynamic-property of the internal item doesn't match" + ); + + using IsDynamic = std::true_type; + static constexpr bool sk_isDynamic = IsDynamic::value; + +}; // struct AbiCodecDynLenListImpl + + +template +struct AbiCodecImpl< + Internal::Obj::ObjCategory::List, + _ItemCodec, + std::true_type, // IsLenDynamic? - true + std::false_type // IsItemDynamic? - false +> : + public AbiCodecDynLenListImpl<_ItemCodec, std::false_type> +{}; // struct AbiCodecImpl + + +// ========== +// AbiCodecImpl for T[] types, where T is dynamic type +// ========== + + +template +struct AbiCodecImpl< + Internal::Obj::ObjCategory::List, + _ItemCodec, + std::true_type, // IsLenDynamic? - true + std::true_type // IsItemDynamic? - true +> : + public AbiCodecDynLenListImpl<_ItemCodec, std::true_type> +{}; // struct AbiCodecImpl + + +} // namespace EthInternal + + +} // namespace Eth +} // namespace EclipseMonitor + diff --git a/include/EclipseMonitor/Eth/AbiParser.hpp b/include/EclipseMonitor/Eth/AbiParser.hpp index fc89b79..be87c10 100644 --- a/include/EclipseMonitor/Eth/AbiParser.hpp +++ b/include/EclipseMonitor/Eth/AbiParser.hpp @@ -19,6 +19,8 @@ #include "../Internal/SimpleRlp.hpp" #include "../Exceptions.hpp" +#include "AbiCommon.hpp" + namespace EclipseMonitor { @@ -26,18 +28,6 @@ namespace Eth { -// ========== -// Constant values -// ========== - - -struct AbiParserConst -{ - static constexpr size_t sk_chunkSize() noexcept - { - return 32; - } -}; // struct AbiParserConst // ========== @@ -48,67 +38,6 @@ struct AbiParserConst namespace EthInternal { -template< - Internal::Obj::RealNumType _RealNumType, - typename _PrimitiveType -> -struct RealNumTypeTraitsPrimitiveImpl -{ - using Primitive = _PrimitiveType; - - static constexpr size_t sk_consumedSize() noexcept - { - return sizeof(Primitive); - } -}; // struct RealNumTypeTraitsPrimitiveImpl - - -template -inline constexpr _T AbiCeilingDiv(_T a, _T b) noexcept -{ - return (a + (b - 1)) / b; -} - - -template -struct RealNumTypeTraits; - - -template<> -struct RealNumTypeTraits : - public RealNumTypeTraitsPrimitiveImpl< - Internal::Obj::RealNumType::UInt8, - uint8_t - > -{}; // struct RealNumTypeTraits - - -template<> -struct RealNumTypeTraits : - public RealNumTypeTraitsPrimitiveImpl< - Internal::Obj::RealNumType::UInt16, - uint16_t - > -{}; // struct RealNumTypeTraits - - -template<> -struct RealNumTypeTraits : - public RealNumTypeTraitsPrimitiveImpl< - Internal::Obj::RealNumType::UInt32, - uint32_t - > -{}; // struct RealNumTypeTraits - - -template<> -struct RealNumTypeTraits : - public RealNumTypeTraitsPrimitiveImpl< - Internal::Obj::RealNumType::UInt64, - uint64_t - > -{}; // struct RealNumTypeTraits - template inline _It AbiParserSkipPadding(size_t skipLen, _It begin, _It end) @@ -189,33 +118,34 @@ template< Internal::Obj::ObjCategory _DataType, typename... _Args > -struct AbiCodecImpl; +struct AbiParserImpl; // ========== -// AbiCodecImpl for integer types +// AbiParserImpl for integer types // ========== template -struct AbiCodecImpl< +struct AbiParserImpl< Internal::Obj::ObjCategory::Integer, std::integral_constant -> +> : + public AbiCodecImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + > { - using RealNumTraits = RealNumTypeTraits<_RealNumType>; - static constexpr size_t sk_consumedSize = RealNumTraits::sk_consumedSize(); - static_assert( - sk_consumedSize <= AbiParserConst::sk_chunkSize(), - "ABI parser - integer type is too large" - ); - static constexpr size_t sk_skipLeadSize = - AbiParserConst::sk_chunkSize() - sk_consumedSize; - using Primitive = typename RealNumTraits::Primitive; - - - AbiCodecImpl() = default; - ~AbiCodecImpl() = default; + using Base = AbiCodecImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + >; + using Self = AbiParserImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + >; + using Codec = Base; + using Primitive = typename Base::Primitive; template @@ -228,7 +158,7 @@ struct AbiCodecImpl< { // Skip the leading zero bytes that are larger than the target type begin = AbiParserSkipPadding( - sk_skipLeadSize, + Base::sk_leadPadSize, begin, end ); @@ -243,36 +173,36 @@ struct AbiCodecImpl< Primitive res = Internal::Rlp::ParsePrimitiveIntValue< Primitive, Internal::Rlp::Endian::native - >::Parse(sk_consumedSize, inFunc); + >::Parse(Base::sk_consumedSize, inFunc); return std::make_tuple(res, begin, 1); } -}; // struct AbiCodecImpl +}; // struct AbiParserImpl // ========== -// AbiCodecImpl for bool types +// AbiParserImpl for bool types // ========== template<> -struct AbiCodecImpl< +struct AbiParserImpl< Internal::Obj::ObjCategory::Bool -> +> : public AbiCodecImpl { - using IntParser = AbiCodecImpl< + using Base = AbiCodecImpl; + using Self = AbiParserImpl; + using Codec = Base; + using Primitive = typename Base::Primitive; + + using IntParser = AbiParserImpl< Internal::Obj::ObjCategory::Integer, - std::integral_constant< - Internal::Obj::RealNumType, - Internal::Obj::RealNumType::UInt8 - > + AbiUInt8 >; - using Primitive = bool; - - - AbiCodecImpl() = default; - ~AbiCodecImpl() = default; - + static_assert( + std::is_same::value, + "ABI parser - bool parser must have the same base as uint8 parser" + ); template std::tuple< @@ -294,45 +224,57 @@ struct AbiCodecImpl< return std::make_tuple(valBool, begin, chunkConsumed); } -}; // struct AbiCodecImpl +}; // struct AbiParserImpl // ========== -// AbiCodecImpl for bytes types +// AbiParserImpl for bytes types // ========== template<> -struct AbiCodecImpl< +struct AbiParserImpl< Internal::Obj::ObjCategory::Bytes, std::false_type /* IsDynamic? - false */ -> +> : public AbiCodecImpl { - using Primitive = std::vector; - + using Base = + AbiCodecImpl; + using Self = + AbiParserImpl; + using Codec = Base; + using Primitive = typename Base::Primitive; - // NOTE: This constructor does not check the size value, since this is an - // internal type and we assume the size is checked before calling this - AbiCodecImpl(size_t size) : - m_size(size) + /** + * @brief Construct a new Abi Parser Impl object + * NOTE: This constructor does not check the size value, + * since this is an internal type and we assume the size is checked + * before calling this + * + * @param size The size of the bytes, which should be within the range of + * (0, 32] + */ + AbiParserImpl(size_t size) : + m_size(size), + m_padSize(AbiParserConst::sk_chunkSize() - m_size) {} - ~AbiCodecImpl() = default; + ~AbiParserImpl() = default; template std::tuple< - Primitive, /* Parsed value */ - _ItType, /* Iterator of where the parsing stopped */ - size_t /* Number of chunks consumed */ + Primitive, /* Parsed value */ + _ItType, /* Iterator of where the parsing stopped */ + size_t /* Number of chunks consumed */ > ToPrimitive(_ItType begin, _ItType end) const { - std::vector res; + Primitive res; res.reserve(m_size); begin = EthInternal::AbiParserCopyBytesThenSkip( m_size, - AbiParserConst::sk_chunkSize() - m_size, + m_padSize, begin, end, std::back_inserter(res) @@ -343,34 +285,32 @@ struct AbiCodecImpl< private: - size_t m_size = 0; + size_t m_size; + size_t m_padSize; -}; // struct AbiCodecImpl +}; // struct AbiParserImpl // ========== -// AbiCodecImpl for bytes types +// AbiParserImpl for bytes types // ========== template<> -struct AbiCodecImpl< +struct AbiParserImpl< Internal::Obj::ObjCategory::Bytes, std::true_type /* IsDynamic? - true */ -> +> : public AbiCodecImpl { - using Primitive = std::vector; - using DynLenParser = AbiCodecImpl< - Internal::Obj::ObjCategory::Integer, - std::integral_constant< - Internal::Obj::RealNumType, - Internal::Obj::RealNumType::UInt64 - > - >; - - AbiCodecImpl() = default; - ~AbiCodecImpl() = default; + using Base = + AbiCodecImpl; + using Self = + AbiParserImpl; + using Codec = Base; + using Primitive = typename Base::Primitive; + using DynLenParser = + AbiParserImpl; template std::tuple< @@ -393,7 +333,7 @@ struct AbiCodecImpl< size_t paddingSize = (numChunk * AbiParserConst::sk_chunkSize()) - len; - std::vector res; + Primitive res; res.reserve(len); begin = EthInternal::AbiParserCopyBytesThenSkip( len, @@ -409,38 +349,51 @@ struct AbiCodecImpl< return std::make_tuple(res, begin, chunkConsumed); } -}; // struct AbiCodecImpl +}; // struct AbiParserImpl // ========== -// AbiCodecImpl for T[k] types, where T is static type +// AbiParserImpl for T[k] types, where T is static type // ========== template -struct AbiCodecImpl< +struct AbiParserImpl< Internal::Obj::ObjCategory::List, _ItemParser, std::false_type, /* IsLenDynamic? - false */ std::false_type /* IsItemDynamic? - false */ -> +> : + public AbiCodecImpl< + Internal::Obj::ObjCategory::List, + typename _ItemParser::Codec, + std::false_type, + std::false_type + > { - static_assert( - !_ItemParser::sk_hasTail, - "ABI parser - static item must have no tail" - ); - + using Base = AbiCodecImpl< + Internal::Obj::ObjCategory::List, + typename _ItemParser::Codec, + std::false_type, + std::false_type + >; + using Self = AbiParserImpl< + Internal::Obj::ObjCategory::List, + _ItemParser, + std::false_type, + std::false_type + >; + using Codec = Base; using ItemParser = _ItemParser; - using ItemPrimitive = typename ItemParser::Primitive; - using Primitive = std::vector; - + using ItemPrimitive = typename Base::ItemPrimitive; + using Primitive = typename Base::Primitive; - AbiCodecImpl(ItemParser itemParser, size_t size) : + AbiParserImpl(ItemParser itemParser, size_t size) : m_itemParser(std::move(itemParser)), m_size(size) {} - ~AbiCodecImpl() = default; + ~AbiParserImpl() = default; template @@ -485,45 +438,59 @@ struct AbiCodecImpl< ItemParser m_itemParser; size_t m_size; -}; // struct AbiCodecImpl +}; // struct AbiParserImpl // ========== -// AbiCodecImpl for T[k] types, where T is dynamic type +// AbiParserImpl for T[k] types, where T is dynamic type // ========== template -struct AbiCodecImpl< +struct AbiParserImpl< Internal::Obj::ObjCategory::List, _ItemParser, std::false_type, /* IsLenDynamic? - false */ std::true_type /* IsItemDynamic? - true */ -> +> : + public AbiCodecImpl< + Internal::Obj::ObjCategory::List, + typename _ItemParser::Codec, + std::false_type, + std::true_type + > { - static_assert( - _ItemParser::sk_hasTail, - "ABI parser - dynamic item must have tail" - ); - + using Base = AbiCodecImpl< + Internal::Obj::ObjCategory::List, + typename _ItemParser::Codec, + std::false_type, + std::true_type + >; + using Self = AbiParserImpl< + Internal::Obj::ObjCategory::List, + _ItemParser, + std::false_type, + std::true_type + >; + using Codec = Base; using ItemParser = _ItemParser; - using ItemPrimitive = typename ItemParser::Primitive; - using Primitive = std::vector; + using ItemPrimitive = typename Base::ItemPrimitive; + using Primitive = typename Base::Primitive; - AbiCodecImpl(ItemParser itemParser, size_t size) : + AbiParserImpl(ItemParser itemParser, size_t size) : m_itemParser(std::move(itemParser)), m_size(size) {} - ~AbiCodecImpl() = default; + ~AbiParserImpl() = default; template std::tuple< - Primitive, /* Parsed value */ - _ItType, /* Iterator of where the parsing stopped */ - size_t /* Number of chunks consumed */ + Primitive, /* Parsed value */ + _ItType, /* Iterator of where the parsing stopped */ + size_t /* Number of chunks consumed */ > ToPrimitive(size_t size, _ItType begin, _ItType end) const { @@ -569,9 +536,9 @@ struct AbiCodecImpl< template std::tuple< - Primitive, /* Parsed value */ - _ItType, /* Iterator of where the parsing stopped */ - size_t /* Number of chunks consumed */ + Primitive, /* Parsed value */ + _ItType, /* Iterator of where the parsing stopped */ + size_t /* Number of chunks consumed */ > ToPrimitive(_ItType begin, _ItType end) const { @@ -583,58 +550,59 @@ struct AbiCodecImpl< ItemParser m_itemParser; size_t m_size; -}; // struct AbiCodecImpl +}; // struct AbiParserImpl // ========== -// AbiCodecImpl for T[] types, where T is static type +// AbiParserImpl for T[] types, where T is static type // ========== -template -struct AbiCodecImpl< - Internal::Obj::ObjCategory::List, - _ItemParser, - std::true_type, /* IsLenDynamic? - true */ - std::false_type /* IsItemDynamic? - false */ -> +template +struct AbiParserDynLenListImpl : + public AbiCodecImpl< + Internal::Obj::ObjCategory::List, + typename _ItemParser::Codec, + std::true_type, + _IsItemDyn + > { - static_assert( - !_ItemParser::sk_hasTail, - "ABI parser - static item must have no tail" - ); - + using Base = AbiCodecImpl< + Internal::Obj::ObjCategory::List, + typename _ItemParser::Codec, + std::true_type, + _IsItemDyn + >; + using Self = AbiParserDynLenListImpl<_ItemParser, _IsItemDyn>; + using Codec = Base; using ItemParser = _ItemParser; - using ItemPrimitive = typename ItemParser::Primitive; - using Primitive = std::vector; + using ItemPrimitive = typename Base::ItemPrimitive; + using Primitive = typename Base::Primitive; - using DynLenParser = AbiCodecImpl< + using DynLenParser = AbiParserImpl< Internal::Obj::ObjCategory::Integer, - std::integral_constant< - Internal::Obj::RealNumType, - Internal::Obj::RealNumType::UInt64 - > + AbiUInt64 >; - using DataParser = AbiCodecImpl< + using DataParser = AbiParserImpl< Internal::Obj::ObjCategory::List, ItemParser, std::false_type, /* IsLenDynamic? - false */ - std::false_type /* IsItemDynamic? - false */ + _IsItemDyn /* IsItemDynamic? - false */ >; - AbiCodecImpl(ItemParser itemParser) : + AbiParserDynLenListImpl(ItemParser itemParser) : m_dataParser(std::move(itemParser), 0) {} - ~AbiCodecImpl() = default; + ~AbiParserDynLenListImpl() = default; template std::tuple< - Primitive, /* Parsed value */ - _ItType, /* Iterator of where the parsing stopped */ - size_t /* Number of chunks consumed */ + Primitive, /* Parsed value */ + _ItType, /* Iterator of where the parsing stopped */ + size_t /* Number of chunks consumed */ > ToPrimitive(_ItType begin, _ItType end) const { @@ -662,85 +630,54 @@ struct AbiCodecImpl< DataParser m_dataParser; -}; // struct AbiCodecImpl - - -// ========== -// AbiCodecImpl for T[] types, where T is dynamic type -// ========== +}; // struct AbiParserDynLenListImpl template -struct AbiCodecImpl< +struct AbiParserImpl< Internal::Obj::ObjCategory::List, _ItemParser, std::true_type, /* IsLenDynamic? - true */ - std::true_type /* IsItemDynamic? - true */ -> + std::false_type /* IsItemDynamic? - true */ +> : + public AbiParserDynLenListImpl<_ItemParser, std::false_type> { - static_assert( - _ItemParser::sk_hasTail, - "ABI parser - dynamic item must have tail" - ); - - - using ItemParser = _ItemParser; - using ItemPrimitive = typename ItemParser::Primitive; - using Primitive = std::vector; - - using DynLenParser = AbiCodecImpl< - Internal::Obj::ObjCategory::Integer, - std::integral_constant< - Internal::Obj::RealNumType, - Internal::Obj::RealNumType::UInt64 - > - >; - using DataParser = AbiCodecImpl< + using Base = AbiParserDynLenListImpl<_ItemParser, std::false_type>; + using Self = AbiParserImpl< Internal::Obj::ObjCategory::List, - ItemParser, - std::false_type, /* IsLenDynamic? - false */ - std::true_type /* IsItemDynamic? - true */ + _ItemParser, + std::true_type, + std::false_type >; - AbiCodecImpl(ItemParser itemParser) : - m_dataParser(std::move(itemParser), 0) - {} - ~AbiCodecImpl() = default; - + using Base::Base; +}; // struct AbiParserImpl - template - std::tuple< - Primitive, /* Parsed value */ - _ItType, /* Iterator of where the parsing stopped */ - size_t /* Number of chunks consumed */ - > - ToPrimitive(_ItType begin, _ItType end) const - { - // first, parse the length of the bytes - size_t totalChunkConsumed = 0; - uint64_t len = 0; - size_t chunkConsumed = 0; - std::tie(len, begin, chunkConsumed) = - DynLenParser().ToPrimitive(begin, end); - totalChunkConsumed += chunkConsumed; - - size_t lenSize = static_cast(len); - - // then, parse the data / list items - Primitive res; - std::tie(res, begin, chunkConsumed) = - m_dataParser.ToPrimitive(lenSize, begin, end); - totalChunkConsumed += chunkConsumed; - - return std::make_tuple(res, begin, totalChunkConsumed); - } +// ========== +// AbiParserImpl for T[] types, where T is dynamic type +// ========== -private: - DataParser m_dataParser; +template +struct AbiParserImpl< + Internal::Obj::ObjCategory::List, + _ItemParser, + std::true_type, /* IsLenDynamic? - true */ + std::true_type /* IsItemDynamic? - true */ +> : + public AbiParserDynLenListImpl<_ItemParser, std::true_type> +{ + using Base = AbiParserDynLenListImpl<_ItemParser, std::true_type>; + using Self = AbiParserImpl< + Internal::Obj::ObjCategory::List, + _ItemParser, + std::true_type, + std::true_type + >; -}; // struct AbiCodecImpl + using Base::Base; +}; // struct AbiParserImpl // ========== @@ -749,19 +686,21 @@ struct AbiCodecImpl< template< - typename _CodecImpl + typename _ParserImpl > struct AbiParserHeadOnlyTypes { - using Codec = _CodecImpl; - using HeadCodec = Codec; + using ParserImpl = _ParserImpl; + using Codec = typename ParserImpl::Codec; + + using HeadParserImpl = ParserImpl; static constexpr bool sk_hasTail = false; - using HeadPrimitive = typename HeadCodec::Primitive; + using HeadPrimitive = typename HeadParserImpl::Primitive; using Primitive = HeadPrimitive; // constructors - AbiParserHeadOnlyTypes(HeadCodec headCodec) : + AbiParserHeadOnlyTypes(HeadParserImpl headCodec) : m_headCodec(std::move(headCodec)) {} // destructor @@ -798,33 +737,41 @@ struct AbiParserHeadOnlyTypes protected: - HeadCodec m_headCodec; + HeadParserImpl m_headCodec; }; // struct AbiParserHeadOnlyTypes template< - typename _CodecImpl + typename _ParserImpl > struct AbiParserHeadTailTypes { - using Codec = _CodecImpl; - using HeadCodec = AbiCodecImpl< + using ParserImpl = _ParserImpl; + using Codec = typename ParserImpl::Codec; + + /** + * @brief The type of the parser used to parse the head part of the ABI data + * NOTE: the ABI spec assume the head is always a uint256 offset, + * but here we assume a uint64 offset for simplicity, and it's + * very unlikely that the offset will be larger than uint64 in + * real-world cases. + * + */ + using HeadParserImpl = AbiParserImpl< Internal::Obj::ObjCategory::Integer, - std::integral_constant< - Internal::Obj::RealNumType, - Internal::Obj::RealNumType::UInt64 - > + AbiUInt64 >; // head is a uint256 offset - using TailCodec = Codec; + + using TailParserImpl = ParserImpl; static constexpr bool sk_hasTail = true; - using HeadPrimitive = typename HeadCodec::Primitive; - using TailPrimitive = typename TailCodec::Primitive; + using HeadPrimitive = typename HeadParserImpl::Primitive; + using TailPrimitive = typename TailParserImpl::Primitive; using Primitive = TailPrimitive; // constructors - AbiParserHeadTailTypes(TailCodec tailCodec) : + AbiParserHeadTailTypes(TailParserImpl tailCodec) : m_headCodec(), m_tailCodec(std::move(tailCodec)) {} @@ -908,43 +855,23 @@ struct AbiParserHeadTailTypes protected: - HeadCodec m_headCodec; - TailCodec m_tailCodec; + HeadParserImpl m_headCodec; + TailParserImpl m_tailCodec; }; // struct AbiParserHeadTailTypes -template +template using AbiHeadTailTypesSelector = typename std::conditional< _HasTail, - AbiParserHeadTailTypes<_CodecImpl>, - AbiParserHeadOnlyTypes<_CodecImpl> + AbiParserHeadTailTypes<_ParserImpl>, + AbiParserHeadOnlyTypes<_ParserImpl> >::type; } // namespace EthInternal -// ========== -// Alias types for convenience -// ========== - - -using AbiUInt8 = - std::integral_constant< - Internal::Obj::RealNumType, - Internal::Obj::RealNumType::UInt8 - >; -using AbiUInt64 = - std::integral_constant< - Internal::Obj::RealNumType, - Internal::Obj::RealNumType::UInt64 - >; - -template -using AbiSize = std::integral_constant; - - // ========== // AbiParser general template // ========== @@ -967,22 +894,21 @@ struct AbiParser< std::integral_constant > : EthInternal::AbiParserHeadOnlyTypes< - EthInternal::AbiCodecImpl< + EthInternal::AbiParserImpl< Internal::Obj::ObjCategory::Integer, std::integral_constant > > { - using Base = EthInternal::AbiParserHeadOnlyTypes< - EthInternal::AbiCodecImpl< - Internal::Obj::ObjCategory::Integer, - std::integral_constant - > + using ParserImpl = EthInternal::AbiParserImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant >; - using Codec = typename Base::Codec; + using Base = EthInternal::AbiParserHeadOnlyTypes; + using Codec = typename ParserImpl::Codec; AbiParser() : - Base(Codec()) + Base(ParserImpl()) {} // LCOV_EXCL_START virtual ~AbiParser() = default; @@ -1000,20 +926,19 @@ struct AbiParser< Internal::Obj::ObjCategory::Bool > : EthInternal::AbiParserHeadOnlyTypes< - EthInternal::AbiCodecImpl< + EthInternal::AbiParserImpl< Internal::Obj::ObjCategory::Bool > > { - using Base = EthInternal::AbiParserHeadOnlyTypes< - EthInternal::AbiCodecImpl< - Internal::Obj::ObjCategory::Bool - > + using ParserImpl = EthInternal::AbiParserImpl< + Internal::Obj::ObjCategory::Bool >; - using Codec = typename Base::Codec; + using Base = EthInternal::AbiParserHeadOnlyTypes; + using Codec = typename ParserImpl::Codec; AbiParser() : - Base(Codec()) + Base(ParserImpl()) {} // LCOV_EXCL_START virtual ~AbiParser() = default; @@ -1032,28 +957,27 @@ struct AbiParser< std::false_type /* IsDynamic? - false */ > : EthInternal::AbiParserHeadOnlyTypes< - EthInternal::AbiCodecImpl< + EthInternal::AbiParserImpl< Internal::Obj::ObjCategory::Bytes, std::false_type > > { - using Base = EthInternal::AbiParserHeadOnlyTypes< - EthInternal::AbiCodecImpl< - Internal::Obj::ObjCategory::Bytes, - std::false_type - > + using ParserImpl = EthInternal::AbiParserImpl< + Internal::Obj::ObjCategory::Bytes, + std::false_type >; - using Codec = typename Base::Codec; + using Base = EthInternal::AbiParserHeadOnlyTypes; + using Codec = typename ParserImpl::Codec; AbiParser(size_t size) : - Base(Codec(EthInternal::AbiWithinChunkSize(size))) + Base(ParserImpl(EthInternal::AbiWithinChunkSize(size))) {} // LCOV_EXCL_START virtual ~AbiParser() = default; // LCOV_EXCL_STOP -}; // struct AbiParser +}; // struct AbiParser template @@ -1062,33 +986,32 @@ struct AbiParser< std::integral_constant > : EthInternal::AbiParserHeadOnlyTypes< - EthInternal::AbiCodecImpl< + EthInternal::AbiParserImpl< Internal::Obj::ObjCategory::Bytes, std::false_type > > { + using ParserImpl = EthInternal::AbiParserImpl< + Internal::Obj::ObjCategory::Bytes, + std::false_type + >; + using Base = EthInternal::AbiParserHeadOnlyTypes; + using Codec = typename ParserImpl::Codec; + static constexpr size_t sk_size = _Size; static_assert( sk_size <= AbiParserConst::sk_chunkSize(), "ABI parser - bytes type is too large" ); - using Base = EthInternal::AbiParserHeadOnlyTypes< - EthInternal::AbiCodecImpl< - Internal::Obj::ObjCategory::Bytes, - std::false_type - > - >; - using Codec = typename Base::Codec; - AbiParser() : - Base(Codec(sk_size)) + Base(ParserImpl(sk_size)) {} // LCOV_EXCL_START virtual ~AbiParser() = default; // LCOV_EXCL_STOP -}; // struct AbiParser +}; // struct AbiParser // ========== @@ -1102,28 +1025,27 @@ struct AbiParser< std::true_type /* IsDynamic? - true */ > : EthInternal::AbiParserHeadTailTypes< - EthInternal::AbiCodecImpl< + EthInternal::AbiParserImpl< Internal::Obj::ObjCategory::Bytes, std::true_type > > { - using Base = EthInternal::AbiParserHeadTailTypes< - EthInternal::AbiCodecImpl< - Internal::Obj::ObjCategory::Bytes, - std::true_type - > + using ParserImpl = EthInternal::AbiParserImpl< + Internal::Obj::ObjCategory::Bytes, + std::true_type >; - using Codec = typename Base::Codec; + using Base = EthInternal::AbiParserHeadTailTypes; + using Codec = typename ParserImpl::Codec; AbiParser() : - Base(Codec()) + Base(ParserImpl()) {} // LCOV_EXCL_START virtual ~AbiParser() = default; // LCOV_EXCL_STOP -}; // struct AbiParser +}; // struct AbiParser // ========== @@ -1138,35 +1060,30 @@ struct AbiParser< std::false_type /* IsLenDynamic? - false */ > : EthInternal::AbiHeadTailTypesSelector< - _ItemParser::sk_hasTail, - EthInternal::AbiCodecImpl< + _ItemParser::Codec::sk_isDynamic, + EthInternal::AbiParserImpl< Internal::Obj::ObjCategory::List, _ItemParser, std::false_type, - std::integral_constant< - bool, - _ItemParser::sk_hasTail - > + typename _ItemParser::Codec::IsDynamic > > { + using ParserImpl = EthInternal::AbiParserImpl< + Internal::Obj::ObjCategory::List, + _ItemParser, + std::false_type, + typename _ItemParser::Codec::IsDynamic + >; using Base = EthInternal::AbiHeadTailTypesSelector< - _ItemParser::sk_hasTail, - EthInternal::AbiCodecImpl< - Internal::Obj::ObjCategory::List, - _ItemParser, - std::false_type, - std::integral_constant< - bool, - _ItemParser::sk_hasTail - > - > + _ItemParser::Codec::sk_isDynamic, + ParserImpl >; - using Codec = typename Base::Codec; + using Codec = typename ParserImpl::Codec; AbiParser(_ItemParser itemParser, size_t len) : - Base(Codec(std::move(itemParser), len)) + Base(ParserImpl(std::move(itemParser), len)) {} // LCOV_EXCL_START virtual ~AbiParser() = default; @@ -1181,36 +1098,31 @@ struct AbiParser< std::integral_constant > : EthInternal::AbiHeadTailTypesSelector< - _ItemParser::sk_hasTail, - EthInternal::AbiCodecImpl< + _ItemParser::Codec::sk_isDynamic, + EthInternal::AbiParserImpl< Internal::Obj::ObjCategory::List, _ItemParser, std::false_type, - std::integral_constant< - bool, - _ItemParser::sk_hasTail - > + typename _ItemParser::Codec::IsDynamic > > { + using ParserImpl = EthInternal::AbiParserImpl< + Internal::Obj::ObjCategory::List, + _ItemParser, + std::false_type, + typename _ItemParser::Codec::IsDynamic + >; using Base = EthInternal::AbiHeadTailTypesSelector< - _ItemParser::sk_hasTail, - EthInternal::AbiCodecImpl< - Internal::Obj::ObjCategory::List, - _ItemParser, - std::false_type, - std::integral_constant< - bool, - _ItemParser::sk_hasTail - > - > + _ItemParser::Codec::sk_isDynamic, + ParserImpl >; - using Codec = typename Base::Codec; + using Codec = typename ParserImpl::Codec; static constexpr size_t sk_size = _Size; AbiParser(_ItemParser itemParser) : - Base(Codec(std::move(itemParser), sk_size)) + Base(ParserImpl(std::move(itemParser), sk_size)) {} // LCOV_EXCL_START virtual ~AbiParser() = default; @@ -1231,34 +1143,26 @@ struct AbiParser< > : EthInternal::AbiParserHeadTailTypes< // dynamic length make this type always has tail - EthInternal::AbiCodecImpl< + EthInternal::AbiParserImpl< Internal::Obj::ObjCategory::List, _ItemParser, std::true_type, - std::integral_constant< - bool, - _ItemParser::sk_hasTail - > + typename _ItemParser::Codec::IsDynamic > > { - using Base = - EthInternal::AbiParserHeadTailTypes< - EthInternal::AbiCodecImpl< - Internal::Obj::ObjCategory::List, - _ItemParser, - std::true_type, - std::integral_constant< - bool, - _ItemParser::sk_hasTail - > - > - >; - using Codec = typename Base::Codec; + using ParserImpl = EthInternal::AbiParserImpl< + Internal::Obj::ObjCategory::List, + _ItemParser, + std::true_type, + typename _ItemParser::Codec::IsDynamic + >; + using Base = EthInternal::AbiParserHeadTailTypes; + using Codec = typename ParserImpl::Codec; explicit AbiParser(_ItemParser itemParser) : - Base(Codec(std::move(itemParser))) + Base(ParserImpl(std::move(itemParser))) {} // LCOV_EXCL_START virtual ~AbiParser() = default; @@ -1268,3 +1172,4 @@ struct AbiParser< } // namespace Eth } // namespace EclipseMonitor + diff --git a/test/src/TestEthAbiParser.cpp b/test/src/TestEthAbiParser.cpp index 5914374..ab3023a 100644 --- a/test/src/TestEthAbiParser.cpp +++ b/test/src/TestEthAbiParser.cpp @@ -1019,7 +1019,7 @@ GTEST_TEST(TestEthAbiParser, ParseMixParamsPrimitive) // function bar( // bool isFoo, // uint64 num, - // bytes32 fooBytes, + // bytes16 fooBytes, // bytes dynBytes, // uint64[2] nums, // bytes[] arrBytes, @@ -1110,6 +1110,14 @@ GTEST_TEST(TestEthAbiParser, ParseMixParamsPrimitive) 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, }; + // std::string testInputHex; + // SimpleObjects::Internal::BytesToHEX( + // std::back_inserter(testInputHex), + // input.begin(), + // input.end() + // ); + // std::cout << testInputHex << std::endl; + // destination variables bool param1; bool param1ExpVal = true; From b4f5c5f3bb7a9151f8361cc9dc5de6673e8185f7 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Mon, 20 May 2024 00:12:54 -0700 Subject: [PATCH 04/22] Added some basics ABI writer classes --- include/EclipseMonitor/Eth/AbiCommon.hpp | 44 +- include/EclipseMonitor/Eth/AbiParser.hpp | 24 +- include/EclipseMonitor/Eth/AbiWriter.hpp | 1035 ++++++++++++++++++++++ test/src/Main.cpp | 2 +- test/src/TestEthAbiWriter.cpp | 520 +++++++++++ 5 files changed, 1601 insertions(+), 24 deletions(-) create mode 100644 include/EclipseMonitor/Eth/AbiWriter.hpp create mode 100644 test/src/TestEthAbiWriter.cpp diff --git a/include/EclipseMonitor/Eth/AbiCommon.hpp b/include/EclipseMonitor/Eth/AbiCommon.hpp index 9d26548..e62eda7 100644 --- a/include/EclipseMonitor/Eth/AbiCommon.hpp +++ b/include/EclipseMonitor/Eth/AbiCommon.hpp @@ -27,13 +27,13 @@ namespace Eth // ========== -struct AbiParserConst +struct AbiCodecConst { static constexpr size_t sk_chunkSize() noexcept { return 32; } -}; // struct AbiParserConst +}; // struct AbiCodecConst // ========== @@ -87,6 +87,14 @@ inline constexpr _T AbiCeilingDiv(_T a, _T b) noexcept } +inline constexpr size_t AbiWithinChunkSize(size_t size) +{ + return size <= AbiCodecConst::sk_chunkSize() ? + size : + throw Exception("ABI parser - bytes type is too large"); +} + + template struct RealNumTypeTraits; @@ -97,7 +105,12 @@ struct RealNumTypeTraits : Internal::Obj::RealNumType::UInt8, uint8_t > -{}; // struct RealNumTypeTraits +{ + static Primitive FromRealNumBase(const Internal::Obj::RealNumBaseObj& val) + { + return val.AsCppUInt8(); + } +}; // struct RealNumTypeTraits template<> @@ -115,7 +128,12 @@ struct RealNumTypeTraits : Internal::Obj::RealNumType::UInt32, uint32_t > -{}; // struct RealNumTypeTraits +{ + static Primitive FromRealNumBase(const Internal::Obj::RealNumBaseObj& val) + { + return val.AsCppUInt32(); + } +}; // struct RealNumTypeTraits template<> @@ -124,7 +142,12 @@ struct RealNumTypeTraits : Internal::Obj::RealNumType::UInt64, uint64_t > -{}; // struct RealNumTypeTraits +{ + static Primitive FromRealNumBase(const Internal::Obj::RealNumBaseObj& val) + { + return val.AsCppUInt64(); + } +}; // struct RealNumTypeTraits } // namespace EthInternal @@ -162,17 +185,18 @@ struct AbiCodecImpl< static constexpr size_t sk_consumedSize = RealNumTraits::sk_consumedSize(); static_assert( - sk_consumedSize <= AbiParserConst::sk_chunkSize(), + sk_consumedSize <= AbiCodecConst::sk_chunkSize(), "ABI parser - integer type is too large" ); static constexpr size_t sk_leadPadSize = - AbiParserConst::sk_chunkSize() - sk_consumedSize; + AbiCodecConst::sk_chunkSize() - sk_consumedSize; using Primitive = typename RealNumTraits::Primitive; using IsDynamic = std::false_type; static constexpr bool sk_isDynamic = IsDynamic::value; + bool IsDynamicType() const noexcept { return sk_isDynamic; } }; // struct AbiCodecImpl @@ -205,6 +229,7 @@ struct AbiCodecImpl< using IsDynamic = std::false_type; static constexpr bool sk_isDynamic = IsDynamic::value; + bool IsDynamicType() const noexcept { return sk_isDynamic; } }; // struct AbiCodecImpl @@ -225,6 +250,7 @@ struct AbiCodecImpl< using IsDynamic = std::false_type; static constexpr bool sk_isDynamic = IsDynamic::value; + bool IsDynamicType() const noexcept { return sk_isDynamic; } }; // struct AbiCodecImpl @@ -245,6 +271,7 @@ struct AbiCodecImpl< using IsDynamic = std::true_type; static constexpr bool sk_isDynamic = IsDynamic::value; + bool IsDynamicType() const noexcept { return sk_isDynamic; } }; // struct AbiCodecImpl @@ -273,6 +300,7 @@ struct AbiCodecImpl< using IsDynamic = std::false_type; static constexpr bool sk_isDynamic = IsDynamic::value; + bool IsDynamicType() const noexcept { return sk_isDynamic; } }; // struct AbiCodecImpl @@ -301,6 +329,7 @@ struct AbiCodecImpl< using IsDynamic = std::true_type; static constexpr bool sk_isDynamic = IsDynamic::value; + bool IsDynamicType() const noexcept { return sk_isDynamic; } }; // struct AbiCodecImpl @@ -334,6 +363,7 @@ struct AbiCodecDynLenListImpl using IsDynamic = std::true_type; static constexpr bool sk_isDynamic = IsDynamic::value; + bool IsDynamicType() const noexcept { return sk_isDynamic; } }; // struct AbiCodecDynLenListImpl diff --git a/include/EclipseMonitor/Eth/AbiParser.hpp b/include/EclipseMonitor/Eth/AbiParser.hpp index be87c10..1f803a0 100644 --- a/include/EclipseMonitor/Eth/AbiParser.hpp +++ b/include/EclipseMonitor/Eth/AbiParser.hpp @@ -94,14 +94,6 @@ inline _SrcIt AbiParserCopyBytesThenSkip( } -inline constexpr size_t AbiWithinChunkSize(size_t size) -{ - return size <= AbiParserConst::sk_chunkSize() ? - size : - throw Exception("ABI parser - bytes type is too large"); -} - - } // namespace EthInternal @@ -195,12 +187,12 @@ struct AbiParserImpl< using Codec = Base; using Primitive = typename Base::Primitive; - using IntParser = AbiParserImpl< + using IntParserImpl = AbiParserImpl< Internal::Obj::ObjCategory::Integer, AbiUInt8 >; static_assert( - std::is_same::value, + std::is_same::value, "ABI parser - bool parser must have the same base as uint8 parser" ); @@ -215,7 +207,7 @@ struct AbiParserImpl< uint8_t valInt = 0; size_t chunkConsumed = 0; std::tie(valInt, begin, chunkConsumed) = - IntParser().ToPrimitive(begin, end); + IntParserImpl().ToPrimitive(begin, end); bool valBool = (valInt == 1 ? true : @@ -256,7 +248,7 @@ struct AbiParserImpl< */ AbiParserImpl(size_t size) : m_size(size), - m_padSize(AbiParserConst::sk_chunkSize() - m_size) + m_padSize(AbiCodecConst::sk_chunkSize() - m_size) {} ~AbiParserImpl() = default; @@ -328,10 +320,10 @@ struct AbiParserImpl< size_t numChunk = EthInternal::AbiCeilingDiv( static_cast(len), - AbiParserConst::sk_chunkSize() + AbiCodecConst::sk_chunkSize() ); size_t paddingSize = - (numChunk * AbiParserConst::sk_chunkSize()) - len; + (numChunk * AbiCodecConst::sk_chunkSize()) - len; Primitive res; res.reserve(len); @@ -517,7 +509,7 @@ struct AbiParserImpl< { // check the offset is correct size_t bytesConsumed = - totalChunkConsumed * AbiParserConst::sk_chunkSize(); + totalChunkConsumed * AbiCodecConst::sk_chunkSize(); if (head != bytesConsumed) { throw Exception("ABI parser - invalid offset"); @@ -1001,7 +993,7 @@ struct AbiParser< static constexpr size_t sk_size = _Size; static_assert( - sk_size <= AbiParserConst::sk_chunkSize(), + sk_size <= AbiCodecConst::sk_chunkSize(), "ABI parser - bytes type is too large" ); diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp new file mode 100644 index 0000000..7fdcac4 --- /dev/null +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -0,0 +1,1035 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#include + +#include +#include + +#include "../Internal/SimpleObj.hpp" +#include "../Internal/SimpleRlp.hpp" +#include "../Exceptions.hpp" + +#include "AbiCommon.hpp" + + +namespace EclipseMonitor +{ +namespace Eth +{ + + +// ========== +// Utilities +// ========== + + +namespace EthInternal +{ + + +/** + * @brief Write padding to the output stream + * + * @tparam _It The type of the iterator that accepts a byte in a time + * @tparam _ValType The type of the value to be filled as padding + * + * @param it The output iterator that accepts a byte in a time + * @param size The length of the padding in bytes + * @param val The value to be filled as padding + * @return The iterator pointing the latest end of the output stream + */ +template +inline _It AbiWritePadding(_It it, size_t size, const _ValType& val) +{ + for (size_t i = 0; i < size; ++i) + { + *(it++) = val; + } + + return it; +} + + +/** + * @brief Write given bytes to the destination stream + * + * @tparam _DestIt The type of the iterators of the output stream + * @tparam _SrcIt The type of the iterators of the input byte stream + * + * @param destIt The iterator of the output stream; the iterator should accept + * a byte in a time + * @param begin The iterator pointing the beginning of the input steam + * @param end The iterator pointing the end of the input stream + * @return The iterator pointing the latest end of the output stream + */ +template +inline _DestIt AbiWriteBytes( + _DestIt destIt, + _SrcIt begin, + _SrcIt end +) +{ + for (auto it = begin; it != end; ++it) + { + *(destIt++) = *(it); + } + return destIt; +} + + +/** + * @brief Write given bytes to the destination stream and then fill in padding + * bytes + * + * @tparam _DestIt The type of the iterators of the output stream + * @tparam _SrcIt The type of the iterators of the input byte stream + * @tparam _ValType The type of the value to be filled as padding + * + * @param destIt The iterator of the output stream + * @param begin The iterator pointing the beginning of the input steam + * @param end The iterator pointing the end of the input stream + * @param padSize The length of the padding + * @param val The value to be filled as padding + * @return The iterator pointing the latest end of the output stream + */ +template +inline _DestIt AbiWriteBytesThenPad( + _DestIt destIt, + _SrcIt begin, + _SrcIt end, + size_t padSize, + const _ValType& val +) +{ + destIt = AbiWriteBytes(destIt, begin, end); + destIt = AbiWritePadding(destIt, padSize, val); + return destIt; +} + + +/** + * @brief The type that the writer is going to write the bytes to. + * + */ +using WrittenBytes = std::vector; + +/** + * @brief The type of output iterator that accepts a byte in a time + * + */ +using WriteIterator = Internal::Obj::OutIterator; + + +} // namespace EthInternal + + +// ========== +// Essential parser implementations +// ========== + + +namespace EthInternal +{ + + +template< + Internal::Obj::ObjCategory _DataType, + typename... _Args +> +struct AbiWriterImpl; + + +// ========== +// AbiWriterImpl for integer types +// ========== + + +template +struct AbiWriterUIntImpl : + public AbiCodecImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + > +{ + using Base = AbiCodecImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + >; + using Self = AbiWriterUIntImpl<_RealNumType>; + using Codec = Base; + + /** + * @brief Get the number of head chunks required to store the integer + * NOTE: The integer type is always a single head chunk + * + * @return The number of chunks required to store the integer + */ + size_t GetNumHeadChunks() const + { + return 1; + } + + /** + * @brief Get the number of tail chunks required to store the integer + * NOTE: The integer type does not have tail chunks + * + * @return The number of chunks required to store the integer + */ + size_t GetNumTailChunks() const + { + return 0; + } + + /** + * @brief Write the given integer value to the destination stream + * + * @tparam _DestIt The type of output iterator that accepts a byte in + * a time + * @tparam _IntType The type of the integer value to be written + * + * @param destIt The output iterator that accepts a byte in a time + * @param val The integer value to be written + * @param bytesWidth The width of the integer in bytes; + * NOTE: it's the caller's responsibility to ensure + * that an appropriate width value is given + * @return The iterator pointing the latest end of the output stream + */ + template + _DestIt Write(_DestIt destIt, const _IntType& val, size_t bytesWidth) const + { + if (bytesWidth > AbiCodecConst::sk_chunkSize()) + { + throw Exception( + "ABI writer - integer width exceeds the maximum width" + ); + } + + size_t padNeeded = AbiCodecConst::sk_chunkSize() - bytesWidth; + destIt = AbiWritePadding(destIt, padNeeded, uint8_t(0)); + + destIt = Internal::Rlp::EncodePrimitiveIntValue< + _IntType, + Internal::Rlp::Endian::native, + false + >::WriteBytes(destIt, val, bytesWidth); + + return destIt; + } + +}; // struct AbiWriterUIntImpl + + +template +struct AbiWriterImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant +> : + public AbiWriterUIntImpl<_RealNumType> +{ + using Base = AbiWriterUIntImpl<_RealNumType>; + using Self = AbiWriterImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + >; + + using Codec = typename Base::Codec; + using RealNumTraits = typename Codec::RealNumTraits; + using IntType = typename RealNumTraits::Primitive; + + template + _DestIt Write(_DestIt destIt, const IntType& val) const + { + return Base::Write(destIt, val, RealNumTraits::sk_consumedSize()); + } + + template + _DestIt Write(_DestIt destIt, const Internal::Obj::RealNumBaseObj& val) const + { + return Self::Write(destIt, RealNumTraits::FromRealNumBase(val)); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + return Self::Write(destIt, val.AsRealNum()); + } + +}; // struct AbiWriterImpl + + +// ========== +// AbiWriterImpl for bool types +// ========== + + +template<> +struct AbiWriterImpl< + Internal::Obj::ObjCategory::Bool +> : + public AbiCodecImpl +{ + using Base = AbiCodecImpl; + using Self = AbiWriterImpl; + + using Codec = Base; + using IntWriterImpl = AbiWriterImpl< + Internal::Obj::ObjCategory::Integer, + AbiUInt8 + >; + static_assert( + std::is_same::value, + "ABI codec - bool writer must have the same codec as uint8 parser" + ); + + // AbiWriterImpl(IntWriterImpl intWriter) : + // m_intWriter(std::move(intWriter)) + // {} + + // ~AbiWriterImpl() = default; + + /** + * @brief Get the number of head chunks required to store the bool + * NOTE: The bool type is always a single head chunk + * + * @return The number of chunks required to store the bool + */ + size_t GetNumHeadChunks() const + { + return 1; + } + + /** + * @brief Get the number of tail chunks required to store the bool + * NOTE: The bool type does not have tail chunks + * + * @return The number of chunks required to store the bool + */ + size_t GetNumTailChunks() const + { + return 0; + } + + /** + * @brief Write the given bool value to the destination stream + * + * @tparam _DestIt The type of output iterator that accepts a byte in + * a time + * + * @param destIt The output iterator that accepts a byte in a time + * @param val The bool value to be written + * @return The iterator pointing the latest end of the output stream + */ + template + _DestIt Write(_DestIt destIt, bool val) const + { + return IntWriterImpl().Write(destIt, val ? uint8_t(1) : uint8_t(0)); + } + + template + _DestIt Write(_DestIt destIt, const Internal::Obj::RealNumBaseObj& val) const + { + return Self::Write(destIt, val.IsTrue()); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + return Self::Write(destIt, val.AsRealNum()); + } + +private: + + // IntWriterImpl m_intWriter; + +}; // struct AbiWriterImpl + + +// ========== +// AbiWriterImpl for bytes types +// ========== + + +template<> +struct AbiWriterImpl< + Internal::Obj::ObjCategory::Bytes, + std::false_type // IsDynamic? - false +> : public AbiCodecImpl +{ + using Base = + AbiCodecImpl; + using Self = + AbiWriterImpl; + + using Codec = Base; + + /** + * @brief Construct a new Abi Parser Impl object + * NOTE: This constructor does not check the size value, + * since this is an internal type and we assume the size is checked + * before calling this + * + * @param size The size of the bytes, which should be within the range of + * (0, 32] + */ + AbiWriterImpl(size_t size) : + m_size(size), + m_padSize(AbiCodecConst::sk_chunkSize() - m_size) + {} + + ~AbiWriterImpl() = default; + + /** + * @brief Get the number of head chunks required to store the static bytes + * NOTE: The static bytes is always a single head chunk + * + * @return The number of chunks required to store the static bytes + */ + size_t GetNumHeadChunks() const + { + return 1; + } + + /** + * @brief Get the number of tail chunks required to store the static bytes + * NOTE: The static bytes type does not have tail chunks + * + * @return The number of chunks required to store the static bytes + */ + size_t GetNumTailChunks() const + { + return 0; + } + + /** + * @brief Write the given bytes to the destination stream + * + * @tparam _DestIt The type of output iterator that accepts a byte in a time + * @tparam _SrcIt The type of input iterator that provides a byte in a time + * + * @param destIt The output iterator that accepts a byte in a time + * @param begin The iterator pointing the beginning of the input steam + * @param end The iterator pointing the end of the input stream + * @return The iterator pointing the latest end of the output stream + */ + template + _DestIt Write(_DestIt destIt, _SrcIt begin, _SrcIt end) const + { + return AbiWriteBytesThenPad( + destIt, + begin, + end, + m_padSize, + uint8_t(0) + ); + } + + /** + * @brief Write the given bytes to the destination stream + * + * @tparam _DestIt The type of output iterator that accepts a byte in a time + * @tparam _CntT The type of the container that stores the bytes + * + * @param destIt The output iterator that accepts a byte in a time + * @param cnt The container that stores the bytes + * @return The iterator pointing the latest end of the output stream + */ + template + _DestIt Write(_DestIt destIt, const _CntT& cnt) const + { + if (cnt.size() != m_size) + { + throw Exception( + "ABI writer - the given bytes should have exactly the same " + "length as the static bytes type" + ); + } + return Write(destIt, cnt.begin(), cnt.end()); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + return Write(destIt, val.AsBytes()); + } + + size_t GetPadSize() const + { + return m_padSize; + } + +private: + + size_t m_size; + size_t m_padSize; + +}; // struct AbiWriterImpl + + +// ========== +// AbiWriterImpl for bytes types +// ========== + + +template<> +struct AbiWriterImpl< + Internal::Obj::ObjCategory::Bytes, + std::true_type // IsDynamic? - true +> : public AbiCodecImpl +{ + using Base = + AbiCodecImpl; + using Self = + AbiWriterImpl; + + using Codec = Base; + using DynLenWriterImpl = + AbiWriterImpl; + + // AbiWriterImpl(DynLenWriterImpl dynLenWriter) : + // m_dynLenWriter(std::move(dynLenWriter)) + // {} + + // ~AbiWriterImpl() = default; + + /** + * @brief Get the number of head chunks required to store the dynamic bytes + * NOTE: The dynamic bytes always requires a single head chunk + * + * @return The number of chunks required + */ + size_t GetNumHeadChunks() const + { + return 1; + } + + /** + * @brief Get the number of data chunks required to store the dynamic bytes + * + * @return The number of chunks required + */ + size_t GetNumDataChunks(size_t len) const + { + return EthInternal::AbiCeilingDiv(len, AbiCodecConst::sk_chunkSize()); + } + + /** + * @brief Get the number of tail chunks required to store the dynamic bytes + * NOTE: The number of tail chunks required by dynamic bytes: + * - 1 chunk stores the length of the bytes, plus + * - num of data chunks + * + * @return The number of chunks required + */ + size_t GetNumTailChunks(size_t len) const + { + return 1 + GetNumDataChunks(len); + } + + /** + * @brief Get the size of the padding needed to store the dynamic bytes + * + * @param len The length of the dynamic bytes + * @return The size of the padding needed + */ + size_t GetPadSize(size_t len) const + { + return (GetNumDataChunks(len) * AbiCodecConst::sk_chunkSize()) - len; + } + + /** + * @brief Write the given bytes to the destination stream + * + * @tparam _DestIt The type of output iterator that accepts a byte in a time + * @tparam _SrcIt The type of input iterator that provides a byte in a time + * + * @param destIt The output iterator that accepts a byte in a time + * @param begin The iterator pointing the beginning of the input steam + * @param end The iterator pointing the end of the input stream + * @param len The length of the input stream; NOTE: it's caller's + * responsibility to ensure that the length is correct + * @return The iterator pointing the latest end of the output stream + */ + template + _DestIt Write(_DestIt destIt, _SrcIt begin, _SrcIt end, size_t len) const + { + size_t padSize = GetPadSize(len); + + destIt = DynLenWriterImpl().Write(destIt, len); + return AbiWriteBytesThenPad( + destIt, + begin, + end, + padSize, + uint8_t(0) + ); + } + + /** + * @brief Write the given bytes to the destination stream + * + * @tparam _DestIt The type of output iterator that accepts a byte in a time + * @tparam _SrcIt The type of input iterator that provides a byte in a time + * + * @param destIt The output iterator that accepts a byte in a time + * @param begin The iterator pointing the beginning of the input steam + * @param end The iterator pointing the end of the input stream + * + * @return The iterator pointing the latest end of the output stream + */ + template + _DestIt Write(_DestIt destIt, _SrcIt begin, _SrcIt end) const + { + auto len = std::distance(begin, end); + if (len < 0) + { + throw Exception( + "ABI writer - invalid iterator range for input bytes" + ); + } + return Write(destIt, begin, end, static_cast(len)); + } + + /** + * @brief Write the given bytes to the destination stream + * + * @tparam _DestIt The type of output iterator that accepts a byte in a time + * @tparam _CntT The type of the container that stores the bytes + * + * @param destIt The output iterator that accepts a byte in a time + * @param cnt The container that stores the bytes + * + * @return The iterator pointing the latest end of the output stream + */ + template + _DestIt Write(_DestIt destIt, const _CntT& cnt) const + { + return Write(destIt, cnt.begin(), cnt.end(), cnt.size()); + } + +private: + + // DynLenWriterImpl m_dynLenWriter; + +}; // struct AbiWriterImpl + + +// ========== +// AbiWriterImpl for T[k] types, where T is static type +// ========== + + +template +struct AbiWriterImpl< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::false_type, // IsLenDynamic? - false + std::false_type // IsItemDynamic? - false +> : + public AbiCodecImpl< + Internal::Obj::ObjCategory::List, + typename _ItemWriter::Codec, + std::false_type, + std::false_type + > +{ + using Base = AbiCodecImpl< + Internal::Obj::ObjCategory::List, + typename _ItemWriter::Codec, + std::false_type, + std::false_type + >; + using Self = AbiWriterImpl< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::false_type, + std::false_type + >; + + using Codec = Base; + using ItemWriter = _ItemWriter; + + AbiWriterImpl(ItemWriter itemWriter, size_t size) : + m_itemWriter(std::move(itemWriter)), + m_size(size) + {} + + ~AbiWriterImpl() = default; + + /** + * @brief Get the number of head chunks required to store the list + * NOTE: The static list always stores all data at head chunks + * + * @return The number of chunks required + */ + size_t GetNumHeadChunks() const + { + return m_size * m_itemWriter.GetNumHeadChunks(); + } + + /** + * @brief Get the number of tail chunks required to store the list + * NOTE: The static list has no tail chunks + * + * @return The number of chunks required + */ + size_t GetNumTailChunks() const + { + return 0; + } + + template + _DestIt Write(_DestIt destIt, _ItemIt begin, _ItemIt end) const + { + for (auto it = begin; it != end; ++it) + { + destIt = m_itemWriter.Write(destIt, *it); + } + + return destIt; + } + + template + _DestIt Write(_DestIt destIt, const _CntT& cnt) const + { + return Write(destIt, cnt.begin(), cnt.end()); + } + +private: + + ItemWriter m_itemWriter; + size_t m_size; + +}; // struct AbiWriterImpl + + +// ========== +// AbiWriterImpl for T[k] types, where T is dynamic type +// ========== + + +// ========== +// AbiWriterImpl for T[] types, where T is static type +// ========== + + +// ========== +// AbiWriterImpl for T[] types, where T is dynamic type +// ========== + + +// ========== +// AbiWriterImpl for (T1, T2, Tn) types, where T is static type +// ========== + + +// ========== +// AbiWriterImpl for (T1, T2, Tn) types, where T is dynamic type +// ========== + + +} // namespace EthInternal + + +class AbiWriterBase +{ +public: // static members: + + using WriteIterator = EthInternal::WriteIterator; + +public: + + AbiWriterBase() = default; + + virtual ~AbiWriterBase() = default; + + virtual bool IsDynamicType() const = 0; + + virtual size_t GetNumHeadChunks() const = 0; + + virtual size_t GetNumTailChunks( + const Internal::Obj::BaseObj& data + ) const = 0; + + virtual + std::tuple< + WriteIterator, + size_t + > + WriteHead( + WriteIterator destIt, + const Internal::Obj::BaseObj& data, + size_t currDataOffset + ) const = 0; + + virtual WriteIterator WriteTail( + WriteIterator destIt, + const Internal::Obj::BaseObj& data + ) const = 0; + + // virtual WriteIterator Write( + // WriteIterator destIt, + // const Internal::Obj::BaseObj& data + // ) const = 0; + +}; // class AbiWriterBase + + +template +class AbiWriterHeadOnlyBase : public AbiWriterBase +{ +public: // static members: + + using Base = AbiWriterBase; + using Self = AbiWriterHeadOnlyBase<_Impl>; + + using WriterImpl = _Impl; + + using WriteIterator = typename Base::WriteIterator; + +public: + + AbiWriterHeadOnlyBase(WriterImpl impl) : + Base(), + m_impl(std::move(impl)) + {} + + virtual ~AbiWriterHeadOnlyBase() = default; + + bool IsDynamicType() const override + { + return m_impl.IsDynamicType(); + } + + size_t GetNumHeadChunks() const override + { + return m_impl.GetNumHeadChunks(); + } + + size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const override + { + return 0; + } + + virtual + std::tuple< + WriteIterator, + size_t + > + WriteHead( + WriteIterator destIt, + const Internal::Obj::BaseObj& data, + size_t currDataOffset + ) const override + { + return std::make_tuple( + m_impl.WriteObj(destIt, data), + currDataOffset + ); + } + + virtual WriteIterator WriteTail( + WriteIterator destIt, + const Internal::Obj::BaseObj& + ) const override + { + // Head only writer does not have tail chunks + // so we do nothing here + return destIt; + } + +private: + + WriterImpl m_impl; + +}; // class AbiWriterHeadOnlyBase + + +// ========== +// AbiWriter general template +// ========== + + +template< + Internal::Obj::ObjCategory _DataType, + typename... _Args +> +struct AbiWriter; + + +// ========== +// AbiWriter for integer types +// ========== + + +template +struct AbiWriter< + Internal::Obj::ObjCategory::Integer, + std::integral_constant +> : + public AbiWriterHeadOnlyBase< + EthInternal::AbiWriterImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + > + > +{ + using WriterImpl = EthInternal::AbiWriterImpl< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + >; + using Base = AbiWriterHeadOnlyBase; + using Self = AbiWriter< + Internal::Obj::ObjCategory::Integer, + std::integral_constant + >; + + AbiWriter() : + Base(WriterImpl()) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + +}; // struct AbiWriter + + +// ========== +// AbiWriter for bool type +// ========== + + +template<> +struct AbiWriter< + Internal::Obj::ObjCategory::Bool +> : + public AbiWriterHeadOnlyBase< + EthInternal::AbiWriterImpl< + Internal::Obj::ObjCategory::Bool + > + > +{ + using WriterImpl = + EthInternal::AbiWriterImpl; + using Base = AbiWriterHeadOnlyBase; + using Self = AbiWriter; + + AbiWriter() : + Base(WriterImpl()) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + +}; // struct AbiWriter + + +// ========== +// AbiWriter for bytes types +// ========== + + +template<> +struct AbiWriter< + Internal::Obj::ObjCategory::Bytes, + std::false_type // IsDynamic? - false +> : + public AbiWriterHeadOnlyBase< + EthInternal::AbiWriterImpl< + Internal::Obj::ObjCategory::Bytes, + std::false_type + > + > +{ + using WriterImpl = EthInternal::AbiWriterImpl< + Internal::Obj::ObjCategory::Bytes, + std::false_type + >; + using Base = AbiWriterHeadOnlyBase; + using Self = AbiWriter; + + + AbiWriter(size_t size) : + AbiWriter(EthInternal::AbiWithinChunkSize(size), size_t()) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + +protected: + + /** + * @brief A constructor that does not check the size value + * NOTE: The caller is responsible to ensure the size is within + * the chunk size; thus, this constructor is protected, and only + * accessible by this class and the derived classes + * + * @param size The size of the bytes, which should be within the chunk size + */ + AbiWriter(size_t size, size_t) : + Base(WriterImpl(size)) + {} + +}; // struct AbiWriter + + +template +struct AbiWriter< + Internal::Obj::ObjCategory::Bytes, + std::integral_constant +> : + public AbiWriter< + Internal::Obj::ObjCategory::Bytes, + std::false_type // IsDynamic? - false + > +{ + using Base = AbiWriter; + using Self = AbiWriter< + Internal::Obj::ObjCategory::Bytes, + std::integral_constant + >; + + static constexpr size_t sk_size = _Size; + static_assert( + sk_size <= AbiCodecConst::sk_chunkSize(), + "ABI parser - bytes type is too large" + ); + + AbiWriter() : + Base(sk_size, size_t()) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + +}; // struct AbiWriter + + +// ========== +// AbiWriter for bytes types +// ========== + + +// ========== +// AbiWriter for list types (T[k]) +// ========== + + +// ========== +// AbiWriter for list types (T[]) +// ========== + + + +} // namespace Eth +} // namespace EclipseMonitor + diff --git a/test/src/Main.cpp b/test/src/Main.cpp index 22ff8a3..02d013c 100644 --- a/test/src/Main.cpp +++ b/test/src/Main.cpp @@ -12,7 +12,7 @@ namespace EclipseMonitor_Test int main(int argc, char** argv) { - constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 23; + constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 24; std::cout << "===== EclipseMonitor test program =====" << std::endl; std::cout << std::endl; diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp new file mode 100644 index 0000000..0266875 --- /dev/null +++ b/test/src/TestEthAbiWriter.cpp @@ -0,0 +1,520 @@ +// Copyright (c) 2022 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + + +#include + +#include + +#include + +#include + +#include "Common.hpp" + + +namespace EclipseMonitor_Test +{ + extern size_t g_numOfTestFile; +} + +using namespace EclipseMonitor_Test; +using namespace EclipseMonitor::Eth; + + +GTEST_TEST(TestEthAbiWriter, CountTestFile) +{ + static auto tmp = ++EclipseMonitor_Test::g_numOfTestFile; + (void)tmp; +} + + +template +static void TestWriterImpl( + const _WriterImpl& writerImpl, + const std::vector& expOutput, + const _ValType& inVal +) +{ + std::vector output; + auto destIt = writerImpl.Write(std::back_inserter(output), inVal); + + EXPECT_EQ(output, expOutput); + + // ensures that the destination iterator is at the last position + *destIt++ = 0x56U; + EXPECT_EQ(output.size(), expOutput.size() + 1); + EXPECT_EQ(output[output.size() - 1], 0x56U); +} + + +template +static void TestWriterImpl( + const _WriterImpl& writerImpl, + const std::vector& expOutput, + _SrcIt begin, + _SrcIt end +) +{ + std::vector output; + auto destIt = writerImpl.Write(std::back_inserter(output), begin, end); + + EXPECT_EQ(output, expOutput); + + // ensures that the destination iterator is at the last position + *destIt++ = 0x56U; + EXPECT_EQ(output.size(), expOutput.size() + 1); + EXPECT_EQ(output[output.size() - 1], 0x56U); +} + + +template +static void TestObjWriter( + const _Writer& writer, + const SimpleObjects::BaseObj& obj, + const std::vector& expHeadOutput, + const std::vector& expTailOutput, + size_t expDataOffsetIncr = 0 +) +{ + std::vector head; + std::vector tail; + + size_t inDataOffset = 0x80U; + size_t resDataOffset = 0; + + auto headDestIt = SimpleObjects::ToOutIt(std::back_inserter(head)); + std::tie(headDestIt, resDataOffset) = + writer.WriteHead(headDestIt, obj, inDataOffset); + + EXPECT_EQ(head, expHeadOutput); + EXPECT_EQ(resDataOffset, inDataOffset + expDataOffsetIncr); + + auto tailDestIt = SimpleObjects::ToOutIt(std::back_inserter(tail)); + tailDestIt = writer.WriteTail(tailDestIt, obj); + + EXPECT_EQ(tail, expTailOutput); + + // ensures that the destination iterator is at the last position + *headDestIt++ = 0x56U; + EXPECT_EQ(head.size(), expHeadOutput.size() + 1); + EXPECT_EQ(head[head.size() - 1], 0x56U); + + *tailDestIt++ = 0x78U; + EXPECT_EQ(tail.size(), expTailOutput.size() + 1); + EXPECT_EQ(tail[tail.size() - 1], 0x78U); +} + + +GTEST_TEST(TestEthAbiWriter, WriteIntegerImpl) +{ + using AbiWriterImplUInt64 = EthInternal::AbiWriterImpl< + SimpleObjects::ObjCategory::Integer, + AbiUInt64 + >; + using AbiWriterUInt64 = AbiWriter< + SimpleObjects::ObjCategory::Integer, + AbiUInt64 + >; + + EXPECT_EQ(AbiWriterImplUInt64().IsDynamicType(), false); + EXPECT_EQ(AbiWriterImplUInt64().GetNumHeadChunks(), 1); + EXPECT_EQ(AbiWriterImplUInt64().GetNumTailChunks(), 0); + + EXPECT_EQ(AbiWriterUInt64().IsDynamicType(), false); + EXPECT_EQ(AbiWriterUInt64().GetNumHeadChunks(), 1); + EXPECT_EQ(AbiWriterUInt64().GetNumTailChunks(SimpleObjects::UInt64()), 0); + + { + std::vector expOutput = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x12U, 0x34U, 0x56U, 0x78U, 0x90U, 0xABU, 0xCDU, 0xEFU, + }; + uint64_t inVal(0x1234567890ABCDEFULL); + SimpleObjects::UInt64 obj(inVal); + + TestWriterImpl(AbiWriterImplUInt64(), expOutput, inVal); + TestObjWriter(AbiWriterUInt64(), obj, expOutput, {}); + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteBoolImpl) +{ + using AbiWriterImplBool = EthInternal::AbiWriterImpl< + SimpleObjects::ObjCategory::Bool + >; + using AbiWriterBool = AbiWriter< + SimpleObjects::ObjCategory::Bool + >; + + EXPECT_EQ(AbiWriterImplBool().IsDynamicType(), false); + EXPECT_EQ(AbiWriterImplBool().GetNumHeadChunks(), 1); + EXPECT_EQ(AbiWriterImplBool().GetNumTailChunks(), 0); + + EXPECT_EQ(AbiWriterBool().IsDynamicType(), false); + EXPECT_EQ(AbiWriterBool().GetNumHeadChunks(), 1); + EXPECT_EQ(AbiWriterBool().GetNumTailChunks(SimpleObjects::Bool()), 0); + + { + std::vector expOutput = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, + }; + bool inVal(true); + SimpleObjects::Bool obj(inVal); + + TestWriterImpl(AbiWriterImplBool(), expOutput, inVal); + TestObjWriter(AbiWriterBool(), obj, expOutput, {}); + } + + { + std::vector expOutput = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + bool inVal(false); + SimpleObjects::Bool obj(inVal); + + TestWriterImpl(AbiWriterImplBool(), expOutput, inVal); + TestObjWriter(AbiWriterBool(), obj, expOutput, {}); + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteStaticBytesImpl) +{ + using AbiWriterImplBytes = EthInternal::AbiWriterImpl< + SimpleObjects::ObjCategory::Bytes, + std::false_type + >; + using AbiWriterBytes = AbiWriter< + SimpleObjects::ObjCategory::Bytes, + std::false_type + >; + using AbiWriterBytes16 = AbiWriter< + SimpleObjects::ObjCategory::Bytes, + AbiSize<16> + >; + + { + AbiWriterImplBytes writerImpl(16); + AbiWriterBytes16 writer; + + EXPECT_EQ(writerImpl.IsDynamicType(), false); + EXPECT_EQ(writerImpl.GetNumHeadChunks(), 1); + EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); + EXPECT_EQ(writerImpl.GetPadSize(), 16); + + EXPECT_EQ(writer.IsDynamicType(), false); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetNumTailChunks(SimpleObjects::Bytes()), 0); + + std::vector expOutput = { + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + SimpleObjects::Bytes inVal = { + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + }; + + TestWriterImpl(writerImpl, expOutput, inVal.AsBytes()); + TestObjWriter(writer, inVal, expOutput, {}); + } + + { + AbiWriterImplBytes writerImpl(32); + AbiWriterBytes writer(32); + + EXPECT_EQ(writerImpl.IsDynamicType(), false); + EXPECT_EQ(writerImpl.GetNumHeadChunks(), 1); + EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); + EXPECT_EQ(writerImpl.GetPadSize(), 0); + + EXPECT_EQ(writer.IsDynamicType(), false); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetNumTailChunks(SimpleObjects::Bytes()), 0); + + std::vector expOutput = { + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + }; + SimpleObjects::Bytes inVal = { + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + }; + + TestWriterImpl(writerImpl, expOutput, inVal.AsBytes()); + TestObjWriter(writer, inVal, expOutput, {}); + } + + // The given bytes length doesn't match the static length + { + AbiWriterImplBytes writerImpl(16); + + std::vector output; + SimpleObjects::Bytes inVal = { + 0x01U, 0x23U, 0x45U, 0x67U, + }; + EXPECT_THROW_MSG( + writerImpl.Write(std::back_inserter(output), inVal.AsBytes());, + EclipseMonitor::Exception, + "ABI writer - the given bytes should have exactly the same " + "length as the static bytes type" + ); + } + + // writer with static length longer than the chunk size + { + EXPECT_THROW( + AbiWriterBytes writer(33), + EclipseMonitor::Exception + ); + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) +{ + using AbiWriterImplBytes = EthInternal::AbiWriterImpl< + SimpleObjects::ObjCategory::Bytes, + std::true_type + >; + + AbiWriterImplBytes writerImpl; + + EXPECT_EQ(writerImpl.IsDynamicType(), true); + + EXPECT_EQ(writerImpl.GetNumHeadChunks(), 1); + + EXPECT_EQ(writerImpl.GetNumDataChunks(0), 0); + EXPECT_EQ(writerImpl.GetNumDataChunks(10), 1); + EXPECT_EQ(writerImpl.GetNumDataChunks(16), 1); + EXPECT_EQ(writerImpl.GetNumDataChunks(32), 1); + EXPECT_EQ(writerImpl.GetNumDataChunks(33), 2); + EXPECT_EQ(writerImpl.GetNumDataChunks(45), 2); + EXPECT_EQ(writerImpl.GetNumDataChunks(64), 2); + EXPECT_EQ(writerImpl.GetNumDataChunks(65), 3); + + EXPECT_EQ(writerImpl.GetNumTailChunks(0), 1); + EXPECT_EQ(writerImpl.GetNumTailChunks(10), 2); + EXPECT_EQ(writerImpl.GetNumTailChunks(16), 2); + EXPECT_EQ(writerImpl.GetNumTailChunks(32), 2); + EXPECT_EQ(writerImpl.GetNumTailChunks(33), 3); + EXPECT_EQ(writerImpl.GetNumTailChunks(45), 3); + EXPECT_EQ(writerImpl.GetNumTailChunks(64), 3); + EXPECT_EQ(writerImpl.GetNumTailChunks(65), 4); + + EXPECT_EQ(writerImpl.GetPadSize(0), 0); + EXPECT_EQ(writerImpl.GetPadSize(10), 22); + EXPECT_EQ(writerImpl.GetPadSize(16), 16); + EXPECT_EQ(writerImpl.GetPadSize(32), 0); + EXPECT_EQ(writerImpl.GetPadSize(33), 31); + EXPECT_EQ(writerImpl.GetPadSize(45), 19); + EXPECT_EQ(writerImpl.GetPadSize(64), 0); + EXPECT_EQ(writerImpl.GetPadSize(65), 31); + + { + std::vector expOutput = { + // len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x34U, + // data + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + // data - continue + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x89U, 0xABU, 0xCDU, 0xEFU, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + SimpleObjects::Bytes inVal = { + // data + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + // data - continue + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x89U, 0xABU, 0xCDU, 0xEFU, + }; + + TestWriterImpl(writerImpl, expOutput, inVal.AsBytes()); + TestWriterImpl( + writerImpl, + expOutput, + inVal.AsBytes().begin(), + inVal.AsBytes().end() + ); + } + + { + std::vector expOutput = { + // len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // data + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + // data - continue + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x89U, 0xABU, 0xCDU, 0xEFU, 0x01U, 0x23U, 0x45U, 0x67U, + 0x67U, 0x45U, 0x23U, 0x01U, 0xEFU, 0xCDU, 0xABU, 0x89U, + }; + SimpleObjects::Bytes inVal = { + // data + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + // data - continue + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x89U, 0xABU, 0xCDU, 0xEFU, 0x01U, 0x23U, 0x45U, 0x67U, + 0x67U, 0x45U, 0x23U, 0x01U, 0xEFU, 0xCDU, 0xABU, 0x89U, + }; + + TestWriterImpl(writerImpl, expOutput, inVal.AsBytes()); + TestWriterImpl( + writerImpl, + expOutput, + inVal.AsBytes().begin(), + inVal.AsBytes().end() + ); + } + + // The given input iterator range is invalid + { + std::vector output; + SimpleObjects::Bytes inVal = { + // data + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + }; + EXPECT_THROW_MSG( + writerImpl.Write( + std::back_inserter(output), + inVal.AsBytes().end(), + inVal.AsBytes().begin() + );, + EclipseMonitor::Exception, + "ABI writer - invalid iterator range for input bytes" + ); + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteStaticListImpl) +{ + using AbiWriterImplUInt64 = EthInternal::AbiWriterImpl< + SimpleObjects::ObjCategory::Integer, + AbiUInt64 + >; + using AbiWriterImplUInt64List = EthInternal::AbiWriterImpl< + SimpleObjects::ObjCategory::List, + AbiWriterImplUInt64, + std::false_type, + std::false_type + >; + + { + AbiWriterImplUInt64List writerImpl(AbiWriterImplUInt64(), 5); + + EXPECT_EQ(writerImpl.IsDynamicType(), false); + + EXPECT_EQ(writerImpl.GetNumHeadChunks(), 5); + EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); + + std::vector expOutput = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x12U, 0x34U, 0x56U, 0x78U, 0x90U, 0xABU, 0xCDU, 0xEFU, + + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0xEFU, 0xCDU, 0xABU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, + + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x11U, 0x22U, 0x33U, 0x44U, 0x55U, 0x66U, 0x77U, 0x88U, + + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x99U, 0x00U, 0xAAU, 0xBBU, 0xCCU, 0xDDU, 0xEEU, 0xFFU, + }; + std::vector inVal = { + (0x1234567890ABCDEFULL), + (0xEFCDAB8967452301ULL), + (0x0123456789ABCDEFULL), + (0x1122334455667788ULL), + (0x9900AABBCCDDEEFFULL), + }; + + TestWriterImpl(writerImpl, expOutput, inVal); + } + + { + AbiWriterImplUInt64List writerImpl(AbiWriterImplUInt64(), 2); + + EXPECT_EQ(writerImpl.IsDynamicType(), false); + + EXPECT_EQ(writerImpl.GetNumHeadChunks(), 2); + EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); + + std::vector expOutput = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x11U, 0x22U, 0x33U, 0x44U, 0x55U, 0x66U, 0x77U, 0x88U, + + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x99U, 0x00U, 0xAAU, 0xBBU, 0xCCU, 0xDDU, 0xEEU, 0xFFU, + }; + std::vector inVal = { + (0x1122334455667788ULL), + (0x9900AABBCCDDEEFFULL), + }; + + TestWriterImpl(writerImpl, expOutput, inVal); + } + +} + From b78ed507f2ee02dd2da7a7f32c56f496fda968fe Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Tue, 21 May 2024 01:31:06 -0700 Subject: [PATCH 05/22] HeadTailWriterBase and dynamic bytes writer --- include/EclipseMonitor/Eth/AbiWriter.hpp | 184 +++++++++++++++++++++++ test/src/TestEthAbiWriter.cpp | 37 +++++ 2 files changed, 221 insertions(+) diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp index 7fdcac4..9ca1b43 100644 --- a/include/EclipseMonitor/Eth/AbiWriter.hpp +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -186,6 +186,11 @@ struct AbiWriterUIntImpl : return 0; } + size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const + { + return GetNumTailChunks(); + } + /** * @brief Write the given integer value to the destination stream * @@ -315,6 +320,11 @@ struct AbiWriterImpl< return 0; } + size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const + { + return GetNumTailChunks(); + } + /** * @brief Write the given bool value to the destination stream * @@ -406,6 +416,11 @@ struct AbiWriterImpl< return 0; } + size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const + { + return GetNumTailChunks(); + } + /** * @brief Write the given bytes to the destination stream * @@ -531,6 +546,16 @@ struct AbiWriterImpl< return 1 + GetNumDataChunks(len); } + size_t GetNumTailChunks(const Internal::Obj::BytesBaseObj& val) const + { + return GetNumTailChunks(val.size()); + } + + size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const + { + return GetNumTailChunks(val.AsBytes()); + } + /** * @brief Get the size of the padding needed to store the dynamic bytes * @@ -612,6 +637,12 @@ struct AbiWriterImpl< return Write(destIt, cnt.begin(), cnt.end(), cnt.size()); } + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + return Write(destIt, val.AsBytes()); + } + private: // DynLenWriterImpl m_dynLenWriter; @@ -683,6 +714,11 @@ struct AbiWriterImpl< return 0; } + size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const + { + return GetNumTailChunks(); + } + template _DestIt Write(_DestIt destIt, _ItemIt begin, _ItemIt end) const { @@ -700,6 +736,12 @@ struct AbiWriterImpl< return Write(destIt, cnt.begin(), cnt.end()); } + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + return Write(destIt, val.AsList()); + } + private: ItemWriter m_itemWriter; @@ -850,6 +892,118 @@ class AbiWriterHeadOnlyBase : public AbiWriterBase }; // class AbiWriterHeadOnlyBase +template +class AbiWriterHeadTailBase : public AbiWriterBase +{ +public: // static members: + + using Base = AbiWriterBase; + using Self = AbiWriterHeadTailBase<_Impl>; + + using WriterImpl = _Impl; + + /** + * @brief The type of the parser used to parse the head part of the ABI data + * NOTE: the ABI spec assume the head is always a uint256 offset, + * but here we assume a uint64 offset for simplicity, and it's + * very unlikely that the offset will be larger than uint64 in + * real-world cases. + * + */ + using HeadWriterImpl = EthInternal::AbiWriterImpl< + Internal::Obj::ObjCategory::Integer, + AbiUInt64 + >; // head is a uint256 offset + + using WriteIterator = typename Base::WriteIterator; + +public: + + AbiWriterHeadTailBase(WriterImpl impl) : + Base(), + m_headWriter(HeadWriterImpl()), + m_impl(std::move(impl)) + {} + + virtual ~AbiWriterHeadTailBase() = default; + + bool IsDynamicType() const override + { + return m_impl.IsDynamicType(); + } + + size_t GetNumHeadChunks() const override + { + return m_impl.GetNumHeadChunks(); + } + + size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const override + { + return m_impl.GetNumTailChunks(val); + } + + virtual + std::tuple< + WriteIterator, + size_t + > + WriteHead( + WriteIterator destIt, + const Internal::Obj::BaseObj& val, + size_t currDataOffset + ) const override + { + if (!IsDynamicType()) + { + // static type - head only + return std::make_tuple( + m_impl.WriteObj(destIt, val), + currDataOffset + ); + } + else + { + // dynamic type - head is a uint representing the data offset + auto headIt = m_headWriter.Write( + destIt, + static_cast(currDataOffset) + ); + + // calculate the new data offset + currDataOffset += ( + m_impl.GetNumTailChunks(val) * + AbiCodecConst::sk_chunkSize() + ); + + return std::make_tuple(headIt, currDataOffset); + } + } + + virtual WriteIterator WriteTail( + WriteIterator destIt, + const Internal::Obj::BaseObj& val + ) const override + { + if (!IsDynamicType()) + { + // static type - no tail chunks + return destIt; + } + else + { + // dynamic type - write the tail chunks + return m_impl.WriteObj(destIt, val); + } + } + +protected: + + HeadWriterImpl m_headWriter; + WriterImpl m_impl; + +}; // class AbiWriterHeadTailBase + + // ========== // AbiWriter general template // ========== @@ -1019,6 +1173,36 @@ struct AbiWriter< // ========== +template<> +struct AbiWriter< + Internal::Obj::ObjCategory::Bytes, + std::true_type // IsDynamic? - true +> : + public AbiWriterHeadTailBase< + EthInternal::AbiWriterImpl< + Internal::Obj::ObjCategory::Bytes, + std::true_type + > + > +{ + using WriterImpl = EthInternal::AbiWriterImpl< + Internal::Obj::ObjCategory::Bytes, + std::true_type + >; + using Base = AbiWriterHeadTailBase; + using Self = AbiWriter; + + AbiWriter() : + Base(WriterImpl()) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + +}; // struct AbiWriter + + // ========== // AbiWriter for list types (T[k]) // ========== diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index 0266875..9523ab0 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -70,6 +70,14 @@ static void TestWriterImpl( } +static const std::vector gsk_testObjWriterHead = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x80U, +}; + + template static void TestObjWriter( const _Writer& writer, @@ -122,6 +130,7 @@ GTEST_TEST(TestEthAbiWriter, WriteIntegerImpl) EXPECT_EQ(AbiWriterImplUInt64().IsDynamicType(), false); EXPECT_EQ(AbiWriterImplUInt64().GetNumHeadChunks(), 1); EXPECT_EQ(AbiWriterImplUInt64().GetNumTailChunks(), 0); + EXPECT_EQ(AbiWriterImplUInt64().GetNumTailChunks(SimpleObjects::UInt64()), 0); EXPECT_EQ(AbiWriterUInt64().IsDynamicType(), false); EXPECT_EQ(AbiWriterUInt64().GetNumHeadChunks(), 1); @@ -155,6 +164,7 @@ GTEST_TEST(TestEthAbiWriter, WriteBoolImpl) EXPECT_EQ(AbiWriterImplBool().IsDynamicType(), false); EXPECT_EQ(AbiWriterImplBool().GetNumHeadChunks(), 1); EXPECT_EQ(AbiWriterImplBool().GetNumTailChunks(), 0); + EXPECT_EQ(AbiWriterImplBool().GetNumTailChunks(SimpleObjects::Bool()), 0); EXPECT_EQ(AbiWriterBool().IsDynamicType(), false); EXPECT_EQ(AbiWriterBool().GetNumHeadChunks(), 1); @@ -212,6 +222,7 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticBytesImpl) EXPECT_EQ(writerImpl.IsDynamicType(), false); EXPECT_EQ(writerImpl.GetNumHeadChunks(), 1); EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); + EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::Bytes()), 0); EXPECT_EQ(writerImpl.GetPadSize(), 16); EXPECT_EQ(writer.IsDynamicType(), false); @@ -240,6 +251,7 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticBytesImpl) EXPECT_EQ(writerImpl.IsDynamicType(), false); EXPECT_EQ(writerImpl.GetNumHeadChunks(), 1); EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); + EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::Bytes()), 0); EXPECT_EQ(writerImpl.GetPadSize(), 0); EXPECT_EQ(writer.IsDynamicType(), false); @@ -295,6 +307,10 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) SimpleObjects::ObjCategory::Bytes, std::true_type >; + using AbiWriterBytes = AbiWriter< + SimpleObjects::ObjCategory::Bytes, + std::true_type + >; AbiWriterImplBytes writerImpl; @@ -319,6 +335,7 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) EXPECT_EQ(writerImpl.GetNumTailChunks(45), 3); EXPECT_EQ(writerImpl.GetNumTailChunks(64), 3); EXPECT_EQ(writerImpl.GetNumTailChunks(65), 4); + EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::Bytes()), 1); EXPECT_EQ(writerImpl.GetPadSize(0), 0); EXPECT_EQ(writerImpl.GetPadSize(10), 22); @@ -329,6 +346,12 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) EXPECT_EQ(writerImpl.GetPadSize(64), 0); EXPECT_EQ(writerImpl.GetPadSize(65), 31); + AbiWriterBytes writer; + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetNumTailChunks(SimpleObjects::Bytes()), 1); + { std::vector expOutput = { // len @@ -366,6 +389,12 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) inVal.AsBytes().begin(), inVal.AsBytes().end() ); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, 3 * AbiCodecConst::sk_chunkSize() + ); } { @@ -406,6 +435,12 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) inVal.AsBytes().begin(), inVal.AsBytes().end() ); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, 3 * AbiCodecConst::sk_chunkSize() + ); } // The given input iterator range is invalid @@ -451,6 +486,7 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticListImpl) EXPECT_EQ(writerImpl.GetNumHeadChunks(), 5); EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); + EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::List()), 0); std::vector expOutput = { 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, @@ -496,6 +532,7 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticListImpl) EXPECT_EQ(writerImpl.GetNumHeadChunks(), 2); EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); + EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::List()), 0); std::vector expOutput = { 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, From acd441208ca56bc7f941c51f51b5ecdc8b99fabd Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Tue, 21 May 2024 02:20:50 -0700 Subject: [PATCH 06/22] ABI Writer for list with const len --- include/EclipseMonitor/Eth/AbiWriter.hpp | 333 ++++++++++++++++++----- test/src/TestEthAbiWriter.cpp | 228 ++++++++++++++-- 2 files changed, 473 insertions(+), 88 deletions(-) diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp index 9ca1b43..06f8ddb 100644 --- a/include/EclipseMonitor/Eth/AbiWriter.hpp +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -650,104 +650,240 @@ struct AbiWriterImpl< }; // struct AbiWriterImpl -// ========== -// AbiWriterImpl for T[k] types, where T is static type -// ========== - - +/** + * @brief This is one of the classes used to manage the nested writers for a + * type that has nested types; + * for example, T[k] types, or tuple (T1, T2, ..., Tn) types. + * And this class is used to manage the nested writers for T[k] types, + * which only have a single type, T, as the nested type. + * + * @tparam _ItemWriter + */ template -struct AbiWriterImpl< - Internal::Obj::ObjCategory::List, - _ItemWriter, - std::false_type, // IsLenDynamic? - false - std::false_type // IsItemDynamic? - false -> : - public AbiCodecImpl< - Internal::Obj::ObjCategory::List, - typename _ItemWriter::Codec, - std::false_type, - std::false_type - > +struct AbiNestedWriterMgrList { - using Base = AbiCodecImpl< - Internal::Obj::ObjCategory::List, - typename _ItemWriter::Codec, - std::false_type, - std::false_type - >; - using Self = AbiWriterImpl< - Internal::Obj::ObjCategory::List, - _ItemWriter, - std::false_type, - std::false_type - >; - - using Codec = Base; using ItemWriter = _ItemWriter; - AbiWriterImpl(ItemWriter itemWriter, size_t size) : + AbiNestedWriterMgrList( + ItemWriter itemWriter, + size_t size + ) : m_itemWriter(std::move(itemWriter)), m_size(size) {} - ~AbiWriterImpl() = default; + AbiNestedWriterMgrList(std::pair pair) : + AbiNestedWriterMgrList(std::move(pair.first), pair.second) + {} + + ~AbiNestedWriterMgrList() = default; + + bool IsDynamicType() const + { + return m_itemWriter.IsDynamicType(); + } /** - * @brief Get the number of head chunks required to store the list - * NOTE: The static list always stores all data at head chunks + * @brief Get the total number of head chunks of nested types, + * NO MATTER IF THIS TYPE IS DYNAMIC OR STATIC * - * @return The number of chunks required + * @return The total number of head chunks */ - size_t GetNumHeadChunks() const + size_t GetTotalNumHeadChunks() const { return m_size * m_itemWriter.GetNumHeadChunks(); } - /** - * @brief Get the number of tail chunks required to store the list - * NOTE: The static list has no tail chunks - * - * @return The number of chunks required - */ - size_t GetNumTailChunks() const + template + void IterateVals(_Func func, _ValIt begin, _ValIt end) const { - return 0; + size_t size = m_size; + for(auto it = begin; it != end; ++it) + { + if (size == 0) + { + throw Exception( + "ABI writer - too many given data than declared" + ); + } + func(m_itemWriter, *it); + --size; + } + if (size != 0) + { + throw Exception( + "ABI writer - the number of given data is less than declared" + ); + } } - size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const + ItemWriter m_itemWriter; + size_t m_size; + +}; // struct AbiNestedWriterMgrList + + +template +struct AbiWriterNestedTypeConstLenImpl +{ + + using NestedWriterMgr = _NestedWriterMgr; + + AbiWriterNestedTypeConstLenImpl(NestedWriterMgr nestedWriterMgr) : + m_nestedWriterMgr(std::move(nestedWriterMgr)) + {} + + ~AbiWriterNestedTypeConstLenImpl() = default; + + bool IsDynamicType() const { - return GetNumTailChunks(); + return m_nestedWriterMgr.IsDynamicType(); } - template - _DestIt Write(_DestIt destIt, _ItemIt begin, _ItemIt end) const + size_t GetNumHeadChunks() const { - for (auto it = begin; it != end; ++it) + if (!IsDynamicType()) { - destIt = m_itemWriter.Write(destIt, *it); + // static type - everything is stored at head chunks + return m_nestedWriterMgr.GetTotalNumHeadChunks(); + } + else + { + // dynamic type - the head chunk stores the offset of the data + return 1; } - - return destIt; } - template - _DestIt Write(_DestIt destIt, const _CntT& cnt) const + struct NumTailChunksFunctor { - return Write(destIt, cnt.begin(), cnt.end()); + NumTailChunksFunctor(size_t& size) : + m_size(size) + {} + + ~NumTailChunksFunctor() = default; + + template + void operator()(const _Writer& writer, const _ValType& val) + { + m_size += writer.GetNumTailChunks(val); + } + + size_t& m_size; + }; // struct NumTailChunksFunctor + + template + size_t GetNumTailChunks(_It begin, _It end) const + { + if (!IsDynamicType()) + { + // static type - no tail chunks + return 0; + } + else + { + // dynamic type - the tail is + // sub_head_1, sub_head_2, ..., sub_head_n, + // sub_tail_1, sub_tail_2, ..., sub_tail_n + size_t numTailChunks = m_nestedWriterMgr.GetTotalNumHeadChunks(); + + m_nestedWriterMgr.IterateVals( + NumTailChunksFunctor(numTailChunks), + begin, + end + ); + + return numTailChunks; + } } template - _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + struct WriteHeadsFunctor { - return Write(destIt, val.AsList()); + WriteHeadsFunctor(_DestIt& destIt, size_t& dataOffset) : + m_destIt(destIt), + m_dataOffset(dataOffset) + {} + + template + void operator()(const _Writer& writer, const _ValType& val) + { + std::tie(m_destIt, m_dataOffset) = + writer.WriteHead(m_destIt, val, m_dataOffset); + } + + _DestIt& m_destIt; + size_t& m_dataOffset; + }; // struct WriteHeadsFunctor + + template + struct WriteTailsFunctor + { + WriteTailsFunctor(_DestIt& destIt) : + m_destIt(destIt) + {} + + template + void operator()(const _Writer& writer, const _ValType& val) + { + m_destIt = writer.WriteTail(m_destIt, val); + } + + _DestIt& m_destIt; + }; // struct WriteTailsFunctor + + template + _DestIt Write(_DestIt destIt, _SrcIt begin, _SrcIt end) const + { + // calculate the offset of the data area + size_t dataOffset = + m_nestedWriterMgr.GetTotalNumHeadChunks() * + AbiCodecConst::sk_chunkSize(); + + // write all head chunks + m_nestedWriterMgr.IterateVals( + WriteHeadsFunctor<_DestIt>(destIt, dataOffset), + begin, + end + ); + + // write all tail chunks + m_nestedWriterMgr.IterateVals( + WriteTailsFunctor<_DestIt>(destIt), + begin, + end + ); + + return destIt; } -private: + NestedWriterMgr m_nestedWriterMgr; - ItemWriter m_itemWriter; - size_t m_size; +}; // struct AbiWriterNestedTypeConstLenImpl -}; // struct AbiWriterImpl + +template +struct AbiWriterNestedListConstLenImpl : + public AbiWriterNestedTypeConstLenImpl<_NestedWriterMgr> +{ + using NestedWriterMgr = _NestedWriterMgr; + using Base = AbiWriterNestedTypeConstLenImpl<_NestedWriterMgr>; + + using Base::Base; + + ~AbiWriterNestedListConstLenImpl() = default; + + size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const + { + const auto& list = val.AsList(); + return Base::GetNumTailChunks(list.begin(), list.end()); + } + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + const auto& list = val.AsList(); + return Base::Write(destIt, list.begin(), list.end()); + } +}; // struct AbiWriterNestedListConstLenImpl // ========== @@ -1208,6 +1344,79 @@ struct AbiWriter< // ========== +template +struct AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::false_type // IsLenDynamic? - false +> : + public AbiWriterHeadTailBase< + EthInternal::AbiWriterNestedListConstLenImpl< + EthInternal::AbiNestedWriterMgrList<_ItemWriter> + > + > +{ + using NestedWritersMgr = EthInternal::AbiNestedWriterMgrList<_ItemWriter>; + using WriterImpl = + EthInternal::AbiWriterNestedListConstLenImpl; + using Base = AbiWriterHeadTailBase; + using Self = AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::false_type + >; + + AbiWriter(_ItemWriter itemWriter, size_t size) : + Base( + WriterImpl( + NestedWritersMgr(std::make_pair(itemWriter, size)) + ) + ) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + +}; // struct AbiParser + + +template +struct AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::integral_constant +> : + public AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::false_type + > +{ + using Base = AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::false_type + >; + using Self = AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::integral_constant + >; + + static constexpr size_t sk_size = _Size; + + AbiWriter(_ItemWriter itemWriter) : + Base(itemWriter, sk_size) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + +}; // struct AbiWriter + + // ========== // AbiWriter for list types (T[]) // ========== diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index 9523ab0..62c7c0d 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -466,27 +466,29 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) } -GTEST_TEST(TestEthAbiWriter, WriteStaticListImpl) +GTEST_TEST(TestEthAbiWriter, WriteStaticList) { - using AbiWriterImplUInt64 = EthInternal::AbiWriterImpl< + using AbiWriterUint64 = AbiWriter< SimpleObjects::ObjCategory::Integer, AbiUInt64 >; - using AbiWriterImplUInt64List = EthInternal::AbiWriterImpl< + using AbiWriterListUint64 = AbiWriter< SimpleObjects::ObjCategory::List, - AbiWriterImplUInt64, - std::false_type, + AbiWriterUint64, std::false_type >; + using AbiWriterListUint64_2 = AbiWriter< + SimpleObjects::ObjCategory::List, + AbiWriterUint64, + AbiSize<2> + >; { - AbiWriterImplUInt64List writerImpl(AbiWriterImplUInt64(), 5); + AbiWriterListUint64 writer(AbiWriterUint64(), 5); - EXPECT_EQ(writerImpl.IsDynamicType(), false); + EXPECT_EQ(writer.IsDynamicType(), false); - EXPECT_EQ(writerImpl.GetNumHeadChunks(), 5); - EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); - EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::List()), 0); + EXPECT_EQ(writer.GetNumHeadChunks(), 5); std::vector expOutput = { 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, @@ -514,25 +516,25 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticListImpl) 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x99U, 0x00U, 0xAAU, 0xBBU, 0xCCU, 0xDDU, 0xEEU, 0xFFU, }; - std::vector inVal = { - (0x1234567890ABCDEFULL), - (0xEFCDAB8967452301ULL), - (0x0123456789ABCDEFULL), - (0x1122334455667788ULL), - (0x9900AABBCCDDEEFFULL), + SimpleObjects::List inVal = { + SimpleObjects::UInt64(0x1234567890ABCDEFULL), + SimpleObjects::UInt64(0xEFCDAB8967452301ULL), + SimpleObjects::UInt64(0x0123456789ABCDEFULL), + SimpleObjects::UInt64(0x1122334455667788ULL), + SimpleObjects::UInt64(0x9900AABBCCDDEEFFULL), }; - TestWriterImpl(writerImpl, expOutput, inVal); + EXPECT_EQ(writer.GetNumTailChunks(inVal), 0); + TestObjWriter(writer, inVal, expOutput, {}); } { - AbiWriterImplUInt64List writerImpl(AbiWriterImplUInt64(), 2); + AbiWriterUint64 innerWriter; + AbiWriterListUint64_2 writer(innerWriter); - EXPECT_EQ(writerImpl.IsDynamicType(), false); + EXPECT_EQ(writer.IsDynamicType(), false); - EXPECT_EQ(writerImpl.GetNumHeadChunks(), 2); - EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); - EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::List()), 0); + EXPECT_EQ(writer.GetNumHeadChunks(), 2); std::vector expOutput = { 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, @@ -545,13 +547,187 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticListImpl) 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x99U, 0x00U, 0xAAU, 0xBBU, 0xCCU, 0xDDU, 0xEEU, 0xFFU, }; - std::vector inVal = { - (0x1122334455667788ULL), - (0x9900AABBCCDDEEFFULL), + SimpleObjects::ListT inVal = { + SimpleObjects::UInt64(0x1122334455667788ULL), + SimpleObjects::UInt64(0x9900AABBCCDDEEFFULL), + }; + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 0); + TestObjWriter(writer, inVal, expOutput, {}); + } + + // Too many values given in the list + { + AbiWriterListUint64 writer(AbiWriterUint64(), 1); + SimpleObjects::ListT inVal = { + SimpleObjects::UInt64(0x1122334455667788ULL), + SimpleObjects::UInt64(0x9900AABBCCDDEEFFULL), + }; + std::vector output; + auto destIt = SimpleObjects::ToOutIt(std::back_inserter(output)); + EXPECT_THROW_MSG( + writer.WriteHead(destIt, inVal, 0);, + EclipseMonitor::Exception, + "ABI writer - too many given data than declared" + ); + } + + // Too few values given in the list + { + AbiWriterListUint64 writer(AbiWriterUint64(), 1); + SimpleObjects::ListT inVal = { + }; + std::vector output; + auto destIt = SimpleObjects::ToOutIt(std::back_inserter(output)); + EXPECT_THROW_MSG( + writer.WriteHead(destIt, inVal, 0);, + EclipseMonitor::Exception, + "ABI writer - the number of given data is less than declared" + ); + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteDynListConstLen) +{ + using AbiWriterBytes = AbiWriter< + SimpleObjects::ObjCategory::Bytes, + std::true_type + >; + using AbiWriterListBytes = AbiWriter< + SimpleObjects::ObjCategory::List, + AbiWriterBytes, + std::false_type + >; + using AbiWriterListBytes2 = AbiWriter< + SimpleObjects::ObjCategory::List, + AbiWriterBytes, + AbiSize<2> + >; + + { + AbiWriterListBytes writer(AbiWriterBytes(), 1); + + EXPECT_EQ(writer.IsDynamicType(), true); + + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + std::vector expOutput = { + // item 1 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x20U, + // data area + // item 1 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 1 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + SimpleObjects::List inVal = { + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}), + }; + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 3); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 3 * AbiCodecConst::sk_chunkSize() + ); + } + + { + AbiWriterBytes innerWriter; + AbiWriterListBytes2 writer(innerWriter); + + EXPECT_EQ(writer.IsDynamicType(), true); + + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + std::vector expOutput = { + // item 1 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x80U, + // data area + // item 1 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 1 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // item 2 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x09U, + // item 2 - bytes + 0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + SimpleObjects::ListT inVal = { + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}), + SimpleObjects::Bytes({0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U}), }; - TestWriterImpl(writerImpl, expOutput, inVal); + EXPECT_EQ(writer.GetNumTailChunks(inVal), 6); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); } + // Too many values given in the list + { + AbiWriterListBytes writer(AbiWriterBytes(), 1); + SimpleObjects::List inVal = { + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}), + SimpleObjects::Bytes({0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U}), + }; + std::vector output; + auto destIt = SimpleObjects::ToOutIt(std::back_inserter(output)); + EXPECT_THROW_MSG( + writer.WriteHead(destIt, inVal, 0);, + EclipseMonitor::Exception, + "ABI writer - too many given data than declared" + ); + } + + // Too few values given in the list + { + AbiWriterListBytes writer(AbiWriterBytes(), 1); + SimpleObjects::List inVal = { + }; + std::vector output; + auto destIt = SimpleObjects::ToOutIt(std::back_inserter(output)); + EXPECT_THROW_MSG( + writer.WriteHead(destIt, inVal, 0);, + EclipseMonitor::Exception, + "ABI writer - the number of given data is less than declared" + ); + } } From 15b9be94d1ea315d3bd5c5d3cb654e7fa732ede9 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Tue, 21 May 2024 19:21:34 -0700 Subject: [PATCH 07/22] Added ABI writer for list with dynamic length --- include/EclipseMonitor/Eth/AbiWriter.hpp | 339 ++++++++++++++++++----- test/src/TestEthAbiWriter.cpp | 238 +++++++++++++++- 2 files changed, 510 insertions(+), 67 deletions(-) diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp index 06f8ddb..c72f8fe 100644 --- a/include/EclipseMonitor/Eth/AbiWriter.hpp +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -650,37 +650,87 @@ struct AbiWriterImpl< }; // struct AbiWriterImpl +/** + * @brief This is one of the classes used to manage the nested writers for a + * type that has nested types; + * for example, T[] types. + * And this class is used to manage the nested writers for T[] types, + * which only have a single type, T, as the nested type, and the length + * is dynamic. + * + * @tparam _ItemWriter + */ +template +struct AbiNestedWriterMgrListDynLen +{ + using ItemWriter = _ItemWriter; + + AbiNestedWriterMgrListDynLen(ItemWriter itemWriter) : + m_itemWriter(std::move(itemWriter)) + {} + + ~AbiNestedWriterMgrListDynLen() = default; + + /** + * @brief Get the total number of head chunks of nested types, + * NO MATTER IF THIS TYPE IS DYNAMIC OR STATIC + * + * @param len The length of the list + * + * @return The total number of head chunks + */ + size_t GetTotalNumHeadChunks(size_t len) const + { + return len * m_itemWriter.GetNumHeadChunks(); + } + + template + void IterateVals(_Func func, _ValIt begin, _ValIt end) const + { + for(auto it = begin; it != end; ++it) + { + func(m_itemWriter, *it); + } + } + + ItemWriter m_itemWriter; + +}; // struct AbiNestedWriterMgrListDynLen + + /** * @brief This is one of the classes used to manage the nested writers for a * type that has nested types; * for example, T[k] types, or tuple (T1, T2, ..., Tn) types. * And this class is used to manage the nested writers for T[k] types, - * which only have a single type, T, as the nested type. + * which only have a single type, T, as the nested type; and the length + * is constant. * * @tparam _ItemWriter */ template -struct AbiNestedWriterMgrList +struct AbiNestedWriterMgrListConstLen : + public AbiNestedWriterMgrListDynLen<_ItemWriter> { using ItemWriter = _ItemWriter; - AbiNestedWriterMgrList( - ItemWriter itemWriter, - size_t size - ) : - m_itemWriter(std::move(itemWriter)), + using Base = AbiNestedWriterMgrListDynLen; + using Self = AbiNestedWriterMgrListConstLen; + + AbiNestedWriterMgrListConstLen(ItemWriter itemWriter, size_t size) : + Base(std::move(itemWriter)), m_size(size) {} - AbiNestedWriterMgrList(std::pair pair) : - AbiNestedWriterMgrList(std::move(pair.first), pair.second) + AbiNestedWriterMgrListConstLen(std::pair pair) : + AbiNestedWriterMgrListConstLen(std::move(pair.first), pair.second) {} - ~AbiNestedWriterMgrList() = default; + ~AbiNestedWriterMgrListConstLen() = default; bool IsDynamicType() const { - return m_itemWriter.IsDynamicType(); + return (Base::m_itemWriter).IsDynamicType(); } /** @@ -691,7 +741,7 @@ struct AbiNestedWriterMgrList */ size_t GetTotalNumHeadChunks() const { - return m_size * m_itemWriter.GetNumHeadChunks(); + return Base::GetTotalNumHeadChunks(m_size); } template @@ -706,7 +756,7 @@ struct AbiNestedWriterMgrList "ABI writer - too many given data than declared" ); } - func(m_itemWriter, *it); + func((Base::m_itemWriter), *it); --size; } if (size != 0) @@ -717,41 +767,40 @@ struct AbiNestedWriterMgrList } } - ItemWriter m_itemWriter; size_t m_size; -}; // struct AbiNestedWriterMgrList +}; // struct AbiNestedWriterMgrListConstLen template -struct AbiWriterNestedTypeConstLenImpl +struct AbiWriterNestedTypeDynLenImpl { using NestedWriterMgr = _NestedWriterMgr; - AbiWriterNestedTypeConstLenImpl(NestedWriterMgr nestedWriterMgr) : + using DynLenWriterImpl = AbiWriterImpl< + Internal::Obj::ObjCategory::Integer, + AbiUInt64 + >; + + AbiWriterNestedTypeDynLenImpl(NestedWriterMgr nestedWriterMgr) : + m_dynLenWriter(), m_nestedWriterMgr(std::move(nestedWriterMgr)) {} - ~AbiWriterNestedTypeConstLenImpl() = default; + ~AbiWriterNestedTypeDynLenImpl() = default; bool IsDynamicType() const { - return m_nestedWriterMgr.IsDynamicType(); + // List with dynamic length is always dynamic type + return true; } size_t GetNumHeadChunks() const { - if (!IsDynamicType()) - { - // static type - everything is stored at head chunks - return m_nestedWriterMgr.GetTotalNumHeadChunks(); - } - else - { - // dynamic type - the head chunk stores the offset of the data - return 1; - } + // List with dynamic length is always dynamic type + // dynamic type - the head chunk stores the offset of the data + return 1; } struct NumTailChunksFunctor @@ -772,28 +821,47 @@ struct AbiWriterNestedTypeConstLenImpl }; // struct NumTailChunksFunctor template - size_t GetNumTailChunks(_It begin, _It end) const + size_t SumNumTailChunks( + size_t initialOffset, + _It begin, + _It end + ) const { - if (!IsDynamicType()) - { - // static type - no tail chunks - return 0; - } - else - { - // dynamic type - the tail is - // sub_head_1, sub_head_2, ..., sub_head_n, - // sub_tail_1, sub_tail_2, ..., sub_tail_n - size_t numTailChunks = m_nestedWriterMgr.GetTotalNumHeadChunks(); + // dynamic type - the tail is + // sub_head_1, sub_head_2, ..., sub_head_n, + // sub_tail_1, sub_tail_2, ..., sub_tail_n + size_t numTailChunks = initialOffset; - m_nestedWriterMgr.IterateVals( - NumTailChunksFunctor(numTailChunks), - begin, - end - ); + m_nestedWriterMgr.IterateVals( + NumTailChunksFunctor(numTailChunks), + begin, + end + ); - return numTailChunks; - } + return numTailChunks; + } + + template + size_t GetNumTailChunks( + size_t len, + _It begin, + _It end + ) const + { + // List with dynamic length is always dynamic type + // dynamic type - the tail is + // len_of_list, + // sub_head_1, sub_head_2, ..., sub_head_n, + // sub_tail_1, sub_tail_2, ..., sub_tail_n + size_t initialOffset = + 1 + // the length of the list + m_nestedWriterMgr.GetTotalNumHeadChunks(len); + + return SumNumTailChunks( + initialOffset, + begin, + end + ); } template @@ -832,12 +900,15 @@ struct AbiWriterNestedTypeConstLenImpl }; // struct WriteTailsFunctor template - _DestIt Write(_DestIt destIt, _SrcIt begin, _SrcIt end) const + _DestIt WriteHeadsAndTails( + _DestIt destIt, + size_t initialOffset, + _SrcIt begin, + _SrcIt end + ) const { // calculate the offset of the data area - size_t dataOffset = - m_nestedWriterMgr.GetTotalNumHeadChunks() * - AbiCodecConst::sk_chunkSize(); + size_t dataOffset = initialOffset; // write all head chunks m_nestedWriterMgr.IterateVals( @@ -856,12 +927,95 @@ struct AbiWriterNestedTypeConstLenImpl return destIt; } + template + _DestIt Write(_DestIt destIt, size_t len, _SrcIt begin, _SrcIt end) const + { + uint64_t len64 = static_cast(len); + m_dynLenWriter.Write(destIt, len64); + + // calculate the offset of the data area + size_t dataOffset = + m_nestedWriterMgr.GetTotalNumHeadChunks(len) * + AbiCodecConst::sk_chunkSize(); + + return WriteHeadsAndTails(destIt, dataOffset, begin, end); + } + + DynLenWriterImpl m_dynLenWriter; NestedWriterMgr m_nestedWriterMgr; -}; // struct AbiWriterNestedTypeConstLenImpl +}; // struct AbiWriterNestedTypeDynLenImpl template +struct AbiWriterNestedTypeConstLenImpl : + public AbiWriterNestedTypeDynLenImpl<_NestedWriterMgr> +{ + + using Base = AbiWriterNestedTypeDynLenImpl<_NestedWriterMgr>; + using Self = AbiWriterNestedTypeConstLenImpl<_NestedWriterMgr>; + + using NestedWriterMgr = _NestedWriterMgr; + + AbiWriterNestedTypeConstLenImpl(NestedWriterMgr nestedWriterMgr) : + Base(std::move(nestedWriterMgr)) + {} + + ~AbiWriterNestedTypeConstLenImpl() = default; + + bool IsDynamicType() const + { + return (Base::m_nestedWriterMgr).IsDynamicType(); + } + + size_t GetNumHeadChunks() const + { + if (!IsDynamicType()) + { + // static type - everything is stored at head chunks + return (Base::m_nestedWriterMgr).GetTotalNumHeadChunks(); + } + else + { + // dynamic type - the head chunk stores the offset of the data + return 1; + } + } + + template + size_t GetNumTailChunks(_It begin, _It end) const + { + if (!IsDynamicType()) + { + // static type - no tail chunks + return 0; + } + else + { + // dynamic type - let base class's function to handle it + return Base::SumNumTailChunks( + (Base::m_nestedWriterMgr).GetTotalNumHeadChunks(), + begin, + end + ); + } + } + + template + _DestIt Write(_DestIt destIt, _SrcIt begin, _SrcIt end) const + { + // calculate the offset of the data area + size_t dataOffset = + (Base::m_nestedWriterMgr).GetTotalNumHeadChunks() * + AbiCodecConst::sk_chunkSize(); + + return Base::WriteHeadsAndTails(destIt, dataOffset, begin, end); + } + +}; // struct AbiWriterNestedTypeConstLenImpl + + +template struct AbiWriterNestedListConstLenImpl : public AbiWriterNestedTypeConstLenImpl<_NestedWriterMgr> { @@ -877,6 +1031,7 @@ struct AbiWriterNestedListConstLenImpl : const auto& list = val.AsList(); return Base::GetNumTailChunks(list.begin(), list.end()); } + template _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const { @@ -886,19 +1041,30 @@ struct AbiWriterNestedListConstLenImpl : }; // struct AbiWriterNestedListConstLenImpl -// ========== -// AbiWriterImpl for T[k] types, where T is dynamic type -// ========== +template +struct AbiWriterNestedListDynLenImpl : + public AbiWriterNestedTypeDynLenImpl<_NestedWriterMgr> +{ + using NestedWriterMgr = _NestedWriterMgr; + using Base = AbiWriterNestedTypeDynLenImpl<_NestedWriterMgr>; + using Base::Base; -// ========== -// AbiWriterImpl for T[] types, where T is static type -// ========== + ~AbiWriterNestedListDynLenImpl() = default; + size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const + { + const auto& list = val.AsList(); + return Base::GetNumTailChunks(list.size(), list.begin(), list.end()); + } -// ========== -// AbiWriterImpl for T[] types, where T is dynamic type -// ========== + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + const auto& list = val.AsList(); + return Base::Write(destIt, list.size(), list.begin(), list.end()); + } +}; // struct AbiWriterNestedListDynLenImpl // ========== @@ -1352,13 +1518,15 @@ struct AbiWriter< > : public AbiWriterHeadTailBase< EthInternal::AbiWriterNestedListConstLenImpl< - EthInternal::AbiNestedWriterMgrList<_ItemWriter> + EthInternal::AbiNestedWriterMgrListConstLen<_ItemWriter> > > { - using NestedWritersMgr = EthInternal::AbiNestedWriterMgrList<_ItemWriter>; - using WriterImpl = - EthInternal::AbiWriterNestedListConstLenImpl; + using NestedWritersMgr = + EthInternal::AbiNestedWriterMgrListConstLen<_ItemWriter>; + using WriterImpl = EthInternal::AbiWriterNestedListConstLenImpl< + NestedWritersMgr + >; using Base = AbiWriterHeadTailBase; using Self = AbiWriter< Internal::Obj::ObjCategory::List, @@ -1422,6 +1590,45 @@ struct AbiWriter< // ========== +template +struct AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::true_type // IsLenDynamic? - true +> : + public AbiWriterHeadTailBase< + EthInternal::AbiWriterNestedListDynLenImpl< + EthInternal::AbiNestedWriterMgrListDynLen<_ItemWriter> + > + > +{ + using NestedWritersMgr = + EthInternal::AbiNestedWriterMgrListDynLen<_ItemWriter>; + using WriterImpl = EthInternal::AbiWriterNestedListDynLenImpl< + NestedWritersMgr + >; + using Base = AbiWriterHeadTailBase; + using Self = AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::true_type + >; + + AbiWriter(_ItemWriter itemWriter) : + Base( + WriterImpl( + NestedWritersMgr(itemWriter) + ) + ) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + +}; // struct AbiParser + + } // namespace Eth } // namespace EclipseMonitor diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index 62c7c0d..b5ba5f5 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -466,7 +466,7 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) } -GTEST_TEST(TestEthAbiWriter, WriteStaticList) +GTEST_TEST(TestEthAbiWriter, WriteStaticListConstLen) { using AbiWriterUint64 = AbiWriter< SimpleObjects::ObjCategory::Integer, @@ -731,3 +731,239 @@ GTEST_TEST(TestEthAbiWriter, WriteDynListConstLen) } } + +GTEST_TEST(TestEthAbiWriter, WriteStaticListDynLen) +{ + using AbiWriterUint64 = AbiWriter< + SimpleObjects::ObjCategory::Integer, + AbiUInt64 + >; + using AbiWriterListUint64 = AbiWriter< + SimpleObjects::ObjCategory::List, + AbiWriterUint64, + std::true_type + >; + + AbiWriterUint64 innerWriter; + AbiWriterListUint64 writer(innerWriter); + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + { + std::vector expOutput = { + // length of the list + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U, + // first item + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x12U, 0x34U, 0x56U, 0x78U, 0x90U, 0xabU, 0xcdU, 0xefU, + // second item + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0xefU, 0xcdU, 0xabU, 0x89U, 0x67U, 0x45U, 0x23U, 0x01U, + }; + SimpleObjects::List inVal = { + SimpleObjects::UInt64(0x1234567890ABCDEFULL), + SimpleObjects::UInt64(0xEFCDAB8967452301ULL), + }; + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 3); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 3 * AbiCodecConst::sk_chunkSize() + ); + } + + { + std::vector expOutput = { + // length of the list + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, + // first item + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x12U, 0x34U, 0x56U, 0x78U, 0x90U, 0xabU, 0xcdU, 0xefU, + }; + SimpleObjects::ListT inVal = { + SimpleObjects::UInt64(0x1234567890ABCDEFULL), + }; + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 2); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 2 * AbiCodecConst::sk_chunkSize() + ); + } + + // a list with no elements + { + std::vector expOutput = { + // length of the list + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + SimpleObjects::ListT inVal = { + }; + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 1); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 1 * AbiCodecConst::sk_chunkSize() + ); + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteDynListDynLen) +{ + using AbiWriterBytes = AbiWriter< + SimpleObjects::ObjCategory::Bytes, + std::true_type + >; + using AbiWriterListUint64 = AbiWriter< + SimpleObjects::ObjCategory::List, + AbiWriterBytes, + std::true_type + >; + + AbiWriterBytes innerWriter; + AbiWriterListUint64 writer(innerWriter); + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + { + std::vector expOutput = { + // length of the list + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, + // item 1 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x20U, + // data area + // item 1 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 1 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + SimpleObjects::List inVal = { + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}), + }; + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 4); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 4 * AbiCodecConst::sk_chunkSize() + ); + } + + { + std::vector expOutput = { + // length of the list + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U, + // item 1 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x80U, + // data area + // item 1 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 1 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // item 2 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x09U, + // item 2 - bytes + 0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + SimpleObjects::ListT inVal = { + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}), + SimpleObjects::Bytes({0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U}), + }; + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 7); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 7 * AbiCodecConst::sk_chunkSize() + ); + } + + // a list with no elements + { + std::vector expOutput = { + // length of the list + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + SimpleObjects::List inVal = { + }; + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 1); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 1 * AbiCodecConst::sk_chunkSize() + ); + } +} + From a7e6ec664fb2d9bc7de7ba0fca9f2494d3b5c6be Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Wed, 22 May 2024 03:19:00 -0700 Subject: [PATCH 08/22] Added ABI writer for tuple types --- include/EclipseMonitor/Eth/AbiWriter.hpp | 502 +++++++++++++++++++++-- test/src/TestEthAbiWriter.cpp | 453 +++++++++++++++++++- 2 files changed, 925 insertions(+), 30 deletions(-) diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp index c72f8fe..6977152 100644 --- a/include/EclipseMonitor/Eth/AbiWriter.hpp +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -772,6 +772,132 @@ struct AbiNestedWriterMgrListConstLen : }; // struct AbiNestedWriterMgrListConstLen +/** + * @brief This is one of the classes used to manage the nested writers for a + * type that has nested types; + * for example, T[] types. + * And this class is used to manage the nested writers for + * (T1, T2, ..., Tn) types, which have multiple + * nested type (T1, T2, ..., Tn). + * + * @tparam _ItemWriters A series of nested writers for the nested types + */ +template +struct AbiNestedWriterMgrTuple; + + +template<> +struct AbiNestedWriterMgrTuple<> +{ + AbiNestedWriterMgrTuple() : + m_size(0), + m_isDynamic(false), // tuple with no nested types is static type + m_totalNumHeadChunks(0) + {} + + ~AbiNestedWriterMgrTuple() = default; + + bool IsDynamicType() const + { + return m_isDynamic; + } + + size_t GetTotalNumHeadChunks() const + { + return m_totalNumHeadChunks; + } + + template + void IterateVals(_Func, _ValIt begin, _ValIt end) const + { + if (begin != end) + { + throw Exception( + "ABI writer - too many given data than declared" + ); + } + } + + template + void ApplyVals(_Func&&) const + {} + + size_t m_size; + bool m_isDynamic; + size_t m_totalNumHeadChunks; + +}; // struct AbiNestedWriterMgrTuple<> + + +template +struct AbiNestedWriterMgrTuple<_ItemWriter, _ItemWriters...> +{ + using ItemWriter = _ItemWriter; + + AbiNestedWriterMgrTuple(ItemWriter&& itemWriter, _ItemWriters&&... itemWriters) : + m_size(1 + sizeof...(_ItemWriters)), + m_itemWriter(std::forward(itemWriter)), + m_next(std::forward<_ItemWriters>(itemWriters)...), + m_isDynamic(m_itemWriter.IsDynamicType() || m_next.IsDynamicType()), + m_totalNumHeadChunks( + m_itemWriter.GetNumHeadChunks() + m_next.GetTotalNumHeadChunks() + ) + {} + + ~AbiNestedWriterMgrTuple() = default; + + bool IsDynamicType() const + { + return m_isDynamic; + } + + /** + * @brief Get the total number of head chunks of nested types, + * NO MATTER IF THIS TYPE IS DYNAMIC OR STATIC + * + * @return The total number of head chunks + */ + size_t GetTotalNumHeadChunks() const + { + return m_totalNumHeadChunks; + } + + template + void IterateVals(_Func func, _ValIt begin, _ValIt end) const + { + if (begin == end) + { + throw Exception( + "ABI writer - the number of given data is less than declared" + ); + } + func(m_itemWriter, *begin); + m_next.IterateVals(func, std::next(begin), end); + } + + template + void ApplyVals(_Func&& func, _ValT&& val, _ValTs&&... vals) const + { + static_assert( + sizeof...(_ValTs) == sizeof...(_ItemWriters), + "ABI writer - the number of given data does not match the declared" + ); + func(m_itemWriter, std::forward<_ValT>(val)); + m_next.ApplyVals( + std::forward<_Func>(func), + std::forward<_ValTs>(vals)... + ); + } + + size_t m_size; + ItemWriter m_itemWriter; + AbiNestedWriterMgrTuple<_ItemWriters...> m_next; + bool m_isDynamic; + size_t m_totalNumHeadChunks; + +}; // struct AbiNestedWriterMgrTuple<_ItemWriter, _ItemWriters...> + + template struct AbiWriterNestedTypeDynLenImpl { @@ -875,8 +1001,11 @@ struct AbiWriterNestedTypeDynLenImpl template void operator()(const _Writer& writer, const _ValType& val) { - std::tie(m_destIt, m_dataOffset) = - writer.WriteHead(m_destIt, val, m_dataOffset); + std::tie(m_destIt, m_dataOffset) = writer.WriteHead( + m_destIt, + val, + m_dataOffset + ); } _DestIt& m_destIt; @@ -1015,29 +1144,120 @@ struct AbiWriterNestedTypeConstLenImpl : }; // struct AbiWriterNestedTypeConstLenImpl -template -struct AbiWriterNestedListConstLenImpl : +template +struct AbiWriterNestedTypeTupleImpl : public AbiWriterNestedTypeConstLenImpl<_NestedWriterMgr> { - using NestedWriterMgr = _NestedWriterMgr; using Base = AbiWriterNestedTypeConstLenImpl<_NestedWriterMgr>; + using Self = AbiWriterNestedTypeTupleImpl<_NestedWriterMgr>; + + using Base::Base; + + using NumTailChunksFunctor = typename Base::NumTailChunksFunctor; + template + using WriteHeadsFunctor = typename Base::template WriteHeadsFunctor<_DestIt>; + template + using WriteTailsFunctor = typename Base::template WriteTailsFunctor<_DestIt>; + + template + size_t GetNumTailChunksTuple(_ValTs&& ...vals) const + { + if (!Base::IsDynamicType()) + { + // static type - no tail chunks + return 0; + } + else + { + // dynamic type - the tail is + // sub_head_1, sub_head_2, ..., sub_head_n, + // sub_tail_1, sub_tail_2, ..., sub_tail_n + size_t numTailChunks = + (Base::m_nestedWriterMgr).GetTotalNumHeadChunks(); + + (Base::m_nestedWriterMgr).ApplyVals( + NumTailChunksFunctor(numTailChunks), + std::forward<_ValTs>(vals)... + ); + + return numTailChunks; + } + } + + template + _DestIt WriteTuple(_DestIt destIt, _ValTs&& ...vals) const + { + // calculate the offset of the data area + size_t dataOffset = + (Base::m_nestedWriterMgr).GetTotalNumHeadChunks() * + AbiCodecConst::sk_chunkSize(); + + + // write all head chunks + (Base::m_nestedWriterMgr).ApplyVals( + WriteHeadsFunctor<_DestIt>(destIt, dataOffset), + std::forward<_ValTs>(vals)... + ); + + // write all tail chunks + (Base::m_nestedWriterMgr).ApplyVals( + WriteTailsFunctor<_DestIt>(destIt), + std::forward<_ValTs>(vals)... + ); + + return destIt; + } +}; // struct AbiWriterNestedTypeTupleImpl + + +template class _BaseCls, class _NestedWriterMgr> +struct AbiWriterNestedConstLenImpl : + public _BaseCls<_NestedWriterMgr> +{ + using NestedWriterMgr = _NestedWriterMgr; + using Base = _BaseCls<_NestedWriterMgr>; using Base::Base; - ~AbiWriterNestedListConstLenImpl() = default; + ~AbiWriterNestedConstLenImpl() = default; + + size_t GetNumTailChunks(const Internal::Obj::ListBaseObj& val) const + { + return Base::GetNumTailChunks(val.begin(), val.end()); + } size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const { - const auto& list = val.AsList(); - return Base::GetNumTailChunks(list.begin(), list.end()); + return GetNumTailChunks(val.AsList()); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::ListBaseObj& val) const + { + return Base::Write(destIt, val.begin(), val.end()); } template _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const { - const auto& list = val.AsList(); - return Base::Write(destIt, list.begin(), list.end()); + return WriteObj(destIt, val.AsList()); } +}; // struct AbiWriterNestedConstLenImpl + + +template +struct AbiWriterNestedListConstLenImpl : + public AbiWriterNestedConstLenImpl< + AbiWriterNestedTypeConstLenImpl, + _NestedWriterMgr + > +{ + using Base = AbiWriterNestedConstLenImpl< + AbiWriterNestedTypeConstLenImpl, + _NestedWriterMgr + >; + + using Base::Base; }; // struct AbiWriterNestedListConstLenImpl @@ -1052,29 +1272,97 @@ struct AbiWriterNestedListDynLenImpl : ~AbiWriterNestedListDynLenImpl() = default; + size_t GetNumTailChunks(const Internal::Obj::ListBaseObj& val) const + { + return Base::GetNumTailChunks(val.size(), val.begin(), val.end()); + } + size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const { - const auto& list = val.AsList(); - return Base::GetNumTailChunks(list.size(), list.begin(), list.end()); + return GetNumTailChunks(val.AsList()); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::ListBaseObj& val) const + { + return Base::Write(destIt, val.size(), val.begin(), val.end()); } template _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const { - const auto& list = val.AsList(); - return Base::Write(destIt, list.size(), list.begin(), list.end()); + return WriteObj(destIt, val.AsList()); } }; // struct AbiWriterNestedListDynLenImpl -// ========== -// AbiWriterImpl for (T1, T2, Tn) types, where T is static type -// ========== +template +struct AbiWriterNestedTupleImpl : + public AbiWriterNestedConstLenImpl< + AbiWriterNestedTypeTupleImpl, + _NestedWriterMgr + > +{ + using NestedWriterMgr = _NestedWriterMgr; + using Base = AbiWriterNestedConstLenImpl< + AbiWriterNestedTypeTupleImpl, + _NestedWriterMgr + >; + using Base::Base; -// ========== -// AbiWriterImpl for (T1, T2, Tn) types, where T is dynamic type -// ========== + ~AbiWriterNestedTupleImpl() = default; + + size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const + { + return Base::GetNumTailChunks(val.AsList()); + } + + template + size_t GetNumTailChunks( + const _TupleType& vals, + Internal::Obj::SizeSeq<_Idx...> + ) const + { + return Base::GetNumTailChunksTuple(std::get<_Idx - 1>(vals)...); + } + + template + size_t GetNumTailChunks(const std::tuple<_ValTs...>& vals) const + { + return GetNumTailChunks( + vals, + Internal::Obj::IncrSizeSeq() + ); + } + + template + _DestIt WriteObj(_DestIt destIt, const Internal::Obj::BaseObj& val) const + { + return Base::WriteObj(destIt, val.AsList()); + } + + template + _DestIt Write( + _DestIt destIt, + const _TupleType& vals, + Internal::Obj::SizeSeq<_Idx...> + ) const + { + return Base::WriteTuple(destIt, std::get<_Idx - 1>(vals)...); + } + + template + _DestIt Write(_DestIt destIt, const std::tuple<_ValTs...>& vals) const + { + return Write( + destIt, + vals, + Internal::Obj::IncrSizeSeq() + ); + } + +}; // struct AbiWriterNestedTupleImpl } // namespace EthInternal @@ -1145,17 +1433,17 @@ class AbiWriterHeadOnlyBase : public AbiWriterBase virtual ~AbiWriterHeadOnlyBase() = default; - bool IsDynamicType() const override + virtual bool IsDynamicType() const override { return m_impl.IsDynamicType(); } - size_t GetNumHeadChunks() const override + virtual size_t GetNumHeadChunks() const override { return m_impl.GetNumHeadChunks(); } - size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const override + virtual size_t GetNumTailChunks(const Internal::Obj::BaseObj&) const override { return 0; } @@ -1229,17 +1517,17 @@ class AbiWriterHeadTailBase : public AbiWriterBase virtual ~AbiWriterHeadTailBase() = default; - bool IsDynamicType() const override + virtual bool IsDynamicType() const override { return m_impl.IsDynamicType(); } - size_t GetNumHeadChunks() const override + virtual size_t GetNumHeadChunks() const override { return m_impl.GetNumHeadChunks(); } - size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const override + virtual size_t GetNumTailChunks(const Internal::Obj::BaseObj& val) const override { return m_impl.GetNumTailChunks(val); } @@ -1546,7 +1834,7 @@ struct AbiWriter< virtual ~AbiWriter() = default; // LCOV_EXCL_STOP -}; // struct AbiParser +}; // struct AbiWriter template @@ -1626,8 +1914,166 @@ struct AbiWriter< virtual ~AbiWriter() = default; // LCOV_EXCL_STOP -}; // struct AbiParser +}; // struct AbiWriter + + +template +struct AbiWriterListDynLen : + public AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::true_type + > +{ + using Base = AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::true_type + >; + using Self = AbiWriterListDynLen<_ItemWriter>; + + AbiWriterListDynLen() : + Base(_ItemWriter()) + {} +}; // struct AbiWriterListDynLen + + +// ========== +// AbiWriter for (T1, T2, ..., Tn) types +// ========== + + +template +struct AbiWriter< + Internal::Obj::ObjCategory::StaticDict, + std::false_type, // Are inner types specified at runtime? - false + _ItemWriters... +> : + public AbiWriterHeadTailBase< + EthInternal::AbiWriterNestedTupleImpl< + EthInternal::AbiNestedWriterMgrTuple<_ItemWriters...> + > + > +{ + using NestedWritersMgr = + EthInternal::AbiNestedWriterMgrTuple<_ItemWriters...>; + using WriterImpl = EthInternal::AbiWriterNestedTupleImpl< + NestedWritersMgr + >; + using Base = AbiWriterHeadTailBase; + using Self = AbiWriter< + Internal::Obj::ObjCategory::StaticDict, + std::false_type, + _ItemWriters... + >; + + using WriteIterator = typename Base::WriteIterator; + AbiWriter(_ItemWriters&& ...itemWriters) : + Base( + WriterImpl( + NestedWritersMgr(std::forward<_ItemWriters>(itemWriters)...) + ) + ) + {} + + // LCOV_EXCL_START + virtual ~AbiWriter() = default; + // LCOV_EXCL_STOP + + using Base::GetNumTailChunks; + + template + size_t GetNumTailChunks(const std::tuple<_ValTs...>& vals) const + { + return (Base::m_impl).GetNumTailChunks(vals); + } + + using Base::WriteHead; + + template + std::tuple< + WriteIterator, + size_t + > + WriteHead( + WriteIterator destIt, + const std::tuple<_ValTs...>& vals, + size_t currDataOffset + ) const + { + if (!Base::IsDynamicType()) + { + // static type - head only + return std::make_tuple( + (Base::m_impl).Write(destIt, vals), + currDataOffset + ); + } + else + { + // dynamic type - head is a uint representing the data offset + auto headIt = (Base::m_headWriter).Write( + destIt, + static_cast(currDataOffset) + ); + + // calculate the new data offset + currDataOffset += ( + (Base::m_impl).GetNumTailChunks(vals) * + AbiCodecConst::sk_chunkSize() + ); + + return std::make_tuple(headIt, currDataOffset); + } + } + + using Base::WriteTail; + + template + WriteIterator WriteTail( + WriteIterator destIt, + const std::tuple<_ValTs...>& vals + ) const + { + if (!Base::IsDynamicType()) + { + // static type - no tail chunks + return destIt; + } + else + { + // dynamic type - write the tail chunks + return (Base::m_impl).Write(destIt, vals); + } + } + +}; // struct AbiWriter + + +template +struct AbiWriterStaticTuple : + public AbiWriter< + Internal::Obj::ObjCategory::StaticDict, + std::false_type, + _ItemWriters... + > +{ + using Base = AbiWriter< + Internal::Obj::ObjCategory::StaticDict, + std::false_type, + _ItemWriters... + >; + using Self = AbiWriterStaticTuple<_ItemWriters...>; + + AbiWriterStaticTuple() : + Base(_ItemWriters()...) + {} + + // LCOV_EXCL_START + virtual ~AbiWriterStaticTuple() = default; + // LCOV_EXCL_STOP +}; // struct AbiWriterStaticTuple } // namespace Eth diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index b5ba5f5..7e3f75d 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -78,10 +78,10 @@ static const std::vector gsk_testObjWriterHead = { }; -template +template static void TestObjWriter( const _Writer& writer, - const SimpleObjects::BaseObj& obj, + const _ObjT& obj, const std::vector& expHeadOutput, const std::vector& expTailOutput, size_t expDataOffsetIncr = 0 @@ -967,3 +967,452 @@ GTEST_TEST(TestEthAbiWriter, WriteDynListDynLen) } } + +GTEST_TEST(TestEthAbiWriter, WriteStaticTuple) +{ + { + using AbiTupleWriter = AbiWriter< + SimpleObjects::ObjCategory::StaticDict, + std::false_type, + AbiWriter, + AbiWriter > + >; + + AbiTupleWriter writer( + ( AbiWriter() ), + ( AbiWriter >() ) + ); + + EXPECT_EQ(writer.IsDynamicType(), false); + EXPECT_EQ(writer.GetNumHeadChunks(), 2); + + { + std::vector expOutput = { + // item 1 + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x12U, 0x34U, 0x56U, 0x78U, 0x90U, 0xabU, 0xcdU, 0xefU, + // item 2 + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + SimpleObjects::UInt64(0x1234567890ABCDEFULL), + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}) + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 0); + TestObjWriter( + writer, + inVal, + expOutput, + {} + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); + auto inValRef = std::tie(inVal1Ref, inVal2Ref); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 0); + TestObjWriter( + writer, + inValRef, + expOutput, + {} + ); + } + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteDynamicTuple) +{ + { + using AbiTupleWriter = AbiWriter< + SimpleObjects::ObjCategory::StaticDict, + std::false_type, + AbiWriter, + AbiWriter + >; + + AbiTupleWriter writer( + ( AbiWriter() ), + ( AbiWriter() ) + ); + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + { + std::vector expOutput = { + // item 1 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x80U, + // data area + // item 1 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 1 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // item 2 - len + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x09U, + // item 2 - bytes + 0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, + 0x01U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}), + SimpleObjects::Bytes({0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U}) + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 6); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); + auto inValRef = std::tie(inVal1Ref, inVal2Ref); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 6); + TestObjWriter( + writer, + inValRef, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); + } + } +} + + +GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) +{ + SimpleObjects::UInt64 val1(12345); + SimpleObjects::UInt64 val2(54321); + SimpleObjects::UInt64 val3(67890); + SimpleObjects::Bytes val4({ 0x01U, 0x02U, 0x03U, 0x04U, 0x05U }); + + // ('uint64', 'uint64[]', 'bytes5') + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterListDynLen< + AbiWriter + >, + AbiWriter > + >; + + AbiTupleWriter writer; + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + std::vector expOutput = { + // item 1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x60U, + // item 3 - value + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // data area + // list length + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U, + // item 2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + // item 2.2 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x09U, 0x32U, + }; + auto inVal = std::make_tuple( + val1, + SimpleObjects::ListT({val2, val3}), + val4 + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 6); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); + const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); + auto inValRef = std::tie(inVal1Ref, inVal2Ref, inVal3Ref); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 6); + TestObjWriter( + writer, + inValRef, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); + } + + // ('uint64', '(uint64,uint64)', 'bytes5') + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + >, + AbiWriter > + >; + + AbiTupleWriter writer; + + EXPECT_EQ(writer.IsDynamicType(), false); + EXPECT_EQ(writer.GetNumHeadChunks(), 4); + + std::vector expOutput = { + // item 1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + // item 2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + // item 2.2 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x09U, 0x32U, + // item 3 - value + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + val1, + std::make_tuple(val2, val3), + val4 + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 0); + TestObjWriter( + writer, + inVal, + expOutput, + {} + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal21Ref = std::get<0>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal22Ref = std::get<1>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); + auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); + auto inValRef = std::tie( + inVal1Ref, + inVal2Ref, + inVal3Ref + ); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 0); + TestObjWriter( + writer, + inValRef, + expOutput, + {} + ); + } + + // ('uint64', '(uint64,bytes)', 'bytes5') + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + >, + AbiWriter > + >; + + AbiTupleWriter writer; + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + std::vector expOutput = { + // item 1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x60U, + // item 3 - value + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // data area + // item 2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + // item 2.2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // item 2.2 - length of bytes + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 2.2 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + val1, + std::make_tuple(val2, val4), + val4 + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 7); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 7 * AbiCodecConst::sk_chunkSize() + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal21Ref = std::get<0>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal22Ref = std::get<1>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); + auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); + auto inValRef = std::tie( + inVal1Ref, + inVal2Ref, + inVal3Ref + ); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 7); + TestObjWriter( + writer, + inValRef, + gsk_testObjWriterHead, + expOutput, + 7 * AbiCodecConst::sk_chunkSize() + ); + } + + // ('uint64', '(uint64,(uint64,bytes))', 'bytes5') + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + > + >, + AbiWriter > + >; + + AbiTupleWriter writer; + + EXPECT_EQ(writer.IsDynamicType(), true); + EXPECT_EQ(writer.GetNumHeadChunks(), 1); + + std::vector expOutput = { + // item 1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + // item 2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x60U, + // item 3 - value + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + // data area 1 + // item 2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + // item 2.2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // data area 2.2 + // item 2.2.1 - value + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x09U, 0x32U, + // item 2.2.2 - offset + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, + // item 2.2.2 - length of bytes + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, + // item 2.2.2 - bytes + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + }; + auto inVal = std::make_tuple( + val1, + std::make_tuple( + val2, + std::make_tuple(val3, val4) + ), + val4 + ); + + EXPECT_EQ(writer.GetNumTailChunks(inVal), 9); + TestObjWriter( + writer, + inVal, + gsk_testObjWriterHead, + expOutput, + 9 * AbiCodecConst::sk_chunkSize() + ); + + const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); + const SimpleObjects::BaseObj& inVal21Ref = std::get<0>(std::get<1>(inVal)); + const SimpleObjects::BaseObj& inVal221Ref = std::get<0>(std::get<1>(std::get<1>(inVal))); + const SimpleObjects::BaseObj& inVal222Ref = std::get<1>(std::get<1>(std::get<1>(inVal))); + const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); + auto inVal22Ref = std::tie(inVal221Ref, inVal222Ref); + auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); + auto inValRef = std::tie( + inVal1Ref, + inVal2Ref, + inVal3Ref + ); + + EXPECT_EQ(writer.GetNumTailChunks(inValRef), 9); + TestObjWriter( + writer, + inValRef, + gsk_testObjWriterHead, + expOutput, + 9 * AbiCodecConst::sk_chunkSize() + ); + } +} + From c7c10fceef10f49bcf52050c2f53c3473c6a2586 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Wed, 22 May 2024 03:40:50 -0700 Subject: [PATCH 09/22] attempt to fix MSVC compile error re invalid template argument --- include/EclipseMonitor/Eth/AbiWriter.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp index 6977152..a5db3b8 100644 --- a/include/EclipseMonitor/Eth/AbiWriter.hpp +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -1253,7 +1253,7 @@ struct AbiWriterNestedListConstLenImpl : > { using Base = AbiWriterNestedConstLenImpl< - AbiWriterNestedTypeConstLenImpl, + EthInternal::AbiWriterNestedTypeConstLenImpl, _NestedWriterMgr >; @@ -1305,7 +1305,7 @@ struct AbiWriterNestedTupleImpl : { using NestedWriterMgr = _NestedWriterMgr; using Base = AbiWriterNestedConstLenImpl< - AbiWriterNestedTypeTupleImpl, + EthInternal::AbiWriterNestedTypeTupleImpl, _NestedWriterMgr >; From d56412b3e4b2f0503d34c225d0030dd002950f37 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Fri, 24 May 2024 00:27:48 -0700 Subject: [PATCH 10/22] Added ABI type name generation --- include/EclipseMonitor/Eth/AbiCommon.hpp | 20 +++- include/EclipseMonitor/Eth/AbiWriter.hpp | 124 +++++++++++++++++++++-- test/src/TestEthAbiWriter.cpp | 64 +++++++----- 3 files changed, 173 insertions(+), 35 deletions(-) diff --git a/include/EclipseMonitor/Eth/AbiCommon.hpp b/include/EclipseMonitor/Eth/AbiCommon.hpp index e62eda7..38df439 100644 --- a/include/EclipseMonitor/Eth/AbiCommon.hpp +++ b/include/EclipseMonitor/Eth/AbiCommon.hpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -110,6 +111,8 @@ struct RealNumTypeTraits : { return val.AsCppUInt8(); } + + static const char* GetTypeName() { return "uint8"; } }; // struct RealNumTypeTraits @@ -119,7 +122,9 @@ struct RealNumTypeTraits : Internal::Obj::RealNumType::UInt16, uint16_t > -{}; // struct RealNumTypeTraits +{ + static const char* GetTypeName() { return "uint16"; } +}; // struct RealNumTypeTraits template<> @@ -133,6 +138,7 @@ struct RealNumTypeTraits : { return val.AsCppUInt32(); } + static const char* GetTypeName() { return "uint32"; } }; // struct RealNumTypeTraits @@ -147,6 +153,7 @@ struct RealNumTypeTraits : { return val.AsCppUInt64(); } + static const char* GetTypeName() { return "uint64"; } }; // struct RealNumTypeTraits @@ -198,6 +205,8 @@ struct AbiCodecImpl< static constexpr bool sk_isDynamic = IsDynamic::value; bool IsDynamicType() const noexcept { return sk_isDynamic; } + static const char* GetTypeName() { return RealNumTraits::GetTypeName(); } + }; // struct AbiCodecImpl @@ -231,6 +240,8 @@ struct AbiCodecImpl< static constexpr bool sk_isDynamic = IsDynamic::value; bool IsDynamicType() const noexcept { return sk_isDynamic; } + static const char* GetTypeName() { return "bool"; } + }; // struct AbiCodecImpl @@ -252,6 +263,11 @@ struct AbiCodecImpl< static constexpr bool sk_isDynamic = IsDynamic::value; bool IsDynamicType() const noexcept { return sk_isDynamic; } + static std::string GetTypeName(size_t size) + { + return std::string("bytes") + std::to_string(size); + } + }; // struct AbiCodecImpl @@ -273,6 +289,8 @@ struct AbiCodecImpl< static constexpr bool sk_isDynamic = IsDynamic::value; bool IsDynamicType() const noexcept { return sk_isDynamic; } + static const char* GetTypeName() { return "bytes"; } + }; // struct AbiCodecImpl diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp index a5db3b8..b9bbb85 100644 --- a/include/EclipseMonitor/Eth/AbiWriter.hpp +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -227,6 +227,12 @@ struct AbiWriterUIntImpl : return destIt; } + const std::string& GetTypeName() const + { + static const std::string sk_nameStr = Codec::GetTypeName(); + return sk_nameStr; + } + }; // struct AbiWriterUIntImpl @@ -353,6 +359,12 @@ struct AbiWriterImpl< return Self::Write(destIt, val.AsRealNum()); } + const std::string& GetTypeName() const + { + static const std::string sk_nameStr = Codec::GetTypeName(); + return sk_nameStr; + } + private: // IntWriterImpl m_intWriter; @@ -389,7 +401,8 @@ struct AbiWriterImpl< */ AbiWriterImpl(size_t size) : m_size(size), - m_padSize(AbiCodecConst::sk_chunkSize() - m_size) + m_padSize(AbiCodecConst::sk_chunkSize() - m_size), + m_nameStr(Codec::GetTypeName(m_size)) {} ~AbiWriterImpl() = default; @@ -478,10 +491,16 @@ struct AbiWriterImpl< return m_padSize; } + const std::string& GetTypeName() const + { + return m_nameStr; + } + private: size_t m_size; size_t m_padSize; + std::string m_nameStr; }; // struct AbiWriterImpl @@ -643,6 +662,12 @@ struct AbiWriterImpl< return Write(destIt, val.AsBytes()); } + const std::string& GetTypeName() const + { + static const std::string sk_nameStr = Codec::GetTypeName(); + return sk_nameStr; + } + private: // DynLenWriterImpl m_dynLenWriter; @@ -666,7 +691,8 @@ struct AbiNestedWriterMgrListDynLen using ItemWriter = _ItemWriter; AbiNestedWriterMgrListDynLen(ItemWriter itemWriter) : - m_itemWriter(std::move(itemWriter)) + m_itemWriter(std::move(itemWriter)), + m_nameStr(m_itemWriter.GetTypeName() + std::string("[]")) {} ~AbiNestedWriterMgrListDynLen() = default; @@ -693,7 +719,13 @@ struct AbiNestedWriterMgrListDynLen } } + const std::string& GetTypeName() const + { + return m_nameStr; + } + ItemWriter m_itemWriter; + std::string m_nameStr; }; // struct AbiNestedWriterMgrListDynLen @@ -720,7 +752,10 @@ struct AbiNestedWriterMgrListConstLen : AbiNestedWriterMgrListConstLen(ItemWriter itemWriter, size_t size) : Base(std::move(itemWriter)), m_size(size) - {} + { + (Base::m_nameStr) = (Base::m_itemWriter).GetTypeName() + + std::string("[") + std::to_string(m_size) + std::string("]"); + } AbiNestedWriterMgrListConstLen(std::pair pair) : AbiNestedWriterMgrListConstLen(std::move(pair.first), pair.second) @@ -792,7 +827,8 @@ struct AbiNestedWriterMgrTuple<> AbiNestedWriterMgrTuple() : m_size(0), m_isDynamic(false), // tuple with no nested types is static type - m_totalNumHeadChunks(0) + m_totalNumHeadChunks(0), + m_nameCommaList() {} ~AbiNestedWriterMgrTuple() = default; @@ -822,9 +858,21 @@ struct AbiNestedWriterMgrTuple<> void ApplyVals(_Func&&) const {} + bool IsTupleEmpty() const + { + return true; + } + + const std::string& GetTypeName() const + { + static const std::string sk_nameStr = "()"; + return sk_nameStr; + } + size_t m_size; bool m_isDynamic; size_t m_totalNumHeadChunks; + std::string m_nameCommaList; }; // struct AbiNestedWriterMgrTuple<> @@ -841,6 +889,16 @@ struct AbiNestedWriterMgrTuple<_ItemWriter, _ItemWriters...> m_isDynamic(m_itemWriter.IsDynamicType() || m_next.IsDynamicType()), m_totalNumHeadChunks( m_itemWriter.GetNumHeadChunks() + m_next.GetTotalNumHeadChunks() + ), + m_nameCommaList( + m_itemWriter.GetTypeName() + ( + m_next.IsTupleEmpty() ? + std::string("") : + (std::string(",") + m_next.m_nameCommaList) + ) + ), + m_nameStr( + std::string("(") + m_nameCommaList + std::string(")") ) {} @@ -889,11 +947,23 @@ struct AbiNestedWriterMgrTuple<_ItemWriter, _ItemWriters...> ); } + bool IsTupleEmpty() const + { + return false; + } + + const std::string& GetTypeName() const + { + return m_nameStr; + } + size_t m_size; ItemWriter m_itemWriter; AbiNestedWriterMgrTuple<_ItemWriters...> m_next; bool m_isDynamic; size_t m_totalNumHeadChunks; + std::string m_nameCommaList; + std::string m_nameStr; }; // struct AbiNestedWriterMgrTuple<_ItemWriter, _ItemWriters...> @@ -1070,6 +1140,11 @@ struct AbiWriterNestedTypeDynLenImpl return WriteHeadsAndTails(destIt, dataOffset, begin, end); } + const std::string& GetTypeName() const + { + return m_nestedWriterMgr.GetTypeName(); + } + DynLenWriterImpl m_dynLenWriter; NestedWriterMgr m_nestedWriterMgr; @@ -1409,6 +1484,8 @@ class AbiWriterBase // const Internal::Obj::BaseObj& data // ) const = 0; + virtual const std::string& GetTypeName() const = 0; + }; // class AbiWriterBase @@ -1428,7 +1505,8 @@ class AbiWriterHeadOnlyBase : public AbiWriterBase AbiWriterHeadOnlyBase(WriterImpl impl) : Base(), - m_impl(std::move(impl)) + m_impl(std::move(impl)), + m_typeName(m_impl.GetTypeName()) {} virtual ~AbiWriterHeadOnlyBase() = default; @@ -1475,9 +1553,15 @@ class AbiWriterHeadOnlyBase : public AbiWriterBase return destIt; } + virtual const std::string& GetTypeName() const + { + return m_typeName; + } + private: WriterImpl m_impl; + std::string m_typeName; }; // class AbiWriterHeadOnlyBase @@ -1512,7 +1596,8 @@ class AbiWriterHeadTailBase : public AbiWriterBase AbiWriterHeadTailBase(WriterImpl impl) : Base(), m_headWriter(HeadWriterImpl()), - m_impl(std::move(impl)) + m_impl(std::move(impl)), + m_typeName(m_impl.GetTypeName()) {} virtual ~AbiWriterHeadTailBase() = default; @@ -1586,10 +1671,16 @@ class AbiWriterHeadTailBase : public AbiWriterBase } } + virtual const std::string& GetTypeName() const + { + return m_typeName; + } + protected: HeadWriterImpl m_headWriter; WriterImpl m_impl; + std::string m_typeName; }; // class AbiWriterHeadTailBase @@ -1873,6 +1964,27 @@ struct AbiWriter< }; // struct AbiWriter +template +struct AbiWriterListConstLen : + public AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::integral_constant + > +{ + using Base = AbiWriter< + Internal::Obj::ObjCategory::List, + _ItemWriter, + std::integral_constant + >; + using Self = AbiWriterListConstLen<_ItemWriter, _Size>; + + AbiWriterListConstLen() : + Base(_ItemWriter()) + {} +}; // struct AbiWriterListConstLen + + // ========== // AbiWriter for list types (T[]) // ========== diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index 7e3f75d..842ba4d 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -131,10 +131,12 @@ GTEST_TEST(TestEthAbiWriter, WriteIntegerImpl) EXPECT_EQ(AbiWriterImplUInt64().GetNumHeadChunks(), 1); EXPECT_EQ(AbiWriterImplUInt64().GetNumTailChunks(), 0); EXPECT_EQ(AbiWriterImplUInt64().GetNumTailChunks(SimpleObjects::UInt64()), 0); + EXPECT_EQ(AbiWriterImplUInt64().GetTypeName(), "uint64"); EXPECT_EQ(AbiWriterUInt64().IsDynamicType(), false); EXPECT_EQ(AbiWriterUInt64().GetNumHeadChunks(), 1); EXPECT_EQ(AbiWriterUInt64().GetNumTailChunks(SimpleObjects::UInt64()), 0); + EXPECT_EQ(AbiWriterUInt64().GetTypeName(), "uint64"); { std::vector expOutput = { @@ -165,10 +167,12 @@ GTEST_TEST(TestEthAbiWriter, WriteBoolImpl) EXPECT_EQ(AbiWriterImplBool().GetNumHeadChunks(), 1); EXPECT_EQ(AbiWriterImplBool().GetNumTailChunks(), 0); EXPECT_EQ(AbiWriterImplBool().GetNumTailChunks(SimpleObjects::Bool()), 0); + EXPECT_EQ(AbiWriterImplBool().GetTypeName(), "bool"); EXPECT_EQ(AbiWriterBool().IsDynamicType(), false); EXPECT_EQ(AbiWriterBool().GetNumHeadChunks(), 1); EXPECT_EQ(AbiWriterBool().GetNumTailChunks(SimpleObjects::Bool()), 0); + EXPECT_EQ(AbiWriterBool().GetTypeName(), "bool"); { std::vector expOutput = { @@ -224,10 +228,12 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticBytesImpl) EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::Bytes()), 0); EXPECT_EQ(writerImpl.GetPadSize(), 16); + EXPECT_EQ(writerImpl.GetTypeName(), "bytes16"); EXPECT_EQ(writer.IsDynamicType(), false); EXPECT_EQ(writer.GetNumHeadChunks(), 1); EXPECT_EQ(writer.GetNumTailChunks(SimpleObjects::Bytes()), 0); + EXPECT_EQ(writerImpl.GetTypeName(), "bytes16"); std::vector expOutput = { 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, @@ -253,10 +259,12 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticBytesImpl) EXPECT_EQ(writerImpl.GetNumTailChunks(), 0); EXPECT_EQ(writerImpl.GetNumTailChunks(SimpleObjects::Bytes()), 0); EXPECT_EQ(writerImpl.GetPadSize(), 0); + EXPECT_EQ(writerImpl.GetTypeName(), "bytes32"); EXPECT_EQ(writer.IsDynamicType(), false); EXPECT_EQ(writer.GetNumHeadChunks(), 1); EXPECT_EQ(writer.GetNumTailChunks(SimpleObjects::Bytes()), 0); + EXPECT_EQ(writerImpl.GetTypeName(), "bytes32"); std::vector expOutput = { 0x01U, 0x23U, 0x45U, 0x67U, 0x89U, 0xABU, 0xCDU, 0xEFU, @@ -346,12 +354,16 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicBytesImpl) EXPECT_EQ(writerImpl.GetPadSize(64), 0); EXPECT_EQ(writerImpl.GetPadSize(65), 31); + EXPECT_EQ(writerImpl.GetTypeName(), "bytes"); + AbiWriterBytes writer; EXPECT_EQ(writer.IsDynamicType(), true); EXPECT_EQ(writer.GetNumHeadChunks(), 1); EXPECT_EQ(writer.GetNumTailChunks(SimpleObjects::Bytes()), 1); + EXPECT_EQ(writer.GetTypeName(), "bytes"); + { std::vector expOutput = { // len @@ -477,11 +489,7 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticListConstLen) AbiWriterUint64, std::false_type >; - using AbiWriterListUint64_2 = AbiWriter< - SimpleObjects::ObjCategory::List, - AbiWriterUint64, - AbiSize<2> - >; + using AbiWriterListUint64_2 = AbiWriterListConstLen; { AbiWriterListUint64 writer(AbiWriterUint64(), 5); @@ -490,6 +498,8 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticListConstLen) EXPECT_EQ(writer.GetNumHeadChunks(), 5); + EXPECT_EQ(writer.GetTypeName(), "uint64[5]"); + std::vector expOutput = { 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, @@ -529,13 +539,14 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticListConstLen) } { - AbiWriterUint64 innerWriter; - AbiWriterListUint64_2 writer(innerWriter); + AbiWriterListUint64_2 writer; EXPECT_EQ(writer.IsDynamicType(), false); EXPECT_EQ(writer.GetNumHeadChunks(), 2); + EXPECT_EQ(writer.GetTypeName(), "uint64[2]"); + std::vector expOutput = { 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, @@ -599,11 +610,7 @@ GTEST_TEST(TestEthAbiWriter, WriteDynListConstLen) AbiWriterBytes, std::false_type >; - using AbiWriterListBytes2 = AbiWriter< - SimpleObjects::ObjCategory::List, - AbiWriterBytes, - AbiSize<2> - >; + using AbiWriterListBytes2 = AbiWriterListConstLen; { AbiWriterListBytes writer(AbiWriterBytes(), 1); @@ -612,6 +619,8 @@ GTEST_TEST(TestEthAbiWriter, WriteDynListConstLen) EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetTypeName(), "bytes[1]"); + std::vector expOutput = { // item 1 - offset 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, @@ -645,13 +654,14 @@ GTEST_TEST(TestEthAbiWriter, WriteDynListConstLen) } { - AbiWriterBytes innerWriter; - AbiWriterListBytes2 writer(innerWriter); + AbiWriterListBytes2 writer; EXPECT_EQ(writer.IsDynamicType(), true); EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetTypeName(), "bytes[2]"); + std::vector expOutput = { // item 1 - offset 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, @@ -738,17 +748,13 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticListDynLen) SimpleObjects::ObjCategory::Integer, AbiUInt64 >; - using AbiWriterListUint64 = AbiWriter< - SimpleObjects::ObjCategory::List, - AbiWriterUint64, - std::true_type - >; + using AbiWriterListUint64 = AbiWriterListDynLen; - AbiWriterUint64 innerWriter; - AbiWriterListUint64 writer(innerWriter); + AbiWriterListUint64 writer; EXPECT_EQ(writer.IsDynamicType(), true); EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetTypeName(), "uint64[]"); { std::vector expOutput = { @@ -840,17 +846,13 @@ GTEST_TEST(TestEthAbiWriter, WriteDynListDynLen) SimpleObjects::ObjCategory::Bytes, std::true_type >; - using AbiWriterListUint64 = AbiWriter< - SimpleObjects::ObjCategory::List, - AbiWriterBytes, - std::true_type - >; + using AbiWriterListUint64 = AbiWriterListDynLen; - AbiWriterBytes innerWriter; - AbiWriterListUint64 writer(innerWriter); + AbiWriterListUint64 writer; EXPECT_EQ(writer.IsDynamicType(), true); EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetTypeName(), "bytes[]"); { std::vector expOutput = { @@ -985,6 +987,7 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticTuple) EXPECT_EQ(writer.IsDynamicType(), false); EXPECT_EQ(writer.GetNumHeadChunks(), 2); + EXPECT_EQ(writer.GetTypeName(), "(uint64,bytes5)"); { std::vector expOutput = { @@ -1045,6 +1048,7 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicTuple) EXPECT_EQ(writer.IsDynamicType(), true); EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetTypeName(), "(bytes,bytes)"); { std::vector expOutput = { @@ -1132,6 +1136,7 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) EXPECT_EQ(writer.IsDynamicType(), true); EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetTypeName(), "(uint64,uint64[],bytes5)"); std::vector expOutput = { // item 1 - value @@ -1199,6 +1204,7 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) EXPECT_EQ(writer.IsDynamicType(), false); EXPECT_EQ(writer.GetNumHeadChunks(), 4); + EXPECT_EQ(writer.GetTypeName(), "(uint64,(uint64,uint64),bytes5)"); std::vector expOutput = { // item 1 - value @@ -1263,6 +1269,7 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) EXPECT_EQ(writer.IsDynamicType(), true); EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetTypeName(), "(uint64,(uint64,bytes),bytes5)"); std::vector expOutput = { // item 1 - value @@ -1342,6 +1349,7 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) EXPECT_EQ(writer.IsDynamicType(), true); EXPECT_EQ(writer.GetNumHeadChunks(), 1); + EXPECT_EQ(writer.GetTypeName(), "(uint64,(uint64,(uint64,bytes)),bytes5)"); std::vector expOutput = { // item 1 - value From aae68c01035e3b49dc349c415e856f6845accd26 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Fri, 24 May 2024 03:20:34 -0700 Subject: [PATCH 11/22] more ABI writer test cases --- test/src/TestEthAbiWriter.cpp | 84 +++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index 842ba4d..bde8b5c 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -1026,6 +1026,18 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticTuple) expOutput, {} ); + + SimpleObjects::List inValObj = SimpleObjects::List({ + SimpleObjects::UInt64(0x1234567890ABCDEFULL), + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}) + }); + EXPECT_EQ(writer.GetNumTailChunks(inValObj), 0); + TestObjWriter( + writer, + inValObj, + expOutput, + {} + ); } } } @@ -1110,6 +1122,19 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicTuple) expOutput, 6 * AbiCodecConst::sk_chunkSize() ); + + SimpleObjects::List inValObj = SimpleObjects::List({ + SimpleObjects::Bytes({0x01U, 0x02U, 0x03U, 0x04U, 0x05U}), + SimpleObjects::Bytes({0x09U, 0x08U, 0x07U, 0x06U, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U}), + }); + EXPECT_EQ(writer.GetNumTailChunks(inValObj), 6); + TestObjWriter( + writer, + inValObj, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); } } } @@ -1187,6 +1212,20 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) expOutput, 6 * AbiCodecConst::sk_chunkSize() ); + + SimpleObjects::List inValObj = SimpleObjects::List({ + val1, + SimpleObjects::List({ val2, val3 }), + val4, + }); + EXPECT_EQ(writer.GetNumTailChunks(inValObj), 6); + TestObjWriter( + writer, + inValObj, + gsk_testObjWriterHead, + expOutput, + 6 * AbiCodecConst::sk_chunkSize() + ); } // ('uint64', '(uint64,uint64)', 'bytes5') @@ -1252,6 +1291,19 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) expOutput, {} ); + + SimpleObjects::List inValObj = SimpleObjects::List({ + val1, + SimpleObjects::List({ val2, val3 }), + val4, + }); + EXPECT_EQ(writer.GetNumTailChunks(inValObj), 0); + TestObjWriter( + writer, + inValObj, + expOutput, + {} + ); } // ('uint64', '(uint64,bytes)', 'bytes5') @@ -1329,6 +1381,20 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) expOutput, 7 * AbiCodecConst::sk_chunkSize() ); + + SimpleObjects::List inValObj = SimpleObjects::List({ + val1, + SimpleObjects::List({ val2, val4 }), + val4, + }); + EXPECT_EQ(writer.GetNumTailChunks(inValObj), 7); + TestObjWriter( + writer, + inValObj, + gsk_testObjWriterHead, + expOutput, + 7 * AbiCodecConst::sk_chunkSize() + ); } // ('uint64', '(uint64,(uint64,bytes))', 'bytes5') @@ -1421,6 +1487,24 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) expOutput, 9 * AbiCodecConst::sk_chunkSize() ); + + SimpleObjects::List inValObj = SimpleObjects::List({ + val1, + SimpleObjects::List({ + val2, + SimpleObjects::List({ val3, val4 }) + }), + val4 + }); + + EXPECT_EQ(writer.GetNumTailChunks(inValObj), 9); + TestObjWriter( + writer, + inValObj, + gsk_testObjWriterHead, + expOutput, + 9 * AbiCodecConst::sk_chunkSize() + ); } } From ac588e1f7ffc8f450d37121705c48a4dd096093a Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Fri, 24 May 2024 03:25:05 -0700 Subject: [PATCH 12/22] Fixed macos compile err --- include/EclipseMonitor/Eth/AbiWriter.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/EclipseMonitor/Eth/AbiWriter.hpp b/include/EclipseMonitor/Eth/AbiWriter.hpp index b9bbb85..ff04ec3 100644 --- a/include/EclipseMonitor/Eth/AbiWriter.hpp +++ b/include/EclipseMonitor/Eth/AbiWriter.hpp @@ -1553,7 +1553,7 @@ class AbiWriterHeadOnlyBase : public AbiWriterBase return destIt; } - virtual const std::string& GetTypeName() const + virtual const std::string& GetTypeName() const override { return m_typeName; } @@ -1671,7 +1671,7 @@ class AbiWriterHeadTailBase : public AbiWriterBase } } - virtual const std::string& GetTypeName() const + virtual const std::string& GetTypeName() const override { return m_typeName; } From 4b9168212f3935fd619b2280b8e4d0547a163256 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Sun, 26 May 2024 03:05:12 -0700 Subject: [PATCH 13/22] added contract func impl --- include/EclipseMonitor/Eth/AbiCommon.hpp | 1 + .../Eth/Transaction/ContractFunction.hpp | 193 ++++++++++++++ test/src/Main.cpp | 2 +- test/src/TestEthTxnContractFunc.cpp | 242 ++++++++++++++++++ 4 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 include/EclipseMonitor/Eth/Transaction/ContractFunction.hpp create mode 100644 test/src/TestEthTxnContractFunc.cpp diff --git a/include/EclipseMonitor/Eth/AbiCommon.hpp b/include/EclipseMonitor/Eth/AbiCommon.hpp index 38df439..9a3df37 100644 --- a/include/EclipseMonitor/Eth/AbiCommon.hpp +++ b/include/EclipseMonitor/Eth/AbiCommon.hpp @@ -13,6 +13,7 @@ #include #include "../Internal/SimpleObj.hpp" +#include "../Exceptions.hpp" #include "AbiCommon.hpp" diff --git a/include/EclipseMonitor/Eth/Transaction/ContractFunction.hpp b/include/EclipseMonitor/Eth/Transaction/ContractFunction.hpp new file mode 100644 index 0000000..b13fbbe --- /dev/null +++ b/include/EclipseMonitor/Eth/Transaction/ContractFunction.hpp @@ -0,0 +1,193 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#include + +#include +#include +#include + +#include "../../Internal/SimpleObj.hpp" +#include "../AbiCommon.hpp" +#include "../DataTypes.hpp" +#include "../Keccak256.hpp" +#include "DynamicFee.hpp" + + +namespace EclipseMonitor +{ +namespace Eth +{ +namespace Transaction +{ + +class ContractFuncBase +{ +public: + + ContractFuncBase(ContractAddr contractAddr, std::string functionName) : + m_contractAddr(contractAddr), + m_functionName(functionName) + {} + + ContractFuncBase(const ContractFuncBase&) = delete; + + ContractFuncBase(ContractFuncBase&& other) : + m_contractAddr(std::move(other.m_contractAddr)), + m_functionName(std::move(other.m_functionName)) + {} + + ~ContractFuncBase() = default; + + ContractFuncBase& operator=(const ContractFuncBase&) = delete; + + ContractFuncBase& operator=(ContractFuncBase&& other) + { + if (this != &other) + { + m_contractAddr = std::move(other.m_contractAddr); + m_functionName = std::move(other.m_functionName); + } + return *this; + } + + const ContractAddr& GetContractAddr() const + { + return m_contractAddr; + } + + const std::string& GetFuncName() const + { + return m_functionName; + } + + template + std::vector GetFuncSelector(_AbiWriter writer) const + { + std::string funcNameAndType = + m_functionName + writer.GetTypeName(); + std::array funcSelectorHash = Keccak256(funcNameAndType); + + // the function selector is the first 4 bytes of the hash + return { + funcSelectorHash[0], funcSelectorHash[1], + funcSelectorHash[2], funcSelectorHash[3] + }; + } + + DynFee BuildTxn( + const std::vector& data + ) const + { + Transaction::DynFee dynFeeTxn; + dynFeeTxn.get_Destination() = + Internal::Obj::Bytes(m_contractAddr.begin(), m_contractAddr.end()); + dynFeeTxn.get_Data() = Internal::Obj::Bytes(data); + return dynFeeTxn; + } + +protected: + + ContractAddr m_contractAddr; + std::string m_functionName; + +}; // class ContractFuncBase + +template +class ContractFuncStaticDef : public ContractFuncBase +{ +public: // static members: + + using AbiWriter = _AbiWriter; + + using Base = ContractFuncBase; + +public: + ContractFuncStaticDef(ContractAddr contractAddr, std::string functionName) : + Base(contractAddr, functionName), + m_abiWriter() + {} + + ContractFuncStaticDef(const ContractFuncStaticDef&) = delete; + + ContractFuncStaticDef(ContractFuncStaticDef&& other) : + Base(std::forward(other)), + m_abiWriter(std::move(other.m_abiWriter)) + {} + + ~ContractFuncStaticDef() = default; + + ContractFuncStaticDef& operator=(const ContractFuncStaticDef&) = delete; + + ContractFuncStaticDef& operator=(ContractFuncStaticDef&& rhs) + { + Base::operator=(std::forward(rhs)); + if (this != &rhs) + { + m_abiWriter = std::move(rhs.m_abiWriter); + } + return *this; + } + + const AbiWriter& GetAbiWriter() const + { + return m_abiWriter; + } + + std::vector GetFuncSelector() const + { + return Base::GetFuncSelector(m_abiWriter); + } + + template + DynFee CallByTxn(_Args&&... args) const + { + std::vector data = GetFuncSelector(); + + std::tuple<_Args&&...> argsTuple(std::forward<_Args>(args)...); + + if (m_abiWriter.IsDynamicType()) + { + // the parameter tuple is dynamic, so the data is in the tail + + size_t sizeNeeded = m_abiWriter.GetNumTailChunks(argsTuple) * + AbiCodecConst::sk_chunkSize(); + data.reserve(data.size() + sizeNeeded); + // the data might be relocated, so we MUST get the iterator here + auto destIt = + Internal::Obj::ToOutIt(std::back_inserter(data)); + + m_abiWriter.WriteTail(destIt, argsTuple); + } + else + { + // the parameter tuple is static, so the data is in the head + + size_t sizeNeeded = m_abiWriter.GetNumHeadChunks() * + AbiCodecConst::sk_chunkSize(); + data.reserve(data.size() + sizeNeeded); + // the data might be relocated, so we MUST get the iterator here + auto destIt = + Internal::Obj::ToOutIt(std::back_inserter(data)); + + m_abiWriter.WriteHead(destIt, argsTuple, 0); + } + return Base::BuildTxn(data); + } + +private: + + AbiWriter m_abiWriter; + +}; // class ContractFunction + + +} // namespace Transaction +} // namespace Eth +} // namespace EclipseMonitor + diff --git a/test/src/Main.cpp b/test/src/Main.cpp index 02d013c..6be7164 100644 --- a/test/src/Main.cpp +++ b/test/src/Main.cpp @@ -12,7 +12,7 @@ namespace EclipseMonitor_Test int main(int argc, char** argv) { - constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 24; + constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 25; std::cout << "===== EclipseMonitor test program =====" << std::endl; std::cout << std::endl; diff --git a/test/src/TestEthTxnContractFunc.cpp b/test/src/TestEthTxnContractFunc.cpp new file mode 100644 index 0000000..dcac506 --- /dev/null +++ b/test/src/TestEthTxnContractFunc.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + + +#include + +#include +#include +#include +#include + +#include + +#include "Common.hpp" + + +namespace EclipseMonitor_Test +{ + extern size_t g_numOfTestFile; +} + +using namespace EclipseMonitor_Test; + +using namespace EclipseMonitor::Eth; + + +static const ContractAddr gsk_testContractAddr = { + // 0x09616C3d61b3331fc4109a9E41a8BDB7d9776609 + 0x09U, 0x61U, 0x6CU, 0x3DU, 0x61U, 0xB3U, 0x33U, 0x1fU, 0xc4U, 0x10U, + 0x9aU, 0x9EU, 0x41U, 0xa8U, 0xBDU, 0xB7U, 0xd9U, 0x77U, 0x66U, 0x09U, +}; + + +GTEST_TEST(TestEthTxnContractFunc, CountTestFile) +{ + static auto tmp = ++EclipseMonitor_Test::g_numOfTestFile; + (void)tmp; +} + + +GTEST_TEST(TestEthTxnContractFunc, ContractFuncStaticDef_Construct) +{ + using namespace EclipseMonitor::Eth::Transaction; + + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + > + >, + AbiWriter > + >; + + // Move Constructor + { + ContractFuncStaticDef func(gsk_testContractAddr,"foo"); + + EXPECT_EQ(func.GetContractAddr(), gsk_testContractAddr); + EXPECT_EQ(func.GetFuncName(), "foo"); + EXPECT_EQ(func.GetAbiWriter().IsDynamicType(), true); + + ContractFuncStaticDef func2(std::move(func)); + + EXPECT_EQ(func2.GetContractAddr(), gsk_testContractAddr); + EXPECT_EQ(func2.GetFuncName(), "foo"); + EXPECT_EQ(func2.GetAbiWriter().IsDynamicType(), true); + } + + // Move assignment + { + ContractFuncStaticDef func(gsk_testContractAddr,"foo"); + + EXPECT_EQ(func.GetContractAddr(), gsk_testContractAddr); + EXPECT_EQ(func.GetFuncName(), "foo"); + EXPECT_EQ(func.GetAbiWriter().IsDynamicType(), true); + + ContractFuncStaticDef func2(ContractAddr(), "bar"); + + EXPECT_EQ(func2.GetContractAddr(), ContractAddr()); + EXPECT_EQ(func2.GetFuncName(), "bar"); + EXPECT_EQ(func2.GetAbiWriter().IsDynamicType(), true); + + func2 = std::move(func); + + EXPECT_EQ(func2.GetContractAddr(), gsk_testContractAddr); + EXPECT_EQ(func2.GetFuncName(), "foo"); + EXPECT_EQ(func2.GetAbiWriter().IsDynamicType(), true); + } +} + + +GTEST_TEST(TestEthTxnContractFunc, ContractFuncStaticDef_CallByTxn) +{ + using namespace EclipseMonitor::Eth::Transaction; + + // static tuple + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + >, + AbiWriter > + >; + + ContractFuncStaticDef func(gsk_testContractAddr, "foo"); + + EXPECT_EQ(func.GetContractAddr(), gsk_testContractAddr); + EXPECT_EQ(func.GetFuncName(), "foo"); + EXPECT_EQ(func.GetAbiWriter().IsDynamicType(), false); + + auto val1 = SimpleObjects::UInt64(12345); + auto val2 = SimpleObjects::UInt64(54321); + auto val3 = SimpleObjects::UInt64(67890); + auto val4 = SimpleObjects::Bytes({ 0x01U, 0x02U, 0x03U, 0x04U, 0x05U }); + + const SimpleObjects::BaseObj& valRef1 = val1; + const SimpleObjects::BaseObj& valRef2 = val2; + const SimpleObjects::BaseObj& valRef3 = val3; + const SimpleObjects::BaseObj& valRef4 = val4; + + auto txn = func.CallByTxn(valRef1, std::tie(valRef2, valRef3), valRef4); + + txn.SetChainID(1900); + txn.SetNonce(34); + txn.SetMaxPriorFeePerGas(2000000000); + txn.SetMaxFeePerGas(2000000000); + txn.SetGasLimit(100000); + txn.SetAmount(0); + + std::vector expectedSerialized = { + 0xf8U, 0xafU, 0x82U, 0x07U, 0x6cU, 0x22U, 0x84U, 0x77U, 0x35U, 0x94U, 0x00U, 0x84U, 0x77U, 0x35U, 0x94U, 0x00U, + 0x83U, 0x01U, 0x86U, 0xa0U, 0x94U, 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, 0x33U, 0x1fU, 0xc4U, 0x10U, 0x9aU, + 0x9eU, 0x41U, 0xa8U, 0xbdU, 0xb7U, 0xd9U, 0x77U, 0x66U, 0x09U, 0x80U, 0xb8U, 0x84U, 0xfaU, 0x81U, 0xb4U, 0x25U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x09U, 0x32U, + 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0xc0U, + }; + + auto rlp = txn.RlpSerialize(); + EXPECT_EQ(rlp, expectedSerialized); + + std::array expHash = { + // fabb5744e518f43d7ad6629736639020594076dcf1cb155ac374f4854614b086 + 0xfaU, 0xbbU, 0x57U, 0x44U, 0xe5U, 0x18U, 0xf4U, 0x3dU, 0x7aU, 0xd6U, 0x62U, 0x97U, 0x36U, 0x63U, 0x90U, 0x20U, + 0x59U, 0x40U, 0x76U, 0xdcU, 0xf1U, 0xcbU, 0x15U, 0x5aU, 0xc3U, 0x74U, 0xf4U, 0x85U, 0x46U, 0x14U, 0xb0U, 0x86U, + }; + auto rlpHash = txn.Hash(); + EXPECT_EQ(expHash, rlpHash); + } + + // dynamic tuple + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + > + >, + AbiWriter > + >; + + ContractFuncStaticDef func(gsk_testContractAddr, "bar"); + + EXPECT_EQ(func.GetContractAddr(), gsk_testContractAddr); + EXPECT_EQ(func.GetFuncName(), "bar"); + EXPECT_EQ(func.GetAbiWriter().IsDynamicType(), true); + + auto val1 = SimpleObjects::UInt64(12345); + auto val2 = SimpleObjects::UInt64(54321); + auto val3 = SimpleObjects::UInt64(67890); + auto val4 = SimpleObjects::Bytes({ 0x01U, 0x02U, 0x03U, 0x04U, 0x05U }); + + const SimpleObjects::BaseObj& valRef1 = val1; + const SimpleObjects::BaseObj& valRef2 = val2; + const SimpleObjects::BaseObj& valRef3 = val3; + const SimpleObjects::BaseObj& valRef4 = val4; + + auto txn = func.CallByTxn( + valRef1, std::forward_as_tuple(valRef2, std::forward_as_tuple(valRef3, valRef4)), valRef4 + ); + + txn.SetChainID(1900); + txn.SetNonce(34); + txn.SetMaxPriorFeePerGas(2000000000); + txn.SetMaxFeePerGas(2000000000); + txn.SetGasLimit(100000); + txn.SetAmount(1000); + + std::vector expectedSerialized = { + 0xf9U, 0x01U, 0x52U, 0x82U, 0x07U, 0x6cU, 0x22U, 0x84U, 0x77U, 0x35U, 0x94U, 0x00U, 0x84U, 0x77U, 0x35U, 0x94U, + 0x00U, 0x83U, 0x01U, 0x86U, 0xa0U, 0x94U, 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, 0x33U, 0x1fU, 0xc4U, 0x10U, + 0x9aU, 0x9eU, 0x41U, 0xa8U, 0xbdU, 0xb7U, 0xd9U, 0x77U, 0x66U, 0x09U, 0x82U, 0x03U, 0xe8U, 0xb9U, 0x01U, 0x24U, + 0xf4U, 0xdcU, 0x33U, 0x21U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x30U, 0x39U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x60U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0xd4U, 0x31U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x01U, 0x09U, 0x32U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x05U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0xc0U, + }; + + auto rlp = txn.RlpSerialize(); + EXPECT_EQ(rlp, expectedSerialized); + + std::array expHash = { + // 7bf04215d5bc74809640a0ecd111c55e7d07ed14e4db4c6e82036d2ce890542e + 0x7bU, 0xf0U, 0x42U, 0x15U, 0xd5U, 0xbcU, 0x74U, 0x80U, 0x96U, 0x40U, 0xa0U, 0xecU, 0xd1U, 0x11U, 0xc5U, 0x5eU, + 0x7dU, 0x07U, 0xedU, 0x14U, 0xe4U, 0xdbU, 0x4cU, 0x6eU, 0x82U, 0x03U, 0x6dU, 0x2cU, 0xe8U, 0x90U, 0x54U, 0x2eU, + }; + auto rlpHash = txn.Hash(); + EXPECT_EQ(expHash, rlpHash); + } +} + From c311301efd9936f5bb2c7343d2d8b023d62223c3 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Sun, 26 May 2024 03:26:28 -0700 Subject: [PATCH 14/22] github action updated actions/checkout to v4 --- .github/workflows/unit-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 0c32d3d..a17ad0a 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -23,7 +23,7 @@ jobs: std: [ 11, 20 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. From 6dc2b54d56236f2839ff53482a000ab12f26ac90 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Sun, 26 May 2024 17:06:31 -0700 Subject: [PATCH 15/22] testing forward_as_tuple --- test/src/TestEthAbiWriter.cpp | 39 +++++++++++++++++++---------- test/src/TestEthTxnContractFunc.cpp | 13 ++++++++-- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index bde8b5c..0ed9ef2 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -1017,7 +1017,7 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticTuple) const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); - auto inValRef = std::tie(inVal1Ref, inVal2Ref); + auto inValRef = std::forward_as_tuple(inVal1Ref, inVal2Ref); EXPECT_EQ(writer.GetNumTailChunks(inValRef), 0); TestObjWriter( @@ -1112,7 +1112,8 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicTuple) const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); - auto inValRef = std::tie(inVal1Ref, inVal2Ref); + + auto inValRef = std::forward_as_tuple(inVal1Ref, inVal2Ref); EXPECT_EQ(writer.GetNumTailChunks(inValRef), 6); TestObjWriter( @@ -1202,7 +1203,8 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); - auto inValRef = std::tie(inVal1Ref, inVal2Ref, inVal3Ref); + + auto inValRef = std::forward_as_tuple(inVal1Ref, inVal2Ref, inVal3Ref); EXPECT_EQ(writer.GetNumTailChunks(inValRef), 6); TestObjWriter( @@ -1277,14 +1279,17 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) const SimpleObjects::BaseObj& inVal21Ref = std::get<0>(std::get<1>(inVal)); const SimpleObjects::BaseObj& inVal22Ref = std::get<1>(std::get<1>(inVal)); const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); - auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); - auto inValRef = std::tie( + + const auto inValRef = std::forward_as_tuple( inVal1Ref, - inVal2Ref, + std::forward_as_tuple(inVal21Ref, inVal22Ref), inVal3Ref ); + const void* test = nullptr; + test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); EXPECT_EQ(writer.GetNumTailChunks(inValRef), 0); + test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); TestObjWriter( writer, inValRef, @@ -1366,14 +1371,17 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) const SimpleObjects::BaseObj& inVal21Ref = std::get<0>(std::get<1>(inVal)); const SimpleObjects::BaseObj& inVal22Ref = std::get<1>(std::get<1>(inVal)); const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); - auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); - auto inValRef = std::tie( + + const auto inValRef = std::forward_as_tuple( inVal1Ref, - inVal2Ref, + std::forward_as_tuple(inVal21Ref, inVal22Ref), inVal3Ref ); + const void* test = nullptr; + test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); EXPECT_EQ(writer.GetNumTailChunks(inValRef), 7); + test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); TestObjWriter( writer, inValRef, @@ -1471,15 +1479,20 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) const SimpleObjects::BaseObj& inVal221Ref = std::get<0>(std::get<1>(std::get<1>(inVal))); const SimpleObjects::BaseObj& inVal222Ref = std::get<1>(std::get<1>(std::get<1>(inVal))); const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); - auto inVal22Ref = std::tie(inVal221Ref, inVal222Ref); - auto inVal2Ref = std::tie(inVal21Ref, inVal22Ref); - auto inValRef = std::tie( + + const auto inValRef = std::forward_as_tuple( inVal1Ref, - inVal2Ref, + std::forward_as_tuple( + inVal21Ref, + std::forward_as_tuple(inVal221Ref, inVal222Ref) + ), inVal3Ref ); + const void* test = nullptr; + test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); EXPECT_EQ(writer.GetNumTailChunks(inValRef), 9); + test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); TestObjWriter( writer, inValRef, diff --git a/test/src/TestEthTxnContractFunc.cpp b/test/src/TestEthTxnContractFunc.cpp index dcac506..8228e3b 100644 --- a/test/src/TestEthTxnContractFunc.cpp +++ b/test/src/TestEthTxnContractFunc.cpp @@ -125,7 +125,11 @@ GTEST_TEST(TestEthTxnContractFunc, ContractFuncStaticDef_CallByTxn) const SimpleObjects::BaseObj& valRef3 = val3; const SimpleObjects::BaseObj& valRef4 = val4; - auto txn = func.CallByTxn(valRef1, std::tie(valRef2, valRef3), valRef4); + auto txn = func.CallByTxn( + valRef1, + std::forward_as_tuple(valRef2, valRef3), + valRef4 + ); txn.SetChainID(1900); txn.SetNonce(34); @@ -192,7 +196,12 @@ GTEST_TEST(TestEthTxnContractFunc, ContractFuncStaticDef_CallByTxn) const SimpleObjects::BaseObj& valRef4 = val4; auto txn = func.CallByTxn( - valRef1, std::forward_as_tuple(valRef2, std::forward_as_tuple(valRef3, valRef4)), valRef4 + valRef1, + std::forward_as_tuple( + valRef2, + std::forward_as_tuple(valRef3, valRef4) + ), + valRef4 ); txn.SetChainID(1900); From 9e9b48c8e27d4d09ede367abf8e4c2ca2544ae58 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Sun, 26 May 2024 17:28:10 -0700 Subject: [PATCH 16/22] fixed issues related to forward_as_tuple --- .../Eth/Transaction/ContractFunction.hpp | 18 ++- test/src/TestEthAbiWriter.cpp | 111 +++++++++++------- 2 files changed, 80 insertions(+), 49 deletions(-) diff --git a/include/EclipseMonitor/Eth/Transaction/ContractFunction.hpp b/include/EclipseMonitor/Eth/Transaction/ContractFunction.hpp index b13fbbe..09f0e94 100644 --- a/include/EclipseMonitor/Eth/Transaction/ContractFunction.hpp +++ b/include/EclipseMonitor/Eth/Transaction/ContractFunction.hpp @@ -149,20 +149,22 @@ class ContractFuncStaticDef : public ContractFuncBase { std::vector data = GetFuncSelector(); - std::tuple<_Args&&...> argsTuple(std::forward<_Args>(args)...); - if (m_abiWriter.IsDynamicType()) { // the parameter tuple is dynamic, so the data is in the tail - size_t sizeNeeded = m_abiWriter.GetNumTailChunks(argsTuple) * - AbiCodecConst::sk_chunkSize(); + size_t sizeNeeded = m_abiWriter.GetNumTailChunks( + std::forward_as_tuple(std::forward<_Args>(args)...) + ) * AbiCodecConst::sk_chunkSize(); data.reserve(data.size() + sizeNeeded); // the data might be relocated, so we MUST get the iterator here auto destIt = Internal::Obj::ToOutIt(std::back_inserter(data)); - m_abiWriter.WriteTail(destIt, argsTuple); + m_abiWriter.WriteTail( + destIt, + std::forward_as_tuple(std::forward<_Args>(args)...) + ); } else { @@ -175,7 +177,11 @@ class ContractFuncStaticDef : public ContractFuncBase auto destIt = Internal::Obj::ToOutIt(std::back_inserter(data)); - m_abiWriter.WriteHead(destIt, argsTuple, 0); + m_abiWriter.WriteHead( + destIt, + std::forward_as_tuple(std::forward<_Args>(args)...), + 0 + ); } return Base::BuildTxn(data); } diff --git a/test/src/TestEthAbiWriter.cpp b/test/src/TestEthAbiWriter.cpp index 0ed9ef2..aea5c74 100644 --- a/test/src/TestEthAbiWriter.cpp +++ b/test/src/TestEthAbiWriter.cpp @@ -1017,12 +1017,16 @@ GTEST_TEST(TestEthAbiWriter, WriteStaticTuple) const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); - auto inValRef = std::forward_as_tuple(inVal1Ref, inVal2Ref); - EXPECT_EQ(writer.GetNumTailChunks(inValRef), 0); + EXPECT_EQ( + writer.GetNumTailChunks( + std::forward_as_tuple(inVal1Ref, inVal2Ref) + ), + 0 + ); TestObjWriter( writer, - inValRef, + std::forward_as_tuple(inVal1Ref, inVal2Ref), expOutput, {} ); @@ -1113,12 +1117,15 @@ GTEST_TEST(TestEthAbiWriter, WriteDynamicTuple) const SimpleObjects::BaseObj& inVal1Ref = std::get<0>(inVal); const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); - auto inValRef = std::forward_as_tuple(inVal1Ref, inVal2Ref); - - EXPECT_EQ(writer.GetNumTailChunks(inValRef), 6); + EXPECT_EQ( + writer.GetNumTailChunks( + std::forward_as_tuple(inVal1Ref, inVal2Ref) + ), + 6 + ); TestObjWriter( writer, - inValRef, + std::forward_as_tuple(inVal1Ref, inVal2Ref), gsk_testObjWriterHead, expOutput, 6 * AbiCodecConst::sk_chunkSize() @@ -1204,12 +1211,15 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) const SimpleObjects::BaseObj& inVal2Ref = std::get<1>(inVal); const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); - auto inValRef = std::forward_as_tuple(inVal1Ref, inVal2Ref, inVal3Ref); - - EXPECT_EQ(writer.GetNumTailChunks(inValRef), 6); + EXPECT_EQ( + writer.GetNumTailChunks( + std::forward_as_tuple(inVal1Ref, inVal2Ref, inVal3Ref) + ), + 6 + ); TestObjWriter( writer, - inValRef, + std::forward_as_tuple(inVal1Ref, inVal2Ref, inVal3Ref), gsk_testObjWriterHead, expOutput, 6 * AbiCodecConst::sk_chunkSize() @@ -1280,19 +1290,23 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) const SimpleObjects::BaseObj& inVal22Ref = std::get<1>(std::get<1>(inVal)); const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); - const auto inValRef = std::forward_as_tuple( - inVal1Ref, - std::forward_as_tuple(inVal21Ref, inVal22Ref), - inVal3Ref + EXPECT_EQ( + writer.GetNumTailChunks( + std::forward_as_tuple( + inVal1Ref, + std::forward_as_tuple(inVal21Ref, inVal22Ref), + inVal3Ref + ) + ), + 0 ); - - const void* test = nullptr; - test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); - EXPECT_EQ(writer.GetNumTailChunks(inValRef), 0); - test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); TestObjWriter( writer, - inValRef, + std::forward_as_tuple( + inVal1Ref, + std::forward_as_tuple(inVal21Ref, inVal22Ref), + inVal3Ref + ), expOutput, {} ); @@ -1372,19 +1386,23 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) const SimpleObjects::BaseObj& inVal22Ref = std::get<1>(std::get<1>(inVal)); const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); - const auto inValRef = std::forward_as_tuple( - inVal1Ref, - std::forward_as_tuple(inVal21Ref, inVal22Ref), - inVal3Ref + EXPECT_EQ( + writer.GetNumTailChunks( + std::forward_as_tuple( + inVal1Ref, + std::forward_as_tuple(inVal21Ref, inVal22Ref), + inVal3Ref + ) + ), + 7 ); - - const void* test = nullptr; - test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); - EXPECT_EQ(writer.GetNumTailChunks(inValRef), 7); - test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); TestObjWriter( writer, - inValRef, + std::forward_as_tuple( + inVal1Ref, + std::forward_as_tuple(inVal21Ref, inVal22Ref), + inVal3Ref + ), gsk_testObjWriterHead, expOutput, 7 * AbiCodecConst::sk_chunkSize() @@ -1480,22 +1498,29 @@ GTEST_TEST(TestEthAbiWriter, WriteMixedTuple) const SimpleObjects::BaseObj& inVal222Ref = std::get<1>(std::get<1>(std::get<1>(inVal))); const SimpleObjects::BaseObj& inVal3Ref = std::get<2>(inVal); - const auto inValRef = std::forward_as_tuple( - inVal1Ref, - std::forward_as_tuple( - inVal21Ref, - std::forward_as_tuple(inVal221Ref, inVal222Ref) + EXPECT_EQ( + writer.GetNumTailChunks( + std::forward_as_tuple( + inVal1Ref, + std::forward_as_tuple( + inVal21Ref, + std::forward_as_tuple(inVal221Ref, inVal222Ref) + ), + inVal3Ref + ) ), - inVal3Ref + 9 ); - - const void* test = nullptr; - test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); - EXPECT_EQ(writer.GetNumTailChunks(inValRef), 9); - test = &(std::get<0>(std::get<1>(inValRef))); std::cout << test << std::endl; ASSERT_NE(test, nullptr); TestObjWriter( writer, - inValRef, + std::forward_as_tuple( + inVal1Ref, + std::forward_as_tuple( + inVal21Ref, + std::forward_as_tuple(inVal221Ref, inVal222Ref) + ), + inVal3Ref + ), gsk_testObjWriterHead, expOutput, 9 * AbiCodecConst::sk_chunkSize() From a3ccc07d3becc650b8feb33448967713abca29de Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Wed, 29 May 2024 00:58:40 -0700 Subject: [PATCH 17/22] Added impl for ecdsa signing algorithm --- .../EclipseMonitor/Eth/Transaction/Ecdsa.hpp | 741 ++++++++++++++++++ include/EclipseMonitor/Internal/Tls.hpp | 27 + test/CMakeLists.txt | 32 + test/src/Main.cpp | 2 +- test/src/TestEthTxnEcdsa.cpp | 340 ++++++++ 5 files changed, 1141 insertions(+), 1 deletion(-) create mode 100644 include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp create mode 100644 include/EclipseMonitor/Internal/Tls.hpp create mode 100644 test/src/TestEthTxnEcdsa.cpp diff --git a/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp b/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp new file mode 100644 index 0000000..3e05f37 --- /dev/null +++ b/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp @@ -0,0 +1,741 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#include + +#include +#include +#include + +#include +#include +#include + +#include "../../Internal/SimpleObj.hpp" +#include "../../Internal/Tls.hpp" +#include "../../Exceptions.hpp" +#include "../DataTypes.hpp" +#include "../Keccak256.hpp" +#include "DynamicFee.hpp" + + +namespace EclipseMonitor +{ +namespace Eth +{ +namespace Transaction +{ + + +inline const Internal::Tls::BigNum& GetBigNum1() +{ + static const Internal::Tls::BigNum sk_2(1); + return sk_2; +} + +inline const Internal::Tls::BigNum& GetBigNum2() +{ + static const Internal::Tls::BigNum sk_2(2); + return sk_2; +} + +inline const Internal::Tls::BigNum& GetBigNum3() +{ + static const Internal::Tls::BigNum sk_3(3); + return sk_3; +} + +inline const Internal::Tls::BigNum& GetBigNum4() +{ + static const Internal::Tls::BigNum sk_4(4); + return sk_4; +} + +inline const Internal::Tls::BigNum& GetBigNum8() +{ + static const Internal::Tls::BigNum sk_8(8); + return sk_8; +} + + +/** + * @brief This function is derived from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py + * + */ +inline Internal::Tls::BigNum JacobianInv( + const Internal::Tls::BigNum& a, + const Internal::Tls::BigNum& n +) +{ + const auto& sk_0 = Internal::Tls::BigNum::Zero(); + const auto& sk_1 = GetBigNum1(); + + // if a == 0: + if (a == 0) + { + // return 0 + return sk_0; + } + + // lm, hm = 1, 0 + auto lm = sk_1; + auto hm = sk_0; + + // low, high = a % n, n + auto low = Internal::Tls::Mod(a, n); + auto high = n; + + // while low > 1: + while (low > 1) + { + // r = high // low + auto r = high / low; + + // nm, new = hm - lm * r, high - low * r + // nm = hm - lm * r + auto nm = hm - (lm * r); + // new = high - low * r + auto new_ = high - (low * r); + + // lm, low, hm, high = nm, new, lm, low + // hm = lm + hm = lm; // take the value of lm before it is overwritten + // lm = nm + lm = std::move(nm); // nm is no longer used + + // high = low + high = low; // take the value of low before it is overwritten + // low = new + low = std::move(new_); // new_ is no longer used + } + + // return lm % n + return Internal::Tls::Mod(lm, n); +} + + +/** + * @brief This function is derived from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py + * + */ +inline +std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +JacobianDouble( + const Internal::Tls::BigNum& x, + const Internal::Tls::BigNum& y, + const Internal::Tls::BigNum& z, + const Internal::Tls::BigNum& curveA, + const Internal::Tls::BigNum& curveP +) +{ + const auto& sk_0 = Internal::Tls::BigNum::Zero(); + + // NOTE: python's `%` operator has different behavior than C++'s `%` operator + // python's `%` operator is modular reduction, while C++'s `%` operator is remainder + // so we need to use `Internal::Tls::Mod` to get the modular reduction + + // if not p[1]: + if (y == 0) + { + // return (0, 0, 0) + return std::make_tuple(sk_0, sk_0, sk_0); + } + + // ysq = (p[1] ** 2) % P + auto ysq = (y * y); + ysq = Internal::Tls::Mod(ysq, curveP); + + // S = (4 * p[0] * ysq) % P + auto s = (x * 4); + s *= ysq; + s = Internal::Tls::Mod(s, curveP); + + // M = (3 * p[0] ** 2 + A * p[2] ** 4) % P + // 3 * p[0] ** 2 + auto m1 = (x * x); + m1 *= 3; + // A * p[2] ** 4 + auto m2 = (z * z); + m2 *= m2; + m2 *= curveA; + auto m = m1 + m2; + m = Internal::Tls::Mod(m, curveP); + + // nx = (M**2 - 2 * S) % P + auto nx = (m * m); + nx -= (s * 2); + nx = Internal::Tls::Mod(nx, curveP); + + // ny = (M * (S - nx) - 8 * ysq**2) % P + auto ny = (s - nx); + ny *= m; + ny -= ((ysq * ysq) * 8); + ny = Internal::Tls::Mod(ny, curveP); + + // nz = (2 * p[1] * p[2]) % P + auto nz = (y * 2); + nz *= z; + nz = Internal::Tls::Mod(nz, curveP); + + // return (nx, ny, nz) + return std::make_tuple(std::move(nx), std::move(ny), std::move(nz)); +} + + +inline +std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +JacobianDouble( + const std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum + >& p, + const Internal::Tls::BigNum& curveA, + const Internal::Tls::BigNum& curveP +) +{ + return JacobianDouble( + std::get<0>(p), + std::get<1>(p), + std::get<2>(p), + curveA, + curveP + ); +} + + +/** + * @brief This function is derived from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py + * + */ +inline +std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +JacobianAdd( + const Internal::Tls::BigNum& pX, + const Internal::Tls::BigNum& pY, + const Internal::Tls::BigNum& pZ, + const Internal::Tls::BigNum& qX, + const Internal::Tls::BigNum& qY, + const Internal::Tls::BigNum& qZ, + const Internal::Tls::BigNum& curveA, + const Internal::Tls::BigNum& curveP +) +{ + const auto& sk_0 = Internal::Tls::BigNum::Zero(); + const auto& sk_1 = GetBigNum1(); + + // if not p[1]: + if (pY == 0) + { + // return q + return std::make_tuple(qX, qY, qZ); + } + + // if not q[1]: + if (qY == 0) + { + // return p + return std::make_tuple(pX, pY, pZ); + } + + // U1 = (p[0] * q[2] ** 2) % P + auto u1 = (qZ * qZ); + u1 *= pX; + u1 = Internal::Tls::Mod(u1, curveP); + + // U2 = (q[0] * p[2] ** 2) % P + auto u2 = (pZ * pZ); + u2 *= qX; + u2 = Internal::Tls::Mod(u2, curveP); + + // S1 = (p[1] * q[2] ** 3) % P + auto s1 = (qZ * qZ); + s1 *= qZ; + s1 *= pY; + s1 = Internal::Tls::Mod(s1, curveP); + + // S2 = (q[1] * p[2] ** 3) % P + auto s2 = (pZ * pZ); + s2 *= pZ; + s2 *= qY; + s2 = Internal::Tls::Mod(s2, curveP); + + // if U1 == U2: + if (u1 == u2) + { + // if S1 != S2: + if (s1 != s2) + { + // return (0, 0, 1) + return std::make_tuple(sk_0, sk_0, sk_1); + } + else + { + // return jacobian_double(p) + return JacobianDouble(pX, pY, pZ, curveA, curveP); + } + } + + // H = U2 - U1 + auto h = u2 - u1; + + // R = S2 - S1 + auto r = s2 - s1; + + // H2 = (H * H) % P + auto h2 = (h * h); + h2 = Internal::Tls::Mod(h2, curveP); + + // H3 = (H * H2) % P + auto h3 = (h * h2); + h3 = Internal::Tls::Mod(h3, curveP); + + // U1H2 = (U1 * H2) % P + auto u1h2 = (u1 * h2); + u1h2 = Internal::Tls::Mod(u1h2, curveP); + + // nx = (R**2 - H3 - 2 * U1H2) % P + auto nx = (r * r); + nx -= h3; + nx -= (u1h2 * 2); + nx = Internal::Tls::Mod(nx, curveP); + + // ny = (R * (U1H2 - nx) - S1 * H3) % P + auto ny = (u1h2 - nx); + ny *= r; + ny -= (s1 * h3); + ny = Internal::Tls::Mod(ny, curveP); + + // nz = (H * p[2] * q[2]) % P + auto nz = (h * pZ); + nz *= qZ; + nz = Internal::Tls::Mod(nz, curveP); + + // return (nx, ny, nz) + return std::make_tuple(std::move(nx), std::move(ny), std::move(nz)); +} + + +inline +std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +JacobianAdd( + const std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum + >& p, + const std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum + >& q, + const Internal::Tls::BigNum& curveA, + const Internal::Tls::BigNum& curveP +) +{ + return JacobianAdd( + std::get<0>(p), + std::get<1>(p), + std::get<2>(p), + std::get<0>(q), + std::get<1>(q), + std::get<2>(q), + curveA, + curveP + ); +} + + +/** + * @brief This function is derived from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py + * + */ +inline +std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +JacobianMultiply( + const Internal::Tls::BigNum& aX, + const Internal::Tls::BigNum& aY, + const Internal::Tls::BigNum& aZ, + const Internal::Tls::BigNum& num, + const Internal::Tls::BigNum& curveA, + const Internal::Tls::BigNum& curveN, + const Internal::Tls::BigNum& curveP +) +{ + const auto& sk_0 = Internal::Tls::BigNum::Zero(); + const auto& sk_1 = GetBigNum1(); + + // if a[1] == 0 or n == 0: + if (aY == 0 || num == 0) + { + // return (0, 0, 1) + return std::make_tuple(sk_0, sk_0, sk_1); + } + + // if n == 1: + if (num == 1) + { + // return a + return std::make_tuple(aX, aY, aZ); + } + + // if n < 0 or n >= N: + if (num < 0 || num >= curveN) + { + // return jacobian_multiply(a, n % N) + return JacobianMultiply( + aX, aY, aZ, + Internal::Tls::Mod(num, curveN), + curveA, curveN, curveP + ); + } + + // if (n % 2) == 0: + uint8_t numMod2 = num.Mod(uint8_t(2)); + if (numMod2 == 0) + { + // return jacobian_double(jacobian_multiply(a, n // 2)) + auto tmp = JacobianMultiply(aX, aY, aZ, (num / 2), curveA, curveN, curveP); + return JacobianDouble(std::get<0>(tmp), std::get<1>(tmp), std::get<2>(tmp), curveA, curveP); + } + // elif (n % 2) == 1: + else if (numMod2 == 1) + { + // return jacobian_add(jacobian_double(jacobian_multiply(a, n // 2)), a) + auto tmp = JacobianDouble( + JacobianMultiply(aX, aY, aZ, (num / 2), curveA, curveN, curveP), + curveA, + curveP + ); + return JacobianAdd( + std::get<0>(tmp), + std::get<1>(tmp), + std::get<2>(tmp), + aX, + aY, + aZ, + curveA, + curveP + ); + } + // else: + { + // raise Exception("Invariant: Unreachable code path") + throw Exception("Invariant: Unreachable code path"); + } +} + + +/** + * @brief This function is derived from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py + * + */ +inline +std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +FromJacobian( + const Internal::Tls::BigNum& pX, + const Internal::Tls::BigNum& pY, + const Internal::Tls::BigNum& pZ, + const Internal::Tls::BigNum& curveP +) +{ + // z = inv(p[2], P) + auto z = JacobianInv(pZ, curveP); + + // return ((p[0] * z**2) % P, (p[1] * z**3) % P) + // (p[0] * z**2) % P + auto zsqr = (z * z); + auto nx = (pX * zsqr); + nx = Internal::Tls::Mod(nx, curveP); + // (p[1] * z**3) % P + auto ny = (z * zsqr); + ny *= pY; + ny = Internal::Tls::Mod(ny, curveP); + + return std::make_tuple(std::move(nx), std::move(ny)); +} + + +inline +std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +FromJacobian( + const std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum, + Internal::Tls::BigNum + >& p, + const Internal::Tls::BigNum& curveP +) +{ + return FromJacobian( + std::get<0>(p), + std::get<1>(p), + std::get<2>(p), + curveP + ); +} + + +/** + * @brief This function is derived from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py + * + */ +inline +std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +JacobianFastMultiply( + const Internal::Tls::BigNum& aX, + const Internal::Tls::BigNum& aY, + const Internal::Tls::BigNum& num, + const Internal::Tls::BigNum& curveA, + const Internal::Tls::BigNum& curveN, + const Internal::Tls::BigNum& curveP +) +{ + const auto& sk_1 = GetBigNum1(); + + // return from_jacobian(jacobian_multiply(to_jacobian(a), n)) + std::tuple< + Internal::Tls::BigNum, + Internal::Tls::BigNum + > ret; + + return FromJacobian( + JacobianMultiply(aX, aY, sk_1, num, curveA, curveN, curveP), + curveP + ); +} + + +/** + * @brief This function is derived from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/ecdsa.py + * + */ +template< + typename _HashCtnT, bool _HashCtnSec, + typename _PKeyCtnT, bool _PKeyCtnSec +> +inline Internal::Tls::BigNum DeterministicGenerateK( + const Internal::Tls::ContCtnReadOnlyRef<_HashCtnT, _HashCtnSec>& hashCtnRef, + const Internal::Tls::ContCtnReadOnlyRef<_PKeyCtnT, _PKeyCtnSec>& pKeyCtnRef +) +{ + // digest_fn: Callable[[], Any] = hashlib.sha256, + + // v_0 = b"\x01" * 32 + static const Internal::Tls::SecretVector sk_v0 = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + }; + // k_0 = b"\x00" * 32 + static const Internal::Tls::SecretVector sk_k0 = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + static const uint8_t sk_00[] = { 0x00U, }; + static const uint8_t sk_01[] = { 0x01U, }; + + // k_1 = hmac.new(k_0, v_0 + b"\x00" + private_key_bytes + msg_hash, digest_fn).digest() + auto k1 = Internal::Tls::HmacerBase( + Internal::Tls::GetMdInfo(Internal::Tls::HashType::SHA256), + Internal::Tls::CtnFullR(sk_k0) + ).Update( + Internal::Tls::CtnFullR(sk_v0) + ).Update( + Internal::Tls::CtnFullR(sk_00) + ).Update( + pKeyCtnRef + ).Update( + hashCtnRef + ).template Finish >(); + + // v_1 = hmac.new(k_1, v_0, digest_fn).digest() + auto v1 = Internal::Tls::HmacerBase( + Internal::Tls::GetMdInfo(Internal::Tls::HashType::SHA256), + Internal::Tls::CtnFullR(k1) + ).Update( + Internal::Tls::CtnFullR(sk_v0) + ).template Finish >(); + + // k_2 = hmac.new(k_1, v_1 + b"\x01" + private_key_bytes + msg_hash, digest_fn).digest() + auto k2 = Internal::Tls::HmacerBase( + Internal::Tls::GetMdInfo(Internal::Tls::HashType::SHA256), + Internal::Tls::CtnFullR(k1) + ).Update( + Internal::Tls::CtnFullR(v1) + ).Update( + Internal::Tls::CtnFullR(sk_01) + ).Update( + pKeyCtnRef + ).Update( + hashCtnRef + ).template Finish >(); + + // v_2 = hmac.new(k_2, v_1, digest_fn).digest() + auto v2 = Internal::Tls::HmacerBase( + Internal::Tls::GetMdInfo(Internal::Tls::HashType::SHA256), + Internal::Tls::CtnFullR(k2) + ).Update( + Internal::Tls::CtnFullR(v1) + ).template Finish >(); + + // kb = hmac.new(k_2, v_2, digest_fn).digest() + auto kb = Internal::Tls::HmacerBase( + Internal::Tls::GetMdInfo(Internal::Tls::HashType::SHA256), + Internal::Tls::CtnFullR(k2) + ).Update( + Internal::Tls::CtnFullR(v2) + ).template Finish >(); + + // k = big_endian_to_int(kb) + auto k = Internal::Tls::BigNum( + Internal::Tls::CtnFullR(kb), + true, // isPositive + false // isLittleEndian + ); + + // return k + return k; +} + + +/** + * @brief This function is derived from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/ecdsa.py + * + */ +template< + typename _HashCtnT, bool _HashCtnSec, + typename _PKeyCtnT, bool _PKeyCtnSec +> +inline +std::tuple< + uint8_t, + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +EcdsaRawSign( + const Internal::Tls::ContCtnReadOnlyRef<_HashCtnT, _HashCtnSec>& hashCtnRef, + const Internal::Tls::ContCtnReadOnlyRef<_PKeyCtnT, _PKeyCtnSec>& pKeyCtnRef, + const Internal::Tls::BigNum& privKeyNum, + const Internal::Tls::BigNum& curveA, + const Internal::Tls::BigNum& curveGx, + const Internal::Tls::BigNum& curveGy, + const Internal::Tls::BigNum& curveN, + const Internal::Tls::BigNum& curveP +) +{ + // z = big_endian_to_int(msg_hash) + auto z = Internal::Tls::BigNum( + hashCtnRef, + true, // isPositive + false // isLittleEndian + ); + + // k = deterministic_generate_k(msg_hash, private_key_bytes) + auto k = DeterministicGenerateK(hashCtnRef, pKeyCtnRef); + + // r, y = fast_multiply(G, k) + Internal::Tls::BigNum r; + Internal::Tls::BigNum y; + std::tie(r, y) = + JacobianFastMultiply(curveGx, curveGy, k, curveA, curveN, curveP); + + // s_raw = inv(k, N) * (z + r * big_endian_to_int(private_key_bytes)) % N + auto sRaw = JacobianInv(k, curveN); + sRaw *= (z + (r * privKeyNum)); + sRaw = Internal::Tls::Mod(sRaw, curveN); + + // v = 27 + ((y % 2) ^ (0 if s_raw * 2 < N else 1)) + // (y % 2) + uint8_t v21 = y.Mod(uint8_t(2)); + // (0 if s_raw * 2 < N else 1) + uint8_t v22 = ((sRaw * 2) < curveN) ? 0 : 1; + uint8_t v = 27 + (v21 ^ v22); + + // s = s_raw if s_raw * 2 < N else N - s_raw + auto s = ((sRaw * 2) < curveN) ? sRaw : (curveN - sRaw); + + // return v - 27, r, s + return std::make_tuple(v - 27, std::move(r), std::move(s)); +} + + +template< + typename _HashCtnT, bool _HashCtnSec +> +inline +std::tuple< + uint8_t, + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +EcdsaRawSign( + const Internal::Tls::ContCtnReadOnlyRef<_HashCtnT, _HashCtnSec>& hashCtnRef, + const Internal::Tls::BigNum& privKeyNum, + const Internal::Tls::BigNum& curveA, + const Internal::Tls::BigNum& curveGx, + const Internal::Tls::BigNum& curveGy, + const Internal::Tls::BigNum& curveN, + const Internal::Tls::BigNum& curveP +) +{ + auto privKeyBytes = privKeyNum.SecretBytes(); + + return EcdsaRawSign( + hashCtnRef, + Internal::Tls::CtnFullR(privKeyBytes), + privKeyNum, + curveA, + curveGx, + curveGy, + curveN, + curveP + ); +} + + +} // namespace Transaction +} // namespace Eth +} // namespace EclipseMonitor + diff --git a/include/EclipseMonitor/Internal/Tls.hpp b/include/EclipseMonitor/Internal/Tls.hpp new file mode 100644 index 0000000..d61dd74 --- /dev/null +++ b/include/EclipseMonitor/Internal/Tls.hpp @@ -0,0 +1,27 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#include + + +namespace EclipseMonitor +{ +namespace Internal +{ + + +#ifndef MBEDTLSCPP_CUSTOMIZED_NAMESPACE +namespace Tls = ::mbedTLScpp; +#else +namespace Tls = ::MBEDTLSCPP_CUSTOMIZED_NAMESPACE; +#endif // !MBEDTLSCPP_CUSTOMIZED_NAMESPACE + + +} // namespace Internal +} // namespace EclipseMonitor + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0ba6f2e..0048ba8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -67,6 +67,36 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(git_simplerlp) +## mbedTLScpp +FetchContent_Declare( + git_mbedtlscpp + GIT_REPOSITORY https://github.com/zhenghaven/mbedTLScpp.git + GIT_TAG dev +) +FetchContent_MakeAvailable(git_mbedtlscpp) + +## MbedTLS +set(ENABLE_TESTING OFF CACHE BOOL "Build mbed TLS tests." FORCE) +set(ENABLE_PROGRAMS OFF CACHE BOOL "Build mbed TLS programs." FORCE) +if (MSVC) + set( + MSVC_STATIC_RUNTIME + ON + CACHE BOOL + "Build the libraries with /MT compiler flag" + FORCE + ) +endif() +FetchContent_Declare( + git_mbedtls + GIT_REPOSITORY https://github.com/zhenghaven/mbedtls.git + GIT_TAG decent-enclave-v3.5.2 +) +FetchContent_MakeAvailable(git_mbedtls) +mbedTLScpp_Decentize_Normal(mbedcrypto) +mbedTLScpp_Decentize_Normal(mbedx509) +mbedTLScpp_Decentize_Normal(mbedtls) + ################################################################################ # Adding testing executable ################################################################################ @@ -87,6 +117,8 @@ target_link_libraries(EclipseMonitor_test SimpleRlp SimpleObjects SimpleUtf + mbedtls + mbedTLScpp gtest ) target_include_directories(EclipseMonitor_test diff --git a/test/src/Main.cpp b/test/src/Main.cpp index 6be7164..3c111bf 100644 --- a/test/src/Main.cpp +++ b/test/src/Main.cpp @@ -12,7 +12,7 @@ namespace EclipseMonitor_Test int main(int argc, char** argv) { - constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 25; + constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 26; std::cout << "===== EclipseMonitor test program =====" << std::endl; std::cout << std::endl; diff --git a/test/src/TestEthTxnEcdsa.cpp b/test/src/TestEthTxnEcdsa.cpp new file mode 100644 index 0000000..dd128b1 --- /dev/null +++ b/test/src/TestEthTxnEcdsa.cpp @@ -0,0 +1,340 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + + +#include + +#include + +#include "Common.hpp" + + +namespace EclipseMonitor_Test +{ + extern size_t g_numOfTestFile; +} + +using namespace EclipseMonitor_Test; + +using namespace EclipseMonitor::Eth; + + +GTEST_TEST(TestEthTxnEcdsa, CountTestFile) +{ + static auto tmp = ++EclipseMonitor_Test::g_numOfTestFile; + (void)tmp; +} + + +/** + * @brief These constants are taken from + * https://github.com/ethereum/eth-keys/blob/main/eth_keys/constants.py + * + */ +static const mbedTLScpp::BigNum sk_a = mbedTLScpp::BigNum(0); +static const mbedTLScpp::BigNum sk_gx = mbedTLScpp::BigNum( + "55066263022277343669578718895168534326250603453777594175500187360389116729240" +); +static const mbedTLScpp::BigNum sk_gy = mbedTLScpp::BigNum( + "32670510020758816978083085130507043184471273380659243275938904335757337482424" +); +static const mbedTLScpp::BigNum sk_n = mbedTLScpp::BigNum( + "115792089237316195423570985008687907852837564279074904382605163141518161494337" +); +static const mbedTLScpp::BigNum sk_p = mbedTLScpp::BigNum( + // 2**256 - 2**32 - 977 + "115792089237316195423570985008687907853269984665640564039457584007908834671663" +); + + +GTEST_TEST(TestEthTxnEcdsa, JacobianInv) +{ + { + mbedTLScpp::BigNum a(0); + mbedTLScpp::BigNum n(1); + + mbedTLScpp::BigNum expOut(0); + + mbedTLScpp::BigNum out = Transaction::JacobianInv(a, n); + + EXPECT_EQ(out, expOut); + } + + { + mbedTLScpp::BigNum a("99894079990873359630376909829698352524809824171660155898784064965080081534450"); + mbedTLScpp::BigNum n("115792089237316195423570985008687907852837564279074904382605163141518161494337"); + + mbedTLScpp::BigNum expOut("48205471765048878769112722906746240055246381653081666392697376400160573977909"); + + mbedTLScpp::BigNum out = Transaction::JacobianInv(a, n); + + EXPECT_EQ(out, expOut); + } +} + + +GTEST_TEST(TestEthTxnEcdsa, JacobianDouble) +{ + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > p = std::make_tuple( + mbedTLScpp::BigNum(100), + mbedTLScpp::BigNum(0), + mbedTLScpp::BigNum(100) + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum(0), + mbedTLScpp::BigNum(0), + mbedTLScpp::BigNum(0) + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianDouble(p, sk_a, sk_p); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } + + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > p = std::make_tuple( + mbedTLScpp::BigNum("36565028958672047985467739768252663549060054249238279599776773157982456747388"), + mbedTLScpp::BigNum("26794385526983480225932345657229967401317403296218116044243872393481628346196"), + mbedTLScpp::BigNum("87336916788907964076621474987505410415813069772338947919355744021760658771529") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum("36036075634199074546129661232420548016519448468337978806663212807452641890103"), + mbedTLScpp::BigNum("113492477844519581031850584107650655330363714502423581674624945975139107167263"), + mbedTLScpp::BigNum("91707237065990426039921931734295629131137542276573823064360208304576683970847") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianDouble(p, sk_a, sk_p); + + std::cout << std::get<0>(out).Dec() << std::endl; + std::cout << std::get<1>(out).Dec() << std::endl; + std::cout << std::get<2>(out).Dec() << std::endl; + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } +} + + +GTEST_TEST(TestEthTxnEcdsa, JacobianAdd) +{ + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > p = std::make_tuple( + mbedTLScpp::BigNum("34832002147657580523623959555951687751971572118948814154019390413440244237266"), + mbedTLScpp::BigNum("10575639833043288494724950414056322299517027438414803915230266464097535610696"), + mbedTLScpp::BigNum("64471558224392339448117885746082193748654986342107253452511469024820282463724") + ); + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > q = std::make_tuple( + mbedTLScpp::BigNum("55066263022277343669578718895168534326250603453777594175500187360389116729240"), + mbedTLScpp::BigNum("32670510020758816978083085130507043184471273380659243275938904335757337482424"), + mbedTLScpp::BigNum(1) + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum("36565028958672047985467739768252663549060054249238279599776773157982456747388"), + mbedTLScpp::BigNum("26794385526983480225932345657229967401317403296218116044243872393481628346196"), + mbedTLScpp::BigNum("87336916788907964076621474987505410415813069772338947919355744021760658771529") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianAdd(p, q, sk_a, sk_p); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } +} + + +GTEST_TEST(TestEthTxnEcdsa, JacobianMultiply) +{ + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > a = std::make_tuple( + mbedTLScpp::BigNum("55066263022277343669578718895168534326250603453777594175500187360389116729240"), + mbedTLScpp::BigNum("32670510020758816978083085130507043184471273380659243275938904335757337482424"), + mbedTLScpp::BigNum(1) + ); + + mbedTLScpp::BigNum num("99894079990873359630376909829698352524809824171660155898784064965080081534450"); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum("36036075634199074546129661232420548016519448468337978806663212807452641890103"), + mbedTLScpp::BigNum("113492477844519581031850584107650655330363714502423581674624945975139107167263"), + mbedTLScpp::BigNum("91707237065990426039921931734295629131137542276573823064360208304576683970847") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianMultiply( + std::get<0>(a), + std::get<1>(a), + std::get<2>(a), + num, + sk_a, + sk_n, + sk_p + ); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } +} + + +GTEST_TEST(TestEthTxnEcdsa, JacobianFastMultiply) +{ + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > a = std::make_tuple( + mbedTLScpp::BigNum("55066263022277343669578718895168534326250603453777594175500187360389116729240"), + mbedTLScpp::BigNum("32670510020758816978083085130507043184471273380659243275938904335757337482424") + ); + + mbedTLScpp::BigNum num("99894079990873359630376909829698352524809824171660155898784064965080081534450"); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum("55398850951096076018653516985588965820355649108029904532973662457952545528338"), + mbedTLScpp::BigNum("1219865831688556121255156054523597135592503170307932332484054549583411582505") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianFastMultiply( + std::get<0>(a), + std::get<1>(a), + num, + sk_a, + sk_n, + sk_p + ); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + } +} + + +GTEST_TEST(TestEthTxnEcdsa, DeterministicGenerateK) +{ + { + std::vector hash = { + 0x7b, 0xf0, 0x42, 0x15, 0xd5, 0xbc, 0x74, 0x80, 0x96, 0x40, 0xa0, 0xec, 0xd1, 0x11, 0xc5, 0x5e, + 0x7d, 0x07, 0xed, 0x14, 0xe4, 0xdb, 0x4c, 0x6e, 0x82, 0x03, 0x6d, 0x2c, 0xe8, 0x90, 0x54, 0x2e, + }; + mbedTLScpp::BigNum key("34390819888240390953029010971248455142986221947941601700148038000926730363672"); + auto keyBytes = key.Bytes(); + + mbedTLScpp::BigNum expOut("99894079990873359630376909829698352524809824171660155898784064965080081534450"); + + mbedTLScpp::BigNum out = Transaction::DeterministicGenerateK( + mbedTLScpp::CtnFullR(hash), + mbedTLScpp::CtnFullR(keyBytes) + ); + + EXPECT_EQ(out, expOut); + } +} + + +GTEST_TEST(TestEthTxnEcdsa, EcdsaRawSign) +{ + { + std::vector hash = { + 0x7b, 0xf0, 0x42, 0x15, 0xd5, 0xbc, 0x74, 0x80, 0x96, 0x40, 0xa0, 0xec, 0xd1, 0x11, 0xc5, 0x5e, + 0x7d, 0x07, 0xed, 0x14, 0xe4, 0xdb, 0x4c, 0x6e, 0x82, 0x03, 0x6d, 0x2c, 0xe8, 0x90, 0x54, 0x2e, + }; + mbedTLScpp::BigNum key("34390819888240390953029010971248455142986221947941601700148038000926730363672"); + + std::tuple< + uint8_t, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + 1, + mbedTLScpp::BigNum("55398850951096076018653516985588965820355649108029904532973662457952545528338"), + mbedTLScpp::BigNum("42247689232515303603446946966523974649153981729397734462495890121129406260483") + ); + + std::tuple< + uint8_t, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::EcdsaRawSign( + mbedTLScpp::CtnFullR(hash), + key, + sk_a, + sk_gx, + sk_gy, + sk_n, + sk_p + ); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } +} + From a6a3050cedd31a50f3d44e55059030d56b5791a4 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Wed, 29 May 2024 02:34:35 -0700 Subject: [PATCH 18/22] ecdsa calc allows borrowed big num obj --- .../EclipseMonitor/Eth/Transaction/Ecdsa.hpp | 275 +++++++++++++----- test/src/TestEthTxnEcdsa.cpp | 66 ++--- 2 files changed, 227 insertions(+), 114 deletions(-) diff --git a/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp b/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp index 3e05f37..f77dc49 100644 --- a/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp +++ b/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp @@ -68,9 +68,13 @@ inline const Internal::Tls::BigNum& GetBigNum8() * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py * */ +template< + typename _ABigNumT, + typename _NBigNumT +> inline Internal::Tls::BigNum JacobianInv( - const Internal::Tls::BigNum& a, - const Internal::Tls::BigNum& n + _ABigNumT&& a, + _NBigNumT&& n ) { const auto& sk_0 = Internal::Tls::BigNum::Zero(); @@ -89,7 +93,7 @@ inline Internal::Tls::BigNum JacobianInv( // low, high = a % n, n auto low = Internal::Tls::Mod(a, n); - auto high = n; + auto high = Internal::Tls::BigNum(n); // while low > 1: while (low > 1) @@ -125,6 +129,13 @@ inline Internal::Tls::BigNum JacobianInv( * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py * */ +template< + typename _XBigNumT, + typename _YBigNumT, + typename _ZBigNumT, + typename _CurveABigNumT, + typename _CurvePBigNumT +> inline std::tuple< Internal::Tls::BigNum, @@ -132,11 +143,11 @@ std::tuple< Internal::Tls::BigNum > JacobianDouble( - const Internal::Tls::BigNum& x, - const Internal::Tls::BigNum& y, - const Internal::Tls::BigNum& z, - const Internal::Tls::BigNum& curveA, - const Internal::Tls::BigNum& curveP + _XBigNumT&& x, + _YBigNumT&& y, + _ZBigNumT&& z, + _CurveABigNumT&& curveA, + _CurvePBigNumT&& curveP ) { const auto& sk_0 = Internal::Tls::BigNum::Zero(); @@ -193,6 +204,10 @@ JacobianDouble( } +template< + typename _CurveABigNumT, + typename _CurvePBigNumT +> inline std::tuple< Internal::Tls::BigNum, @@ -205,16 +220,16 @@ JacobianDouble( Internal::Tls::BigNum, Internal::Tls::BigNum >& p, - const Internal::Tls::BigNum& curveA, - const Internal::Tls::BigNum& curveP + _CurveABigNumT&& curveA, + _CurvePBigNumT&& curveP ) { return JacobianDouble( std::get<0>(p), std::get<1>(p), std::get<2>(p), - curveA, - curveP + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurvePBigNumT>(curveP) ); } @@ -224,6 +239,16 @@ JacobianDouble( * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py * */ +template< + typename _PxBigNumT, + typename _PyBigNumT, + typename _PzBigNumT, + typename _QxBigNumT, + typename _QyBigNumT, + typename _QzBigNumT, + typename _CurveABigNumT, + typename _CurvePBigNumT +> inline std::tuple< Internal::Tls::BigNum, @@ -231,14 +256,14 @@ std::tuple< Internal::Tls::BigNum > JacobianAdd( - const Internal::Tls::BigNum& pX, - const Internal::Tls::BigNum& pY, - const Internal::Tls::BigNum& pZ, - const Internal::Tls::BigNum& qX, - const Internal::Tls::BigNum& qY, - const Internal::Tls::BigNum& qZ, - const Internal::Tls::BigNum& curveA, - const Internal::Tls::BigNum& curveP + _PxBigNumT&& pX, + _PyBigNumT&& pY, + _PzBigNumT&& pZ, + _QxBigNumT&& qX, + _QyBigNumT&& qY, + _QzBigNumT&& qZ, + _CurveABigNumT&& curveA, + _CurvePBigNumT&& curveP ) { const auto& sk_0 = Internal::Tls::BigNum::Zero(); @@ -248,14 +273,22 @@ JacobianAdd( if (pY == 0) { // return q - return std::make_tuple(qX, qY, qZ); + return std::make_tuple( + Internal::Tls::BigNum(qX), + Internal::Tls::BigNum(qY), + Internal::Tls::BigNum(qZ) + ); } // if not q[1]: if (qY == 0) { // return p - return std::make_tuple(pX, pY, pZ); + return std::make_tuple( + Internal::Tls::BigNum(pX), + Internal::Tls::BigNum(pY), + Internal::Tls::BigNum(pZ) + ); } // U1 = (p[0] * q[2] ** 2) % P @@ -292,7 +325,13 @@ JacobianAdd( else { // return jacobian_double(p) - return JacobianDouble(pX, pY, pZ, curveA, curveP); + return JacobianDouble( + std::forward<_PxBigNumT>(pX), + std::forward<_PyBigNumT>(pY), + std::forward<_PzBigNumT>(pZ), + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurvePBigNumT>(curveP) + ); } } @@ -336,6 +375,10 @@ JacobianAdd( } +template< + typename _CurveABigNumT, + typename _CurvePBigNumT +> inline std::tuple< Internal::Tls::BigNum, @@ -353,8 +396,8 @@ JacobianAdd( Internal::Tls::BigNum, Internal::Tls::BigNum >& q, - const Internal::Tls::BigNum& curveA, - const Internal::Tls::BigNum& curveP + _CurveABigNumT&& curveA, + _CurvePBigNumT&& curveP ) { return JacobianAdd( @@ -364,8 +407,8 @@ JacobianAdd( std::get<0>(q), std::get<1>(q), std::get<2>(q), - curveA, - curveP + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurvePBigNumT>(curveP) ); } @@ -375,6 +418,15 @@ JacobianAdd( * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py * */ +template< + typename _AxBigNumT, + typename _AyBigNumT, + typename _AzBigNumT, + typename _NumBigNumT, + typename _CurveABigNumT, + typename _CurveNBigNumT, + typename _CurvePBigNumT +> inline std::tuple< Internal::Tls::BigNum, @@ -382,13 +434,13 @@ std::tuple< Internal::Tls::BigNum > JacobianMultiply( - const Internal::Tls::BigNum& aX, - const Internal::Tls::BigNum& aY, - const Internal::Tls::BigNum& aZ, - const Internal::Tls::BigNum& num, - const Internal::Tls::BigNum& curveA, - const Internal::Tls::BigNum& curveN, - const Internal::Tls::BigNum& curveP + _AxBigNumT&& aX, + _AyBigNumT&& aY, + _AzBigNumT&& aZ, + _NumBigNumT&& num, + _CurveABigNumT&& curveA, + _CurveNBigNumT&& curveN, + _CurvePBigNumT&& curveP ) { const auto& sk_0 = Internal::Tls::BigNum::Zero(); @@ -405,7 +457,11 @@ JacobianMultiply( if (num == 1) { // return a - return std::make_tuple(aX, aY, aZ); + return std::make_tuple( + Internal::Tls::BigNum(aX), + Internal::Tls::BigNum(aY), + Internal::Tls::BigNum(aZ) + ); } // if n < 0 or n >= N: @@ -413,9 +469,13 @@ JacobianMultiply( { // return jacobian_multiply(a, n % N) return JacobianMultiply( - aX, aY, aZ, + std::forward<_AxBigNumT>(aX), + std::forward<_AyBigNumT>(aY), + std::forward<_AzBigNumT>(aZ), Internal::Tls::Mod(num, curveN), - curveA, curveN, curveP + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurveNBigNumT>(curveN), + std::forward<_CurvePBigNumT>(curveP) ); } @@ -424,27 +484,49 @@ JacobianMultiply( if (numMod2 == 0) { // return jacobian_double(jacobian_multiply(a, n // 2)) - auto tmp = JacobianMultiply(aX, aY, aZ, (num / 2), curveA, curveN, curveP); - return JacobianDouble(std::get<0>(tmp), std::get<1>(tmp), std::get<2>(tmp), curveA, curveP); + auto tmp = JacobianMultiply( + std::forward<_AxBigNumT>(aX), + std::forward<_AyBigNumT>(aY), + std::forward<_AzBigNumT>(aZ), + (num / 2), + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurveNBigNumT>(curveN), + std::forward<_CurvePBigNumT>(curveP) + ); + return JacobianDouble( + std::get<0>(tmp), + std::get<1>(tmp), + std::get<2>(tmp), + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurvePBigNumT>(curveP) + ); } // elif (n % 2) == 1: else if (numMod2 == 1) { // return jacobian_add(jacobian_double(jacobian_multiply(a, n // 2)), a) auto tmp = JacobianDouble( - JacobianMultiply(aX, aY, aZ, (num / 2), curveA, curveN, curveP), - curveA, - curveP + JacobianMultiply( + std::forward<_AxBigNumT>(aX), + std::forward<_AyBigNumT>(aY), + std::forward<_AzBigNumT>(aZ), + (num / 2), + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurveNBigNumT>(curveN), + std::forward<_CurvePBigNumT>(curveP) + ), + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurvePBigNumT>(curveP) ); return JacobianAdd( std::get<0>(tmp), std::get<1>(tmp), std::get<2>(tmp), - aX, - aY, - aZ, - curveA, - curveP + std::forward<_AxBigNumT>(aX), + std::forward<_AyBigNumT>(aY), + std::forward<_AzBigNumT>(aZ), + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurvePBigNumT>(curveP) ); } // else: @@ -460,20 +542,29 @@ JacobianMultiply( * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py * */ +template< + typename _PxBigNumT, + typename _PyBigNumT, + typename _PzBigNumT, + typename _CurvePBigNumT +> inline std::tuple< Internal::Tls::BigNum, Internal::Tls::BigNum > FromJacobian( - const Internal::Tls::BigNum& pX, - const Internal::Tls::BigNum& pY, - const Internal::Tls::BigNum& pZ, - const Internal::Tls::BigNum& curveP + _PxBigNumT&& pX, + _PyBigNumT&& pY, + _PzBigNumT&& pZ, + _CurvePBigNumT&& curveP ) { // z = inv(p[2], P) - auto z = JacobianInv(pZ, curveP); + auto z = JacobianInv( + std::forward<_PzBigNumT>(pZ), + std::forward<_CurvePBigNumT>(curveP) + ); // return ((p[0] * z**2) % P, (p[1] * z**3) % P) // (p[0] * z**2) % P @@ -489,6 +580,7 @@ FromJacobian( } +template inline std::tuple< Internal::Tls::BigNum, @@ -500,14 +592,14 @@ FromJacobian( Internal::Tls::BigNum, Internal::Tls::BigNum >& p, - const Internal::Tls::BigNum& curveP + _CurvePBigNumT&& curveP ) { return FromJacobian( std::get<0>(p), std::get<1>(p), std::get<2>(p), - curveP + std::forward<_CurvePBigNumT>(curveP) ); } @@ -517,18 +609,26 @@ FromJacobian( * https://github.com/ethereum/eth-keys/blob/main/eth_keys/backends/native/jacobian.py * */ +template< + typename _AxBigNumT, + typename _AyBigNumT, + typename _NumBigNumT, + typename _CurveABigNumT, + typename _CurveNBigNumT, + typename _CurvePBigNumT +> inline std::tuple< Internal::Tls::BigNum, Internal::Tls::BigNum > JacobianFastMultiply( - const Internal::Tls::BigNum& aX, - const Internal::Tls::BigNum& aY, - const Internal::Tls::BigNum& num, - const Internal::Tls::BigNum& curveA, - const Internal::Tls::BigNum& curveN, - const Internal::Tls::BigNum& curveP + _AxBigNumT&& aX, + _AyBigNumT&& aY, + _NumBigNumT&& num, + _CurveABigNumT&& curveA, + _CurveNBigNumT&& curveN, + _CurvePBigNumT&& curveP ) { const auto& sk_1 = GetBigNum1(); @@ -540,8 +640,16 @@ JacobianFastMultiply( > ret; return FromJacobian( - JacobianMultiply(aX, aY, sk_1, num, curveA, curveN, curveP), - curveP + JacobianMultiply( + std::forward<_AxBigNumT>(aX), + std::forward<_AyBigNumT>(aY), + sk_1, + std::forward<_NumBigNumT>(num), + std::forward<_CurveABigNumT>(curveA), + std::forward<_CurveNBigNumT>(curveN), + std::forward<_CurvePBigNumT>(curveP) + ), + std::forward<_CurvePBigNumT>(curveP) ); } @@ -646,7 +754,13 @@ inline Internal::Tls::BigNum DeterministicGenerateK( */ template< typename _HashCtnT, bool _HashCtnSec, - typename _PKeyCtnT, bool _PKeyCtnSec + typename _PKeyCtnT, bool _PKeyCtnSec, + typename _PkeyBigNumTraits, + typename _CurveABigNumTraits, + typename _CurveGxBigNumTraits, + typename _CurveGyBigNumTraits, + typename _CurveNBigNumTraits, + typename _CurvePBigNumTraits > inline std::tuple< @@ -657,12 +771,12 @@ std::tuple< EcdsaRawSign( const Internal::Tls::ContCtnReadOnlyRef<_HashCtnT, _HashCtnSec>& hashCtnRef, const Internal::Tls::ContCtnReadOnlyRef<_PKeyCtnT, _PKeyCtnSec>& pKeyCtnRef, - const Internal::Tls::BigNum& privKeyNum, - const Internal::Tls::BigNum& curveA, - const Internal::Tls::BigNum& curveGx, - const Internal::Tls::BigNum& curveGy, - const Internal::Tls::BigNum& curveN, - const Internal::Tls::BigNum& curveP + const Internal::Tls::BigNumber<_PkeyBigNumTraits>& privKeyNum, + const Internal::Tls::BigNumber<_CurveABigNumTraits>& curveA, + const Internal::Tls::BigNumber<_CurveGxBigNumTraits>& curveGx, + const Internal::Tls::BigNumber<_CurveGyBigNumTraits>& curveGy, + const Internal::Tls::BigNumber<_CurveNBigNumTraits>& curveN, + const Internal::Tls::BigNumber<_CurvePBigNumTraits>& curveP ) { // z = big_endian_to_int(msg_hash) @@ -702,7 +816,13 @@ EcdsaRawSign( template< - typename _HashCtnT, bool _HashCtnSec + typename _HashCtnT, bool _HashCtnSec, + typename _PkeyBigNumTraits, + typename _CurveABigNumTraits, + typename _CurveGxBigNumTraits, + typename _CurveGyBigNumTraits, + typename _CurveNBigNumTraits, + typename _CurvePBigNumTraits > inline std::tuple< @@ -712,15 +832,16 @@ std::tuple< > EcdsaRawSign( const Internal::Tls::ContCtnReadOnlyRef<_HashCtnT, _HashCtnSec>& hashCtnRef, - const Internal::Tls::BigNum& privKeyNum, - const Internal::Tls::BigNum& curveA, - const Internal::Tls::BigNum& curveGx, - const Internal::Tls::BigNum& curveGy, - const Internal::Tls::BigNum& curveN, - const Internal::Tls::BigNum& curveP + const Internal::Tls::BigNumber<_PkeyBigNumTraits>& privKeyNum, + const Internal::Tls::BigNumber<_CurveABigNumTraits>& curveA, + const Internal::Tls::BigNumber<_CurveGxBigNumTraits>& curveGx, + const Internal::Tls::BigNumber<_CurveGyBigNumTraits>& curveGy, + const Internal::Tls::BigNumber<_CurveNBigNumTraits>& curveN, + const Internal::Tls::BigNumber<_CurvePBigNumTraits>& curveP ) { - auto privKeyBytes = privKeyNum.SecretBytes(); + auto privKeyBytes = + privKeyNum.template SecretBytes(); return EcdsaRawSign( hashCtnRef, diff --git a/test/src/TestEthTxnEcdsa.cpp b/test/src/TestEthTxnEcdsa.cpp index dd128b1..2a0fe79 100644 --- a/test/src/TestEthTxnEcdsa.cpp +++ b/test/src/TestEthTxnEcdsa.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "Common.hpp" @@ -28,25 +29,7 @@ GTEST_TEST(TestEthTxnEcdsa, CountTestFile) } -/** - * @brief These constants are taken from - * https://github.com/ethereum/eth-keys/blob/main/eth_keys/constants.py - * - */ -static const mbedTLScpp::BigNum sk_a = mbedTLScpp::BigNum(0); -static const mbedTLScpp::BigNum sk_gx = mbedTLScpp::BigNum( - "55066263022277343669578718895168534326250603453777594175500187360389116729240" -); -static const mbedTLScpp::BigNum sk_gy = mbedTLScpp::BigNum( - "32670510020758816978083085130507043184471273380659243275938904335757337482424" -); -static const mbedTLScpp::BigNum sk_n = mbedTLScpp::BigNum( - "115792089237316195423570985008687907852837564279074904382605163141518161494337" -); -static const mbedTLScpp::BigNum sk_p = mbedTLScpp::BigNum( - // 2**256 - 2**32 - 977 - "115792089237316195423570985008687907853269984665640564039457584007908834671663" -); +static const mbedTLScpp::EcGroup<> sk_secp256k1(mbedTLScpp::EcType::SECP256K1); GTEST_TEST(TestEthTxnEcdsa, JacobianInv) @@ -102,7 +85,11 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianDouble) mbedTLScpp::BigNum, mbedTLScpp::BigNum, mbedTLScpp::BigNum - > out = Transaction::JacobianDouble(p, sk_a, sk_p); + > out = Transaction::JacobianDouble( + p, + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowP() + ); EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); @@ -134,11 +121,11 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianDouble) mbedTLScpp::BigNum, mbedTLScpp::BigNum, mbedTLScpp::BigNum - > out = Transaction::JacobianDouble(p, sk_a, sk_p); - - std::cout << std::get<0>(out).Dec() << std::endl; - std::cout << std::get<1>(out).Dec() << std::endl; - std::cout << std::get<2>(out).Dec() << std::endl; + > out = Transaction::JacobianDouble( + p, + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowP() + ); EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); @@ -183,7 +170,12 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianAdd) mbedTLScpp::BigNum, mbedTLScpp::BigNum, mbedTLScpp::BigNum - > out = Transaction::JacobianAdd(p, q, sk_a, sk_p); + > out = Transaction::JacobianAdd( + p, + q, + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowP() + ); EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); @@ -226,9 +218,9 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianMultiply) std::get<1>(a), std::get<2>(a), num, - sk_a, - sk_n, - sk_p + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowN(), + sk_secp256k1.BorrowP() ); EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); @@ -266,9 +258,9 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianFastMultiply) std::get<0>(a), std::get<1>(a), num, - sk_a, - sk_n, - sk_p + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowN(), + sk_secp256k1.BorrowP() ); EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); @@ -325,11 +317,11 @@ GTEST_TEST(TestEthTxnEcdsa, EcdsaRawSign) > out = Transaction::EcdsaRawSign( mbedTLScpp::CtnFullR(hash), key, - sk_a, - sk_gx, - sk_gy, - sk_n, - sk_p + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowGx(), + sk_secp256k1.BorrowGy(), + sk_secp256k1.BorrowN(), + sk_secp256k1.BorrowP() ); EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); From 10b0bb05fd98ba3168690b1b90816ed66c5cd786 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Wed, 29 May 2024 02:59:07 -0700 Subject: [PATCH 19/22] Added 2nd ecdsa test case that generates v val of 0 --- test/src/TestEthTxnEcdsa.cpp | 234 +++++++++++++++++++++++++++++++++-- 1 file changed, 226 insertions(+), 8 deletions(-) diff --git a/test/src/TestEthTxnEcdsa.cpp b/test/src/TestEthTxnEcdsa.cpp index 2a0fe79..79f7b87 100644 --- a/test/src/TestEthTxnEcdsa.cpp +++ b/test/src/TestEthTxnEcdsa.cpp @@ -55,6 +55,17 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianInv) EXPECT_EQ(out, expOut); } + + { + mbedTLScpp::BigNum a("4755893456204814032994661987161704419906856408563089114860023487200879314920"); + mbedTLScpp::BigNum n("115792089237316195423570985008687907852837564279074904382605163141518161494337"); + + mbedTLScpp::BigNum expOut("8366362605996906969732918924814779820613675455983702332239682356052933443685"); + + mbedTLScpp::BigNum out = Transaction::JacobianInv(a, n); + + EXPECT_EQ(out, expOut); + } } @@ -131,6 +142,42 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianDouble) EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); } + + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > p = std::make_tuple( + mbedTLScpp::BigNum("14861663377811509140551858067315276845464282493407987296353092997784687167314"), + mbedTLScpp::BigNum("39137420299569502647765350355455095302255521025456411443562043337254407045840"), + mbedTLScpp::BigNum("40099571553575564002937172146104371577379850074239136598504885595987528294846") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum("63575194886187257535938157704972707470722922421800391784521628407589249322769"), + mbedTLScpp::BigNum("40161198225766027124270551863076939477305469833559547838035156868151083466459"), + mbedTLScpp::BigNum("87891979409671700349269413343649637404628212098163418479237423976283077679673") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianDouble( + p, + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowP() + ); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } } @@ -181,6 +228,52 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianAdd) EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); } + + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > p = std::make_tuple( + mbedTLScpp::BigNum("81852845867168905301505054245430925093078919361432342426470976391340413601139"), + mbedTLScpp::BigNum("44338445320234977458987797881500440713217305413439258886338627240901591305666"), + mbedTLScpp::BigNum("47594349659260652877357148834108997596577843090446296597453723945805718117563") + ); + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > q = std::make_tuple( + mbedTLScpp::BigNum("55066263022277343669578718895168534326250603453777594175500187360389116729240"), + mbedTLScpp::BigNum("32670510020758816978083085130507043184471273380659243275938904335757337482424"), + mbedTLScpp::BigNum(1) + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum("40448055496287309044869980056118293952322637777137096262082128484131334177985"), + mbedTLScpp::BigNum("103296625191508965494948037293515077505697237578720143244847055492794393537747"), + mbedTLScpp::BigNum("63820715475438429361341171002329409693455152442280053906972735224332669284210") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianAdd( + p, + q, + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowP() + ); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } } @@ -227,6 +320,48 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianMultiply) EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); } + + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > a = std::make_tuple( + mbedTLScpp::BigNum("55066263022277343669578718895168534326250603453777594175500187360389116729240"), + mbedTLScpp::BigNum("32670510020758816978083085130507043184471273380659243275938904335757337482424"), + mbedTLScpp::BigNum(1) + ); + + mbedTLScpp::BigNum num("4755893456204814032994661987161704419906856408563089114860023487200879314920"); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum("63575194886187257535938157704972707470722922421800391784521628407589249322769"), + mbedTLScpp::BigNum("40161198225766027124270551863076939477305469833559547838035156868151083466459"), + mbedTLScpp::BigNum("87891979409671700349269413343649637404628212098163418479237423976283077679673") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianMultiply( + std::get<0>(a), + std::get<1>(a), + std::get<2>(a), + num, + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowN(), + sk_secp256k1.BorrowP() + ); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } } @@ -266,16 +401,52 @@ GTEST_TEST(TestEthTxnEcdsa, JacobianFastMultiply) EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); } + + { + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > a = std::make_tuple( + mbedTLScpp::BigNum("55066263022277343669578718895168534326250603453777594175500187360389116729240"), + mbedTLScpp::BigNum("32670510020758816978083085130507043184471273380659243275938904335757337482424") + ); + + mbedTLScpp::BigNum num("4755893456204814032994661987161704419906856408563089114860023487200879314920"); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + mbedTLScpp::BigNum("57531915739949951657140584335392702729792194311398064976436476590597615371172"), + mbedTLScpp::BigNum("74532461135409732490675357031953695959767481587610106665841628494200677277931") + ); + + std::tuple< + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::JacobianFastMultiply( + std::get<0>(a), + std::get<1>(a), + num, + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowN(), + sk_secp256k1.BorrowP() + ); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + } } GTEST_TEST(TestEthTxnEcdsa, DeterministicGenerateK) { + std::vector hash = { + 0x7b, 0xf0, 0x42, 0x15, 0xd5, 0xbc, 0x74, 0x80, 0x96, 0x40, 0xa0, 0xec, 0xd1, 0x11, 0xc5, 0x5e, + 0x7d, 0x07, 0xed, 0x14, 0xe4, 0xdb, 0x4c, 0x6e, 0x82, 0x03, 0x6d, 0x2c, 0xe8, 0x90, 0x54, 0x2e, + }; + { - std::vector hash = { - 0x7b, 0xf0, 0x42, 0x15, 0xd5, 0xbc, 0x74, 0x80, 0x96, 0x40, 0xa0, 0xec, 0xd1, 0x11, 0xc5, 0x5e, - 0x7d, 0x07, 0xed, 0x14, 0xe4, 0xdb, 0x4c, 0x6e, 0x82, 0x03, 0x6d, 0x2c, 0xe8, 0x90, 0x54, 0x2e, - }; mbedTLScpp::BigNum key("34390819888240390953029010971248455142986221947941601700148038000926730363672"); auto keyBytes = key.Bytes(); @@ -288,16 +459,31 @@ GTEST_TEST(TestEthTxnEcdsa, DeterministicGenerateK) EXPECT_EQ(out, expOut); } + + { + mbedTLScpp::BigNum key("62878277211082814447969588726438965043493409034528277085854515572569382032315"); + auto keyBytes = key.Bytes(); + + mbedTLScpp::BigNum expOut("4755893456204814032994661987161704419906856408563089114860023487200879314920"); + + mbedTLScpp::BigNum out = Transaction::DeterministicGenerateK( + mbedTLScpp::CtnFullR(hash), + mbedTLScpp::CtnFullR(keyBytes) + ); + + EXPECT_EQ(out, expOut); + } } GTEST_TEST(TestEthTxnEcdsa, EcdsaRawSign) { + std::vector hash = { + 0x7b, 0xf0, 0x42, 0x15, 0xd5, 0xbc, 0x74, 0x80, 0x96, 0x40, 0xa0, 0xec, 0xd1, 0x11, 0xc5, 0x5e, + 0x7d, 0x07, 0xed, 0x14, 0xe4, 0xdb, 0x4c, 0x6e, 0x82, 0x03, 0x6d, 0x2c, 0xe8, 0x90, 0x54, 0x2e, + }; + { - std::vector hash = { - 0x7b, 0xf0, 0x42, 0x15, 0xd5, 0xbc, 0x74, 0x80, 0x96, 0x40, 0xa0, 0xec, 0xd1, 0x11, 0xc5, 0x5e, - 0x7d, 0x07, 0xed, 0x14, 0xe4, 0xdb, 0x4c, 0x6e, 0x82, 0x03, 0x6d, 0x2c, 0xe8, 0x90, 0x54, 0x2e, - }; mbedTLScpp::BigNum key("34390819888240390953029010971248455142986221947941601700148038000926730363672"); std::tuple< @@ -328,5 +514,37 @@ GTEST_TEST(TestEthTxnEcdsa, EcdsaRawSign) EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); } + + { + mbedTLScpp::BigNum key("62878277211082814447969588726438965043493409034528277085854515572569382032315"); + + std::tuple< + uint8_t, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > expOut = std::make_tuple( + 0, + mbedTLScpp::BigNum("57531915739949951657140584335392702729792194311398064976436476590597615371172"), + mbedTLScpp::BigNum("52996200849355888973535696918874003680055380398586210701928882732133694725751") + ); + + std::tuple< + uint8_t, + mbedTLScpp::BigNum, + mbedTLScpp::BigNum + > out = Transaction::EcdsaRawSign( + mbedTLScpp::CtnFullR(hash), + key, + sk_secp256k1.BorrowA(), + sk_secp256k1.BorrowGx(), + sk_secp256k1.BorrowGy(), + sk_secp256k1.BorrowN(), + sk_secp256k1.BorrowP() + ); + + EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); + EXPECT_EQ(std::get<1>(out), std::get<1>(expOut)); + EXPECT_EQ(std::get<2>(out), std::get<2>(expOut)); + } } From 1bc769a24ca5ca0b567d317594ae687251c93314 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Wed, 29 May 2024 23:45:25 -0700 Subject: [PATCH 20/22] Added v,r,s fields to DynFeeTxn --- .../Eth/Transaction/DynamicFee.hpp | 74 +++++++++++++++++-- test/src/TestEthTxnContractFunc.cpp | 4 +- test/src/TestEthTxnDynFee.cpp | 6 +- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp b/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp index db5ba58..8e45087 100644 --- a/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp +++ b/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp @@ -60,7 +60,16 @@ using DynFeeTupleCore = std::tuple< Internal::Obj::Bytes>, // 09. std::pair, - AccessListObj> + AccessListObj>, + // 10. + std::pair, + Internal::Obj::Bytes>, + // 11. + std::pair, + Internal::Obj::Bytes>, + // 12. + std::pair, + Internal::Obj::Bytes> >; @@ -91,7 +100,16 @@ using DynFeeParserTupleCore = std::tuple< Internal::Rlp::BytesParser>, // 09. std::pair, - AccessListObjParser> + AccessListObjParser>, + // 10. + std::pair, + Internal::Rlp::BytesParser>, + // 11. + std::pair, + Internal::Rlp::BytesParser>, + // 12. + std::pair, + Internal::Rlp::BytesParser> >; @@ -118,10 +136,13 @@ class DynFee : ValidateContractAddr(get_Destination()); } - Internal::Rlp::OutputContainerType RlpSerialize() const + Internal::Rlp::OutputContainerType RlpSerializeUnsigned() const { Validate(); - return Internal::Rlp::WriterGeneric::Write(*this); + return Internal::Rlp::WriterGeneric::StaticDictWriter::Write( + *this, + /*skipLast=*/3 // skip the last 3 (i.e., v, r, s) fields + ); } /** @@ -136,7 +157,7 @@ class DynFee : std::array Hash() const { - auto rlp = RlpSerialize(); + auto rlp = RlpSerializeUnsigned(); // prepend the type flag rlp.insert(rlp.begin(), GetTypeFlag()); @@ -321,14 +342,53 @@ class DynFee : return Base::template get >(); } + // 10. v + typename Base::template GetRef > + get_v() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_v() const + { + return Base::template get >(); + } + + // 11. r + typename Base::template GetRef > + get_r() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_r() const + { + return Base::template get >(); + } + + // 12. s + typename Base::template GetRef > + get_s() + { + return Base::template get >(); + } + + typename Base::template GetConstRef > + get_s() const + { + return Base::template get >(); + } + }; // class DynFee using DynFeeParser = Internal::Rlp::StaticDictParserT< DynFeeParserTupleCore, - false, - false, + /*_AllowMissingItem =*/true, // It's okay if the v, r, s fields are missing + /*_AllowExtraItem =*/false, DynFee >; diff --git a/test/src/TestEthTxnContractFunc.cpp b/test/src/TestEthTxnContractFunc.cpp index 8228e3b..229250c 100644 --- a/test/src/TestEthTxnContractFunc.cpp +++ b/test/src/TestEthTxnContractFunc.cpp @@ -153,7 +153,7 @@ GTEST_TEST(TestEthTxnContractFunc, ContractFuncStaticDef_CallByTxn) 0xc0U, }; - auto rlp = txn.RlpSerialize(); + auto rlp = txn.RlpSerializeUnsigned(); EXPECT_EQ(rlp, expectedSerialized); std::array expHash = { @@ -236,7 +236,7 @@ GTEST_TEST(TestEthTxnContractFunc, ContractFuncStaticDef_CallByTxn) 0x00U, 0x00U, 0x00U, 0x00U, 0xc0U, }; - auto rlp = txn.RlpSerialize(); + auto rlp = txn.RlpSerializeUnsigned(); EXPECT_EQ(rlp, expectedSerialized); std::array expHash = { diff --git a/test/src/TestEthTxnDynFee.cpp b/test/src/TestEthTxnDynFee.cpp index eb8d1a4..daf2c7d 100644 --- a/test/src/TestEthTxnDynFee.cpp +++ b/test/src/TestEthTxnDynFee.cpp @@ -225,7 +225,7 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) std::cout << "Expected RLP: " << SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; - auto rlp = dynFeeTxn.RlpSerialize(); + auto rlp = dynFeeTxn.RlpSerializeUnsigned(); std::cout << "Generated RLP: " << SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; @@ -315,7 +315,7 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) std::cout << "Expected RLP: " << SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; - auto rlp = dynFeeTxn.RlpSerialize(); + auto rlp = dynFeeTxn.RlpSerializeUnsigned(); std::cout << "Generated RLP: " << SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; @@ -399,7 +399,7 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) std::cout << "Expected RLP: " << SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; - auto rlp = dynFeeTxn.RlpSerialize(); + auto rlp = dynFeeTxn.RlpSerializeUnsigned(); std::cout << "Generated RLP: " << SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; From d4b0c56233c3086c0731df1c389121420255f775 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Thu, 30 May 2024 01:37:18 -0700 Subject: [PATCH 21/22] Added signed transaction serialization. --- .../Eth/Transaction/DynamicFee.hpp | 14 + .../EclipseMonitor/Eth/Transaction/Ecdsa.hpp | 75 +++++ test/GenTransactionSamples.py | 299 ++++++++++++++++++ test/src/TestEthTxnDynFee.cpp | 198 +++++++++++- test/src/TestEthTxnEcdsa.cpp | 12 +- 5 files changed, 574 insertions(+), 24 deletions(-) create mode 100644 test/GenTransactionSamples.py diff --git a/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp b/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp index 8e45087..25a9b93 100644 --- a/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp +++ b/include/EclipseMonitor/Eth/Transaction/DynamicFee.hpp @@ -145,6 +145,20 @@ class DynFee : ); } + Internal::Rlp::OutputContainerType RlpSerializeSigned() const + { + Validate(); + if (get_r().size() == 0 || get_s().size() == 0) + { + throw Exception("The given transaction is not signed"); + } + auto rlp = + Internal::Rlp::WriterGeneric::StaticDictWriter::Write(*this); + rlp.insert(rlp.begin(), GetTypeFlag()); + + return rlp; + } + /** * @brief Get the type flag, which is 0x02 for dynamic fee transaction * diff --git a/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp b/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp index f77dc49..53c800e 100644 --- a/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp +++ b/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -856,6 +857,80 @@ EcdsaRawSign( } +template< + typename _HashCtnT, bool _HashCtnSec, + typename _PkeyBigNumTraits, + typename _EcGroupTraits +> +inline +std::tuple< + uint8_t, + Internal::Tls::BigNum, + Internal::Tls::BigNum +> +EcdsaRawSign( + const Internal::Tls::ContCtnReadOnlyRef<_HashCtnT, _HashCtnSec>& hashCtnRef, + const Internal::Tls::BigNumber<_PkeyBigNumTraits>& privKeyNum, + const Internal::Tls::EcGroup<_EcGroupTraits>& curveGroup +) +{ + auto privKeyBytes = + privKeyNum.template SecretBytes(); + + return EcdsaRawSign( + hashCtnRef, + Internal::Tls::CtnFullR(privKeyBytes), + privKeyNum, + curveGroup.BorrowA(), + curveGroup.BorrowGx(), + curveGroup.BorrowGy(), + curveGroup.BorrowN(), + curveGroup.BorrowP() + ); +} + + +template< + typename _PkeyTraits +> +inline void SignTransaction( + DynFee& txn, + const Internal::Tls::EcKeyPair< + Internal::Tls::EcType::SECP256K1, + _PkeyTraits + >& keyPair +) +{ + auto hash = txn.Hash(); + + auto signBigNum = EcdsaRawSign( + Internal::Tls::CtnFullR(hash), + keyPair.BorrowSecretNum(), + keyPair.BorrowGroup() + ); + + uint8_t v = std::get<0>(signBigNum); + if (v == 0) + { + txn.get_v().resize(0); + } + else + { + txn.get_v().resize(1); + txn.get_v()[0] = v; + } + + + txn.get_r() = Internal::Obj::Bytes( + std::get<1>(signBigNum).template Bytes() + ); + + txn.get_s() = Internal::Obj::Bytes( + std::get<2>(signBigNum).template Bytes() + ); +} + + } // namespace Transaction } // namespace Eth } // namespace EclipseMonitor diff --git a/test/GenTransactionSamples.py b/test/GenTransactionSamples.py new file mode 100644 index 0000000..77088f9 --- /dev/null +++ b/test/GenTransactionSamples.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- +### +# Copyright (c) 2024 Haofan Zheng +# Use of this source code is governed by an MIT-style +# license that can be found in the LICENSE file or at +# https://opensource.org/licenses/MIT. +### + + +# This python code helps to generate the input/output and raw transaction data +# for unit tests in the EclipseMonitor::Eth::Transaction module and +# EclipseMonitor::Eth ABI writer classes. +# Many of the sample input data are taken from +# https://github.com/ethereum/eth-account/blob/8478a86a8d235acba0a33fcae5804887473c72de/eth_account/account.py#L655 + + +import web3 + +from eth_utils import ( + keccak, +) + + +TEST_ABI_1 = [ + # ('uint64','(uint64,uint64)','bytes5') + { + "name": "foo", + "type": "function", + "inputs": [ + { "name": "val1", "type": "uint64" }, + { + "name": "val2", "type": "tuple", + "components": [ + { "name": "val21", "type": "uint64" }, + { "name": "val22", "type": "uint64", } + ] + }, + { "name": "val3", "type": "bytes5" } + ], + "outputs": [ + { "name": "ret1", "type": "uint64" }, + { "name": "ret2", "type": "bytes5" } + ] + }, + # ('uint64','(uint64,(uint64,bytes))','bytes5') + { + "name": "bar", + "type": "function", + "inputs": [ + { "name": "val1", "type": "uint64" }, + { + "name": "val2", "type": "tuple", + "components": [ + { "name": "val21", "type": "uint64" }, + { + "name": "val22", + "type": "tuple", + "components": [ + { "name": "val221", "type": "uint64" }, + { "name": "val222", "type": "bytes" } + ] + } + ] + }, + { "name": "val3", "type": "bytes5" } + ], + "outputs": [ + { "name": "ret1", "type": "uint64" }, + { "name": "ret2", "type": "bytes" } + ] + } +] +TEST_CONTRACT_ADDR_1 = '0x09616C3d61b3331fc4109a9E41a8BDB7d9776609' +TEST_PRIVATE_KEY_1 = '0x''4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318' +TEST_PRIVATE_KEY_2 = '0x''8b03d7fdc28059800b252d475164a581d94563c9a39f435dc9b130acb5ab93bb' + + +# import sys +# print() +# for byte in serialized: +# sys.stdout.write('{}U, '.format(hex(byte))) +# print() + + +w3 = web3.Web3() + + +# transaction1 = { +# "type": 2, # optional - can be implicitly determined based on max fee params +# "gas": 100000, +# "maxFeePerGas": 2000000000, +# "maxPriorityFeePerGas": 2000000000, +# "data": "0x616263646566", +# "nonce": 34, +# "to": "0x09616C3d61b3331fc4109a9E41a8BDB7d9776609", +# "value": "0x5af3107a4000", +# "accessList": ( # optional +# { +# "address": "0x0000000000000000000000000000000000000001", +# "storageKeys": ( +# "0x0100000000000000000000000000000000000000000000000000000000000000", +# ) +# }, +# ), +# "chainId": 1900, +# } +# transaction1Serialized = 'f86f82076c2284773594008477359400830186a09409616c3d61b3331fc4109a9e41a8bdb7d9776609865af3107a400086616263646566f838f7940000000000000000000000000000000000000001e1a00100000000000000000000000000000000000000000000000000000000000000' +# transaction1Hash = 'd385a3379c2fbd2ccdda2cb84fa1202cfd0635ba0e422a1921ccf8361b24465c' + +# assert keccak(b'\x02' + bytes.fromhex(transaction1Serialized)) == bytes.fromhex(transaction1Hash) + +# signedTransaction1 = w3.eth.account.sign_transaction(transaction1, TEST_PRIVATE_KEY_1) + + + + + +# transaction1 = { +# "type": 2, # optional - can be implicitly determined based on max fee params +# "gas": 123456, +# "maxFeePerGas": 987654321, +# "maxPriorityFeePerGas": 98765432154321, +# "data": "0x9879ab123d274ef5", +# "nonce": 814370, +# "to": "0x09616C3d61b3331fc4109a9E41a8BDB7d9776609", +# "value": 28957, +# "accessList": ( # optional +# { +# "address": "0x0000000000000000000000000000000000000001", +# "storageKeys": tuple() +# }, +# ), +# "chainId": 634, +# } +# transaction1Serialized = 'f85082027a830c6d228659d39e7fe8d1843ade68b18301e2409409616c3d61b3331fc4109a9e41a8bdb7d977660982711d889879ab123d274ef5d7d6940000000000000000000000000000000000000001c0' +# transaction1Hash = 'ac8785206b5b48c55ec9ccd796282dad8d8669cb91dde26e4ce424ccbdcbd0e0' + +# assert keccak(b'\x02' + bytes.fromhex(transaction1Serialized)) == bytes.fromhex(transaction1Hash) + +# signedTransaction1 = w3.eth.account.sign_transaction(transaction1, TEST_PRIVATE_KEY_1) + + + + + +# transaction1 = { +# "type": 2, # optional - can be implicitly determined based on max fee params +# "gas": 2563498, +# "maxFeePerGas": 14067925928, +# "maxPriorityFeePerGas": 2956132109347, +# "data": "0x5789a7b3fe8d", +# "nonce": 25169, +# "to": "0x09616C3d61b3331fc4109a9E41a8BDB7d9776609", +# "value": 1532891978124, +# "chainId": 3948, +# } +# transaction1Serialized = 'f85082027a830c6d228659d39e7fe8d1843ade68b18301e2409409616c3d61b3331fc4109a9e41a8bdb7d977660982711d889879ab123d274ef5d7d6940000000000000000000000000000000000000001c0' +# transaction1Hash = 'ac8785206b5b48c55ec9ccd796282dad8d8669cb91dde26e4ce424ccbdcbd0e0' + +# assert keccak(b'\x02' + bytes.fromhex(transaction1Serialized)) == bytes.fromhex(transaction1Hash) + +# signedTransaction1 = w3.eth.account.sign_transaction(transaction1, TEST_PRIVATE_KEY_1) + + + + + +def FormatBytes(data: bytes) -> str: + s = '' + i = 0 + for b in data: + s += '0x{:02x}U, '.format(b) + i += 1 + if i % 32 == 0: + s += '\n' + + return s + + + + + +paramTypes = ('bytes[2]',) +paramArgs = ([b'\x01\x02\x03\x04\x05', b'\x09\x08\x07\x06\x05\x04\x03\x02\x01'],) +encoded = w3.codec.encode(paramTypes, paramArgs) +print(paramTypes) +print(FormatBytes(encoded)) +print() + + + + +paramTypes = ('uint64[]',) +paramArgs = ([0x1234567890ABCDEF, 0xEFCDAB8967452301],) +encoded = w3.codec.encode(paramTypes, paramArgs) +print(paramTypes) +print(FormatBytes(encoded)) +print() + + + + +paramTypes = ('bytes[]',) +paramArgs = ([b'\x01\x02\x03\x04\x05', b'\x09\x08\x07\x06\x05\x04\x03\x02\x01'],) +encoded = w3.codec.encode(paramTypes, paramArgs) +print(paramTypes) +print(FormatBytes(encoded)) +print() + + + + +paramTypes = ('bytes', 'bytes',) +paramArgs = (b'\x01\x02\x03\x04\x05', b'\x09\x08\x07\x06\x05\x04\x03\x02\x01',) +encoded = w3.codec.encode(paramTypes, paramArgs) +print(paramTypes) +print(FormatBytes(encoded)) +print() + + + + +paramTypes = ('uint64', 'uint64[]', 'bytes5') +paramArgs = (12345, [54321, 67890], b'\x01\x02\x03\x04\x05') +encoded = w3.codec.encode(paramTypes, paramArgs) +print(paramTypes) +print(FormatBytes(encoded)) +print() + + + + +paramTypes = ('uint64', '(uint64,uint64)', 'bytes5') +paramArgs = (12345, (54321, 67890), b'\x01\x02\x03\x04\x05') +encoded = w3.codec.encode(paramTypes, paramArgs) +print(paramTypes) +print(FormatBytes(encoded)) +print() + + + +paramTypes = ('uint64', '(uint64,bytes)', 'bytes5') +paramArgs = (12345, (54321, b'\x01\x02\x03\x04\x05'), b'\x01\x02\x03\x04\x05') +encoded = w3.codec.encode(paramTypes, paramArgs) +print(paramTypes) +print(FormatBytes(encoded)) +print() + + + +paramTypes = ('uint64', '(uint64,(uint64,bytes))', 'bytes5') +paramArgs = (12345, (54321, (67890, b'\x01\x02\x03\x04\x05')), b'\x01\x02\x03\x04\x05') +encoded = w3.codec.encode(paramTypes, paramArgs) +print(paramTypes) +print(FormatBytes(encoded)) +print() + + + +testContract1 = w3.eth.contract(address=TEST_CONTRACT_ADDR_1, abi=TEST_ABI_1) +paramArgs = [12345, (54321, 67890), b'\x01\x02\x03\x04\x05'] +executable = testContract1.functions['foo'](*paramArgs) +msg = { + "type": 2, + "gas": 100000, + "maxFeePerGas": 2000000000, + "maxPriorityFeePerGas": 2000000000, + "nonce": 34, + "value": 0, + "chainId": 1900, +} +tx = executable.build_transaction(msg) +signedTx = w3.eth.account.sign_transaction(tx, TEST_PRIVATE_KEY_1) +print('rawTransaction:') +print(FormatBytes(signedTx.rawTransaction)) +print() +# w3.eth.send_raw_transaction(signedTx) + + + +testContract1 = w3.eth.contract(address=TEST_CONTRACT_ADDR_1, abi=TEST_ABI_1) +paramArgs = [12345, (54321, (67890, b'\x01\x02\x03\x04\x05')), b'\x01\x02\x03\x04\x05'] +executable = testContract1.functions['bar'](*paramArgs) +msg = { + "type": 2, + "gas": 100000, + "maxFeePerGas": 2000000000, + "maxPriorityFeePerGas": 2000000000, + "nonce": 34, + "value": 1000, + "chainId": 1900, +} +tx = executable.build_transaction(msg) +signedTx = w3.eth.account.sign_transaction(tx, TEST_PRIVATE_KEY_2) +print('rawTransaction:') +print(FormatBytes(signedTx.rawTransaction)) +print() + diff --git a/test/src/TestEthTxnDynFee.cpp b/test/src/TestEthTxnDynFee.cpp index daf2c7d..fc7af40 100644 --- a/test/src/TestEthTxnDynFee.cpp +++ b/test/src/TestEthTxnDynFee.cpp @@ -6,9 +6,16 @@ #include +#include +#include +#include #include +#include #include +#include +#include + #include #include "Common.hpp" @@ -222,12 +229,12 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, }; - std::cout << "Expected RLP: " << - SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; + // std::cout << "Expected RLP: " << + // SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; auto rlp = dynFeeTxn.RlpSerializeUnsigned(); - std::cout << "Generated RLP: " << - SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; + // std::cout << "Generated RLP: " << + // SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; EXPECT_EQ(rlp, expRlp); @@ -312,13 +319,8 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0xc0U, }; - std::cout << "Expected RLP: " << - SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; auto rlp = dynFeeTxn.RlpSerializeUnsigned(); - std::cout << "Generated RLP: " << - SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; - EXPECT_EQ(rlp, expRlp); auto rlpDecoded = Transaction::DynFee::FromRlp(rlp); @@ -396,13 +398,8 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) 0x64U, 0xe7U, 0x7bU, 0x59U, 0x8cU, 0x86U, 0x57U, 0x89U, 0xa7U, 0xb3U, 0xfeU, 0x8dU, 0xc0U, }; - std::cout << "Expected RLP: " << - SimpleObjects::Codec::Hex::Encode(expRlp) << std::endl; auto rlp = dynFeeTxn.RlpSerializeUnsigned(); - std::cout << "Generated RLP: " << - SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; - EXPECT_EQ(rlp, expRlp); auto rlpDecoded = Transaction::DynFee::FromRlp(rlp); @@ -430,3 +427,176 @@ GTEST_TEST(TestEthTxnDynFee, DeEncoding) } } + +GTEST_TEST(TestEthTxnDynFee, SignedTransaction) +{ + static const ContractAddr gsk_testContractAddr = { + // 0x09616C3d61b3331fc4109a9E41a8BDB7d9776609 + 0x09U, 0x61U, 0x6CU, 0x3DU, 0x61U, 0xB3U, 0x33U, 0x1fU, 0xc4U, 0x10U, + 0x9aU, 0x9EU, 0x41U, 0xa8U, 0xBDU, 0xB7U, 0xd9U, 0x77U, 0x66U, 0x09U, + }; + + std::unique_ptr rand = + mbedTLScpp::Internal::make_unique(); + + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + >, + AbiWriter > + >; + + auto keyPair = mbedTLScpp::EcKeyPair:: + FromSecretNum( + mbedTLScpp::BigNum( + "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318", + /*radix=*/16 + ), + *rand + ); + + Transaction::ContractFuncStaticDef func( + gsk_testContractAddr, + "foo" + ); + + auto txn = func.CallByTxn( + SimpleObjects::UInt64(12345), + std::forward_as_tuple( + SimpleObjects::UInt64(54321), + SimpleObjects::UInt64(67890) + ), + SimpleObjects::Bytes({ 0x01U, 0x02U, 0x03U, 0x04U, 0x05U }) + ); + + txn.SetChainID(1900); + txn.SetNonce(34); + txn.SetMaxPriorFeePerGas(2000000000); + txn.SetMaxFeePerGas(2000000000); + txn.SetGasLimit(100000); + txn.SetAmount(0); + + Transaction::SignTransaction(txn, keyPair); + + ASSERT_EQ(txn.get_v().size(), 1); + EXPECT_EQ(txn.get_v()[0], 0x01U); + + std::vector expectedSerialized = { + 0x02U, 0xf8U, 0xf2U, 0x82U, 0x07U, 0x6cU, 0x22U, 0x84U, 0x77U, 0x35U, 0x94U, 0x00U, 0x84U, 0x77U, 0x35U, 0x94U, + 0x00U, 0x83U, 0x01U, 0x86U, 0xa0U, 0x94U, 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, 0x33U, 0x1fU, 0xc4U, 0x10U, + 0x9aU, 0x9eU, 0x41U, 0xa8U, 0xbdU, 0xb7U, 0xd9U, 0x77U, 0x66U, 0x09U, 0x80U, 0xb8U, 0x84U, 0xfaU, 0x81U, 0xb4U, + 0x25U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x30U, + 0x39U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xd4U, + 0x31U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U, 0x09U, + 0x32U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0xc0U, 0x01U, 0xa0U, 0xb0U, 0xe3U, 0xf0U, 0x36U, 0xceU, 0x14U, 0x5fU, 0x98U, 0xc5U, 0xd2U, 0x59U, 0x8fU, + 0x53U, 0x12U, 0x61U, 0xf3U, 0xebU, 0x5eU, 0xc5U, 0xadU, 0xe7U, 0x2aU, 0x92U, 0x1dU, 0xb8U, 0x9eU, 0x28U, 0x75U, + 0x6dU, 0x8bU, 0xd1U, 0xc6U, 0xa0U, 0x64U, 0x96U, 0x99U, 0x1aU, 0xa1U, 0x6dU, 0x6dU, 0xa5U, 0x85U, 0x1aU, 0xd4U, + 0x88U, 0x34U, 0x6fU, 0x71U, 0xd3U, 0xb1U, 0xddU, 0xa7U, 0xbfU, 0x15U, 0x93U, 0xb5U, 0xe2U, 0x61U, 0xb6U, 0xabU, + 0xd3U, 0xf5U, 0x8aU, 0x43U, 0x69U, + }; + + auto rlp = txn.RlpSerializeSigned(); + EXPECT_EQ(rlp, expectedSerialized); + + // std::cout << "Expected RLP: " << + // SimpleObjects::Codec::Hex::Encode(expectedSerialized) << std::endl; + // std::cout << "Generated RLP: " << + // SimpleObjects::Codec::Hex::Encode(rlp) << std::endl; + // std::cout << "Generated R: " << + // SimpleObjects::Codec::Hex::Encode(txn.get_r()) << std::endl; + // std::cout << "Generated S: " << + // SimpleObjects::Codec::Hex::Encode(txn.get_s()) << std::endl; + } + + { + using AbiTupleWriter = AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriterStaticTuple< + AbiWriter, + AbiWriter + > + >, + AbiWriter > + >; + + auto keyPair = mbedTLScpp::EcKeyPair:: + FromSecretNum( + mbedTLScpp::BigNum( + "8b03d7fdc28059800b252d475164a581d94563c9a39f435dc9b130acb5ab93bb", + /*radix=*/16 + ), + *rand + ); + + Transaction::ContractFuncStaticDef func( + gsk_testContractAddr, + "bar" + ); + + auto txn = func.CallByTxn( + SimpleObjects::UInt64(12345), + std::forward_as_tuple( + SimpleObjects::UInt64(54321), + std::forward_as_tuple( + SimpleObjects::UInt64(67890), + SimpleObjects::Bytes({ 0x01U, 0x02U, 0x03U, 0x04U, 0x05U }) + ) + ), + SimpleObjects::Bytes({ 0x01U, 0x02U, 0x03U, 0x04U, 0x05U }) + ); + + txn.SetChainID(1900); + txn.SetNonce(34); + txn.SetMaxPriorFeePerGas(2000000000); + txn.SetMaxFeePerGas(2000000000); + txn.SetGasLimit(100000); + txn.SetAmount(1000); + + Transaction::SignTransaction(txn, keyPair); + + EXPECT_EQ(txn.get_v().size(), 0); + + std::vector expectedSerialized = { + 0x02U, 0xf9U, 0x01U, 0x95U, 0x82U, 0x07U, 0x6cU, 0x22U, 0x84U, 0x77U, 0x35U, 0x94U, 0x00U, 0x84U, 0x77U, 0x35U, + 0x94U, 0x00U, 0x83U, 0x01U, 0x86U, 0xa0U, 0x94U, 0x09U, 0x61U, 0x6cU, 0x3dU, 0x61U, 0xb3U, 0x33U, 0x1fU, 0xc4U, + 0x10U, 0x9aU, 0x9eU, 0x41U, 0xa8U, 0xbdU, 0xb7U, 0xd9U, 0x77U, 0x66U, 0x09U, 0x82U, 0x03U, 0xe8U, 0xb9U, 0x01U, + 0x24U, 0xf4U, 0xdcU, 0x33U, 0x21U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x30U, 0x39U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x60U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0xd4U, 0x31U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x01U, 0x09U, 0x32U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x05U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0xc0U, 0x80U, 0xa0U, 0x7fU, 0x31U, 0xe9U, 0x0dU, 0x3cU, 0x0aU, 0x0cU, 0x0dU, + 0x24U, 0x65U, 0xafU, 0x4eU, 0xc3U, 0x1aU, 0xe1U, 0xe6U, 0x26U, 0xfeU, 0xd0U, 0x26U, 0x09U, 0x89U, 0xf2U, 0xc4U, + 0xc9U, 0x04U, 0x20U, 0x3aU, 0x22U, 0x77U, 0xc3U, 0xa4U, 0xa0U, 0x75U, 0x2aU, 0xc9U, 0x65U, 0x88U, 0xf3U, 0xabU, + 0x28U, 0x08U, 0x78U, 0x7cU, 0x5bU, 0x8fU, 0xa1U, 0xf5U, 0xb3U, 0xadU, 0xc0U, 0x86U, 0xedU, 0x27U, 0x34U, 0x91U, + 0x36U, 0xb1U, 0x94U, 0xc2U, 0x14U, 0xe5U, 0x52U, 0x5eU, 0x77U, + }; + + auto rlp = txn.RlpSerializeSigned(); + EXPECT_EQ(rlp, expectedSerialized); + } +} + diff --git a/test/src/TestEthTxnEcdsa.cpp b/test/src/TestEthTxnEcdsa.cpp index 79f7b87..dd34942 100644 --- a/test/src/TestEthTxnEcdsa.cpp +++ b/test/src/TestEthTxnEcdsa.cpp @@ -503,11 +503,7 @@ GTEST_TEST(TestEthTxnEcdsa, EcdsaRawSign) > out = Transaction::EcdsaRawSign( mbedTLScpp::CtnFullR(hash), key, - sk_secp256k1.BorrowA(), - sk_secp256k1.BorrowGx(), - sk_secp256k1.BorrowGy(), - sk_secp256k1.BorrowN(), - sk_secp256k1.BorrowP() + sk_secp256k1 ); EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); @@ -535,11 +531,7 @@ GTEST_TEST(TestEthTxnEcdsa, EcdsaRawSign) > out = Transaction::EcdsaRawSign( mbedTLScpp::CtnFullR(hash), key, - sk_secp256k1.BorrowA(), - sk_secp256k1.BorrowGx(), - sk_secp256k1.BorrowGy(), - sk_secp256k1.BorrowN(), - sk_secp256k1.BorrowP() + sk_secp256k1 ); EXPECT_EQ(std::get<0>(out), std::get<0>(expOut)); From c9a566eb7d734151cbac3d81d20dbd5814c48c15 Mon Sep 17 00:00:00 2001 From: Haofan Zheng Date: Wed, 26 Jun 2024 20:02:42 -0700 Subject: [PATCH 22/22] Added eth address class and conversion from eckey to addr --- include/EclipseMonitor/Eth/Address.hpp | 134 ++++++++++++++++++ .../EclipseMonitor/Eth/Transaction/Ecdsa.hpp | 38 +++++ test/src/Main.cpp | 2 +- test/src/TestEthAddress.cpp | 112 +++++++++++++++ test/src/TestEthTxnEcdsa.cpp | 67 +++++++++ 5 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 include/EclipseMonitor/Eth/Address.hpp create mode 100644 test/src/TestEthAddress.cpp diff --git a/include/EclipseMonitor/Eth/Address.hpp b/include/EclipseMonitor/Eth/Address.hpp new file mode 100644 index 0000000..a9dba17 --- /dev/null +++ b/include/EclipseMonitor/Eth/Address.hpp @@ -0,0 +1,134 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +#pragma once + + +#include + +#include + +#include "../Exceptions.hpp" +#include "../Internal/SimpleObj.hpp" +#include "DataTypes.hpp" +#include "Keccak256.hpp" + + +namespace EclipseMonitor +{ +namespace Eth +{ + + +class Address +{ +public: // static members: + + using value_type = ContractAddr; + + static constexpr size_t sk_sizeBytes = 20; + +public: + + Address(const value_type& addr) : + m_addr(addr) + {} + + explicit Address(const std::string& addr) : + m_addr() + { + size_t expLen = sk_sizeBytes * 2; + auto begin = addr.begin(); + + // check if the string begins with "0x" + if ( + (addr.size() >= 2) && // at least 2 characters + ((addr[0] == '0') && (addr[1] == 'x')) // starts with "0x" + ) + { + begin += 2; + expLen += 2; + } + + // check if the string is of the correct length + if (addr.size() != expLen) + { + throw Exception( + "The given ETH address hex string is of incorrect length" + ); + } + + Internal::Obj::Codec::Hex::Decode(m_addr.begin(), begin, addr.end()); + } + + Address(const Address& addr) : + m_addr(addr.m_addr) + {} + + Address(Address&& addr) : + m_addr(std::move(addr.m_addr)) + {} + + ~Address() = default; + + bool operator==(const Address& addr) const + { + return m_addr == addr.m_addr; + } + + bool operator!=(const Address& addr) const + { + return m_addr != addr.m_addr; + } + + std::string ToString(const std::string& prefix = "0x") const + { + // std::array should generate a hex string of length 40 + std::string hexLower = + Internal::Obj::Codec::Hex::Encode(m_addr, ""); + // std::array should generate a hex string of length 40 + std::string hexUpper = + Internal::Obj::Codec::HEX::Encode(m_addr, ""); + + // the checksummed address that is going to be generated + std::string checksummed = prefix; + + // The result of a 256-bit hash should have 32 bytes + auto addrHash = Keccak256(hexLower); + + for (size_t i = 0; i < hexLower.size(); ++i) + { + // if `i` is even, the nibble is the higher 4-bit of the byte + // e.g., (0 % 2) = 0, (2 % 2) = 0, (4 % 2) = 0, ... + // 1 - (i % 2) = 1 - 0 = 1 + // (1 - (i % 2)) * 4 = 1 * 4 = 4 + // if `i` is odd, the nibble is the lower 4-bit of the byte + // e.g., (1 % 2) = 1, (3 % 2) = 1, (5 % 2) = 1, ... + // 1 - (i % 2) = 1 - 1 = 0 + // (1 - (i % 2)) * 4 = 0 * 4 = 0 + uint8_t rightShift = (1 - (i % 2)) * 4; + uint8_t hashByte = addrHash[i / 2]; + uint8_t hashNibble = (hashByte >> rightShift) & 0x0FU; + + // if the nibble is greater than 7, the hex should be upper case + // otherwise, the hex should be lower case + char hexCh = hashNibble > 7 ? hexUpper[i] : hexLower[i]; + + checksummed.push_back(hexCh); + } + + return checksummed; + } + + +private: // members: + + value_type m_addr; +}; // class Address + + +} // namespace Eth +} // namespace EclipseMonitor + diff --git a/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp b/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp index 53c800e..beca30f 100644 --- a/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp +++ b/include/EclipseMonitor/Eth/Transaction/Ecdsa.hpp @@ -20,6 +20,7 @@ #include "../../Internal/SimpleObj.hpp" #include "../../Internal/Tls.hpp" #include "../../Exceptions.hpp" +#include "../Address.hpp" #include "../DataTypes.hpp" #include "../Keccak256.hpp" #include "DynamicFee.hpp" @@ -931,6 +932,43 @@ inline void SignTransaction( } +template +inline Address AddressFromPublicKey( + const Internal::Tls::EcPublicKeyBase<_PkeyTraits>& pubKey +) +{ + static constexpr Internal::Tls::EcType sk_ecType = + Internal::Tls::EcType::SECP256K1; + static constexpr size_t sk_curveByteSize = + Internal::Tls::GetCurveByteSize(sk_ecType); + + if (pubKey.GetEcType() != sk_ecType) + { + throw Exception("ETH key should be on curve secp256k1"); + } + + std::vector bytesXY; + bytesXY.reserve(sk_curveByteSize * 2); + auto appendBytesXY = [&bytesXY](std::vector&& bytes) + { + bytesXY.insert(bytesXY.end(), bytes.begin(), bytes.end()); + }; + appendBytesXY(pubKey.BorrowPubPointX().template Bytes()); + appendBytesXY(pubKey.BorrowPubPointY().template Bytes()); + + auto bytesHash = Keccak256(bytesXY); + size_t copyBegins = bytesHash.size() - 20; + ContractAddr bytesHash20; + std::copy_n( + bytesHash.begin() + copyBegins, + bytesHash20.size(), + bytesHash20.begin() + ); + + return Address(bytesHash20); +} + + } // namespace Transaction } // namespace Eth } // namespace EclipseMonitor diff --git a/test/src/Main.cpp b/test/src/Main.cpp index 3c111bf..3707cd0 100644 --- a/test/src/Main.cpp +++ b/test/src/Main.cpp @@ -12,7 +12,7 @@ namespace EclipseMonitor_Test int main(int argc, char** argv) { - constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 26; + constexpr size_t EXPECTED_NUM_OF_TEST_FILE = 27; std::cout << "===== EclipseMonitor test program =====" << std::endl; std::cout << std::endl; diff --git a/test/src/TestEthAddress.cpp b/test/src/TestEthAddress.cpp new file mode 100644 index 0000000..aaf4007 --- /dev/null +++ b/test/src/TestEthAddress.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2024 Haofan Zheng +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + + +#include + +#include + +#include "Common.hpp" + + +namespace EclipseMonitor_Test +{ + extern size_t g_numOfTestFile; +} + +using namespace EclipseMonitor_Test; +using namespace EclipseMonitor::Eth; + + +GTEST_TEST(TestEthAddress, CountTestFile) +{ + static auto tmp = ++EclipseMonitor_Test::g_numOfTestFile; + (void)tmp; +} + + +/** + * @brief + * + * @param addrHexStr this string should avoid the prefix "0x" + * @param prefix + */ +static void TestAddressParseAndString( + const std::string& addrHexStr, + const std::string& prefix = "0x" +) +{ + // parse without the prefix + Address addr(addrHexStr); + auto generatedStr = addr.ToString(""); + EXPECT_EQ(generatedStr, addrHexStr); + + // parse again with the prefix + Address addr2(generatedStr); + auto generatedStr2 = addr2.ToString(prefix); + EXPECT_EQ(generatedStr2, prefix + addrHexStr); + + // two `Address` instances should be equal + EXPECT_EQ(addr, addr2); + EXPECT_FALSE(addr != addr2); + + // test the copy constructor + Address copied(addr); + EXPECT_EQ(copied, addr); + + // test the move constructor + Address moved(std::move(copied)); + EXPECT_EQ(moved, addr); +} + + +GTEST_TEST(TestEthAddress, ParseAndString) +{ + TestAddressParseAndString("010EEE07C4020148D96F80CEd0EE4D129a267D20"); + TestAddressParseAndString("453272C49Dd5b2343Fef13EAdb746E083fB36411"); + TestAddressParseAndString("653E2Bb1258edA29c2F348e88de7F936af8E32C3"); + TestAddressParseAndString("359E745B64498408F11e2811c7376c745084C80f"); + TestAddressParseAndString("9Baa87097A3C3Ff7Fb6428baa2930a031A1Ea4dF"); + TestAddressParseAndString("Dbc12BE0FB8059040b275fe35D6C0c44e420436E"); + TestAddressParseAndString("2bE4803127CD97Abb65F1bE319fA18b6A5567C77"); + TestAddressParseAndString("B39c2ecB0BC4Fa3e75e4Adcb3A59B8cb46AEc16c"); + TestAddressParseAndString("7Bc655F54f53c5ae0aac55d19CCe245368f518AB"); + TestAddressParseAndString("786d53fCc2ac73F3ac8aC21a1E03c0c1bDC70Ad3"); + + // string with incorrect length + EXPECT_THROW_MSG( + TestAddressParseAndString(""), + EclipseMonitor::Exception, + "The given ETH address hex string is of incorrect length" + ); + EXPECT_THROW_MSG( + TestAddressParseAndString("0"), + EclipseMonitor::Exception, + "The given ETH address hex string is of incorrect length" + ); + EXPECT_THROW_MSG( + TestAddressParseAndString("0x"), + EclipseMonitor::Exception, + "The given ETH address hex string is of incorrect length" + ); + EXPECT_THROW_MSG( + TestAddressParseAndString("786d53fCc2"), + EclipseMonitor::Exception, + "The given ETH address hex string is of incorrect length" + ); + EXPECT_THROW_MSG( + TestAddressParseAndString("786d53fCc2ac73F3ac8aC21a1E03c0c1bDC70Ad3786d53fCc2"), + EclipseMonitor::Exception, + "The given ETH address hex string is of incorrect length" + ); + + // string containing invalid characters + EXPECT_THROW_MSG( + TestAddressParseAndString("786d53fCc2ac73F3ac8HC21a1E03c0c1bDC70Ad3"), + std::invalid_argument, + "Invalid hex character" + ); +} + diff --git a/test/src/TestEthTxnEcdsa.cpp b/test/src/TestEthTxnEcdsa.cpp index dd34942..f584675 100644 --- a/test/src/TestEthTxnEcdsa.cpp +++ b/test/src/TestEthTxnEcdsa.cpp @@ -7,6 +7,8 @@ #include #include + +#include #include #include "Common.hpp" @@ -540,3 +542,68 @@ GTEST_TEST(TestEthTxnEcdsa, EcdsaRawSign) } } + +GTEST_TEST(TestEthTxnEcdsa, AddressFromPublicKey) +{ + std::unique_ptr rand = + mbedTLScpp::Internal::make_unique(); + + { + auto keyPair = mbedTLScpp::EcKeyPair:: + FromSecretNum( + mbedTLScpp::BigNum( + "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318", + /*radix=*/16 + ), + *rand + ); + + Address addr = Transaction::AddressFromPublicKey(keyPair); + EXPECT_EQ(addr.ToString(), "0x2c7536E3605D9C16a7a3D7b1898e529396a65c23"); + } + + { + auto keyPair = mbedTLScpp::EcKeyPair:: + FromSecretNum( + mbedTLScpp::BigNum( + "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318", + /*radix=*/16 + ), + *rand + ); + EXPECT_THROW_MSG( + Transaction::AddressFromPublicKey(keyPair), + EclipseMonitor::Exception, + "ETH key should be on curve secp256k1" + ); + } + + { + auto pubKeyDer = SimpleObjects::Codec::Hex::Decode >( + std::string( + "3056301006072a8648ce3d020106052b8104000a034200040630aac5785f14f4" + "cf4713a9ef9b4f32e3e7ae4793de26bdc28c4ea1dd80f8b01bfe05ebb211e441" + "0b2ed29e36c7d9b8ae75f4514d7dd435cb86fc3e5cdf267f" + ) + ); + auto pubKey = mbedTLScpp::EcPublicKey:: + FromDER(mbedTLScpp::CtnFullR(pubKeyDer)); + Address addr = Transaction::AddressFromPublicKey(pubKey); + EXPECT_EQ(addr.ToString(), "0x010EEE07C4020148D96F80CEd0EE4D129a267D20"); + } + + { + auto pubKeyDer = SimpleObjects::Codec::Hex::Decode >( + std::string( + "3056301006072a8648ce3d020106052b8104000a034200049d3dea6bb79267e1" + "1135464ecbf99061b50c8ce852db578616a230d37ac3c0bcbe76bb3fa280b582" + "542a474d16e754e4f83b04cc9448c95c02b16945e4e16063" + ) + ); + auto pubKey = mbedTLScpp::EcPublicKey:: + FromDER(mbedTLScpp::CtnFullR(pubKeyDer)); + Address addr = Transaction::AddressFromPublicKey(pubKey); + EXPECT_EQ(addr.ToString(), "0x453272C49Dd5b2343Fef13EAdb746E083fB36411"); + } +} +