From 59d22f1dac3eb7686975ad82c07cb0e5ad09d3a2 Mon Sep 17 00:00:00 2001 From: as-iotex Date: Mon, 11 Apr 2022 13:32:26 +0100 Subject: [PATCH] feat: add bignumber abi decoding --- CMakeLists.txt | 7 ++- src/abi/abiDecode.cpp | 22 +++++++++ src/abi/abiDecode.h | 10 +++++ src/bignum/bignum.cpp | 35 +++++++++++++++ src/bignum/bignum.h | 49 +++++++++++++++++++++ tests/src/abi/decode/abiDecodeUintTests.cpp | 24 +++++++++- 6 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 src/bignum/bignum.cpp create mode 100644 src/bignum/bignum.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cfb3d4..6432997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,9 @@ add_library(iotex-client STATIC src/extern/crypto/ed25519-donna/ed25519.c # CppLogger src/extern/cpplogger/cpplogger.cpp + # Bignum + src/extern/uint256_t/uint256_t.cpp + src/extern/uint256_t/uint128_t/uint128_t.cpp # Protobuf src/protobuf_files/c_files/action.pb.c @@ -65,6 +68,7 @@ add_library(iotex-client STATIC src/abi/abiDecode.cpp src/helpers/json_helper.cpp src/helpers/client_helper.cpp + src/bignum/bignum.cpp src/contract/contract.cpp src/contract/xrc20Contract.cpp ) @@ -78,7 +82,8 @@ target_include_directories(iotex-client PRIVATE src/protobuf_files/c_files PRIVATE src/extern/crypto # Needed for sources inside crypto PRIVATE src/extern/nanopb - PUBLIC src/extern + PUBLIC src/extern/uint256_t/uint128_t/ # Needed for sources inside uint256_t + PUBLIC src/extern ) target_compile_definitions(iotex-client diff --git a/src/abi/abiDecode.cpp b/src/abi/abiDecode.cpp index 476b38a..283c4f3 100644 --- a/src/abi/abiDecode.cpp +++ b/src/abi/abiDecode.cpp @@ -8,6 +8,7 @@ using namespace iotex; using namespace std; using namespace iotex::abi; +using namespace iotex::bignum; namespace { @@ -43,6 +44,27 @@ uint64_t iotex::abi::decode::decodeUint64(const char pData[64]) return out; } +ResultCode iotex::abi::decode::decodeBigUint(const char* pData, size_t uintSize, iotex::bignum::Bignum& out) +{ + if (uintSize == 0 || uintSize % 8 || uintSize > 256) + { + return ResultCode::ERROR_BAD_PARAMETER; + } + + uint8_t bytesSize = uintSize/8; + uint8_t paddingBytes = 32 - bytesSize; + pData += paddingBytes*2; + + // Copy to a new buffer so we can zero terminate it and pass the string to the bignum constructor. + char zeroTerminated[bytesSize*2 + 1]; + zeroTerminated[bytesSize*2] = '\0'; + memcpy(zeroTerminated, pData, bytesSize*2); + + out = Bignum(zeroTerminated, NumericBase::Base16); + + return ResultCode::SUCCESS; +} + int8_t iotex::abi::decode::decodeInt8(const char pData[64]) { int64_t out = 0; diff --git a/src/abi/abiDecode.h b/src/abi/abiDecode.h index 6d7f001..eb47830 100644 --- a/src/abi/abiDecode.h +++ b/src/abi/abiDecode.h @@ -1,6 +1,7 @@ #ifndef IOTEX_ABI_DECODE_H #define IOTEX_ABI_DECODE_H +#include "bignum/bignum.h" #include "helpers/client_helper.h" #include "IoTeXConstants.h" #include "IoTeXResultCodes.h" @@ -55,6 +56,15 @@ uint32_t decodeUint32(const char pData[64]); */ uint64_t decodeUint64(const char pData[64]); +/** + * @brief Decodes an ABI encoded big unsigned integer (up to 256 bit). + * + * @param pData The ABI encoded data. + * @param uintSize The size of the integer in bits. + * @param[out] out The decoded integer. + */ +iotex::ResultCode decodeBigUint(const char* pData, size_t uintSize, iotex::bignum::Bignum& out); + /** * @brief Decodes an ABI encoded 8 bit signed integer. * diff --git a/src/bignum/bignum.cpp b/src/bignum/bignum.cpp new file mode 100644 index 0000000..292dd4e --- /dev/null +++ b/src/bignum/bignum.cpp @@ -0,0 +1,35 @@ +#include "bignum/bignum.h" + +using namespace iotex::bignum; + +Bignum::Bignum() +{ + _u256 = uint256_0; +} + +Bignum::Bignum(const char* str, NumericBase base) +{ + uint8_t baseInt = 16; + if (base == NumericBase::Base10) { baseInt = 10; } + + _u256 = uint256_t(str, baseInt); +} + +IotexString Bignum::ToString(NumericBase base) const +{ + if (base == NumericBase::Base10) + { + return _u256.str(10); + } + else if (base == NumericBase::Base16) + { + return _u256.str(16); + } + else return ""; +} + +Bignum& Bignum::operator=(const Bignum& bignum) +{ + _u256 = uint256_t(bignum.ToString(NumericBase::Base16), 16); + return *this; +} \ No newline at end of file diff --git a/src/bignum/bignum.h b/src/bignum/bignum.h new file mode 100644 index 0000000..c57b19a --- /dev/null +++ b/src/bignum/bignum.h @@ -0,0 +1,49 @@ +#pragma once + +#include "extern/uint256_t/uint256_t.h" +#include "helpers/client_helper.h" + +namespace iotex +{ +namespace bignum +{ +enum NumericBase +{ + Base10, + Base16 +}; + +/** + * @brief Represents an unigned integer of up to 256 bytes. + * + */ +class Bignum +{ + public: + /** + * @brief Default constructor. Constructs a Bignum with the value of 0. + */ + Bignum(); + + /** + * @brief Constructs a Bignum from a string value. + * + * @param str The value. + * @param base The numeric base of the string (Decimal or Hexadecimal). + */ + Bignum(const char* str, NumericBase base); + + /** + * @brief Returns the value as a string. + * + * @param base The numeric base to use (Decimal or Hexadecimal). + */ + IotexString ToString(NumericBase base) const; + + Bignum& operator=(const Bignum& bignum); + + private: + uint256_t _u256; +}; +} // namespace bignum +} // namespace iotex \ No newline at end of file diff --git a/tests/src/abi/decode/abiDecodeUintTests.cpp b/tests/src/abi/decode/abiDecodeUintTests.cpp index 947b167..8cd6da7 100644 --- a/tests/src/abi/decode/abiDecodeUintTests.cpp +++ b/tests/src/abi/decode/abiDecodeUintTests.cpp @@ -4,11 +4,13 @@ #include "abi/abiDecode.h" #include "contract/contract.h" #include "signer/signer.h" +#include "bignum/bignum.h" using namespace std; using namespace testing; using namespace iotex; using namespace iotex::abi::decode; +using namespace iotex::bignum; class AbiDecodeUintTests : public Test { @@ -109,4 +111,24 @@ TEST_F(AbiDecodeUintTests, Handles0xPrefix) ResultCode result = decodeUint<1>(encoded, strlen(encoded), &decoded); ASSERT_EQ(ResultCode::SUCCESS, result); ASSERT_EQ(29, decoded); -} \ No newline at end of file +} + +TEST_F(AbiDecodeUintTests, Bignum_Ok) +{ + const char expectedDecimal[] = "446371678961165142885801714189622662489706559239886995455"; + char encoded[] = "0000000000000000123456789acbdeffffffffffffffffffffffffffffffffff"; + + Bignum decoded; + + // Decode as uint64 + ResultCode result = decodeBigUint(encoded, 256, decoded); + ASSERT_EQ(ResultCode::SUCCESS, result); + auto decodedStr = decoded.ToString(NumericBase::Base10).c_str(); + ASSERT_STREQ(expectedDecimal, decodedStr); + + // Decode as 24 bytes. This tests it handles the padding bytes correctly. + result = decodeBigUint(encoded, 192, decoded); + ASSERT_EQ(ResultCode::SUCCESS, result); + decodedStr = decoded.ToString(NumericBase::Base10).c_str(); + ASSERT_STREQ(expectedDecimal, decodedStr); +}