diff --git a/evm/processor.cpp b/evm/processor.cpp index 1e3d1c7..bf7675f 100644 --- a/evm/processor.cpp +++ b/evm/processor.cpp @@ -1150,7 +1150,7 @@ namespace evm prepare_mem_access(offset, size); uint8_t h[32]; - Keccak_256(ctxt->mem.data() + offset, static_cast(size), h); + keccak_256(ctxt->mem.data() + offset, static_cast(size), h); ctxt->s.push(from_big_endian(h, h + sizeof(h))); } diff --git a/evm/util.cpp b/evm/util.cpp index cbc25fc..18ed29c 100644 --- a/evm/util.cpp +++ b/evm/util.cpp @@ -50,7 +50,7 @@ namespace evm const auto rlp_encoding = rlp::encode(sender, nonce); uint8_t buffer[32u]; - Keccak_256( + keccak_256( rlp_encoding.data(), static_cast(rlp_encoding.size()), buffer); diff --git a/include/bigint.h b/include/bigint.h index 4c6cf1b..85b2ac8 100644 --- a/include/bigint.h +++ b/include/bigint.h @@ -39,6 +39,13 @@ inline auto to_hex_str(const uint256_t& v) return ss.str(); } +inline auto to_lower_hex_str(const uint256_t& v) +{ + auto s = to_hex_str(v); + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + return s; +} + template auto from_big_endian(const Iterator begin, const Iterator end) { diff --git a/include/util.h b/include/util.h index d1dc2e4..12ae34e 100644 --- a/include/util.h +++ b/include/util.h @@ -38,7 +38,7 @@ namespace evm assign_const(x, std::move(t)); } - inline void Keccak_256( + inline void keccak_256( const unsigned char* input, unsigned int inputByteLen, unsigned char* output) @@ -55,6 +55,21 @@ namespace evm Keccak_HashFinal(&hi, output); } + inline std::array keccak_256( + const unsigned char* begin, size_t byte_len) + { + std::array h; + keccak_256(begin, byte_len, h.data()); + return h; + } + + inline std::array keccak_256( + const std::string& s, size_t skip = 0) + { + skip = std::min(skip, s.size()); + return keccak_256((const unsigned char*)s.data() + skip, s.size() - skip); + } + std::string strip(const std::string& s); std::vector to_bytes(const std::string& s); @@ -79,6 +94,37 @@ namespace evm return to_hex_string(bytes.begin(), bytes.end()); } + inline std::string to_checksum_address(const Address& a) + { + auto s = to_lower_hex_str(a); + + // Start at index 2 to skip the "0x" prefix + const auto h = keccak_256(s, 2); + + std::cout << s << std::endl; + std::cout << to_hex_string(h) << std::endl; + + for (size_t i = 0; i < s.size() - 2; ++i) + { + auto& c = s[i + 2]; + if (c >= 'a' && c <= 'f') + { + if (h[i / 2] & (i % 2 == 0 ? 0x80 : 0x08)) + { + c = std::toupper(c); + } + } + } + + return s; + } + + inline bool is_checksum_address(const std::string& s) + { + const auto cs = to_checksum_address(from_hex_str(s)); + return cs == s; + } + Address generate_address(const Address& sender, uint64_t nonce); uint64_t to_uint64(const nlohmann::json& j); diff --git a/samples/erc20/main.cpp b/samples/erc20/main.cpp index 495b963..eb547ce 100644 --- a/samples/erc20/main.cpp +++ b/samples/erc20/main.cpp @@ -48,15 +48,6 @@ evm::Address get_random_address() } /////////////////////////////////////////////////////////////////////////////// -// Truncate 160-bit addresses to a more human-friendly length, retaining the -// start and end for identification -std::string short_name(const evm::Address& address) -{ - const auto full_hex = to_hex_str(address); - return full_hex.substr(0, 5) + std::string("...") + - full_hex.substr(full_hex.size() - 3); -} - // Run input as an EVM transaction, check the result and return the output std::vector run_and_check_result( Environment& env, @@ -189,8 +180,8 @@ bool transfer( append_argument(function_call, amount); std::cout << "Transferring " << amount << " from " - << short_name(source_address) << " to " - << short_name(target_address); + << evm::to_checksum_address(source_address) << " to " + << evm::to_checksum_address(target_address); const auto output = run_and_check_result(env, source_address, contract_address, function_call); @@ -258,7 +249,7 @@ void print_erc20_state( std::cout << "User balances: " << std::endl; for (const auto& pair : balances) { - std::cout << " " << pair.second << " owned by " << short_name(pair.first); + std::cout << " " << pair.second << " owned by " << evm::to_checksum_address(pair.first); if (pair.first == env.owner_address) { std::cout << " (original contract creator)"; diff --git a/samples/sum/main.cpp b/samples/sum/main.cpp index 6e279f9..8400bff 100644 --- a/samples/sum/main.cpp +++ b/samples/sum/main.cpp @@ -155,11 +155,11 @@ int main(int argc, char** argv) const uint256_t result = from_big_endian(e.output.begin(), e.output.end()); - std::cout << to_hex_str(arg_a); + std::cout << to_lower_hex_str(arg_a); std::cout << " + "; - std::cout << to_hex_str(arg_b); + std::cout << to_lower_hex_str(arg_b); std::cout << " = "; - std::cout << to_hex_str(result); + std::cout << to_lower_hex_str(result); std::cout << std::endl; if (verbose) diff --git a/tests/main.cpp b/tests/main.cpp index 10b7e3a..e2889d5 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -29,6 +29,59 @@ TEST_CASE("util" * doctest::test_suite("util")) REQUIRE(to_bytes("0xabc") == vector{0xa, 0xbc}); REQUIRE(to_bytes("0xabcd") == vector{0xab, 0xcd}); } + + SUBCASE("keccak_256") + { + const std::string empty; + REQUIRE( + to_hex_string(keccak_256(empty)) == + "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + + REQUIRE( + to_hex_string(keccak_256(empty, 5)) == + "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + + const std::string s = "Hello world"; + REQUIRE( + to_hex_string(keccak_256(s)) == + "0xed6c11b0b5b808960df26f5bfc471d04c1995b0ffd2055925ad1be28d6baadfd"); + + REQUIRE( + to_hex_string(keccak_256(s, 1)) == + "0x06f5a9ffe20e0fda47399119d5f89e6ea5aa7442fdbc973c365ef4ad993cde12"); + + REQUIRE( + to_hex_string(keccak_256(s, 6)) == + "0x8452c9b9140222b08593a26daa782707297be9f7b3e8281d7b4974769f19afd0"); + } + + SUBCASE("to_checksum_address") + { + const Address t0 = + from_hex_str("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"); + REQUIRE( + to_checksum_address(t0) == "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); + + const Address t1 = + from_hex_str("0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"); + REQUIRE( + to_checksum_address(t1) == "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"); + + const Address t2 = + from_hex_str("0xDBF03B407C01E7CD3CBEA99509D93F8DDDC8C6FB"); + REQUIRE( + to_checksum_address(t2) == "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB"); + + const Address t3 = + from_hex_str("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"); + REQUIRE( + to_checksum_address(t3) == "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"); + + REQUIRE(is_checksum_address("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")); + REQUIRE(is_checksum_address("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")); + REQUIRE(is_checksum_address("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")); + REQUIRE(is_checksum_address("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")); + } } TEST_CASE("byteExport" * doctest::test_suite("primitive"))