From e6d980d0924ddc3dab8e739f7311a85abd48eeb9 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Thu, 18 Aug 2022 14:54:44 +0300 Subject: [PATCH] 1) raw_transaction 2) signed_transaction 3) rlp_encoder classes --- .../peerplays_sidechain/CMakeLists.txt | 1 + .../peerplays_sidechain/ethereum/encoders.cpp | 95 ++++++++++++ .../ethereum/transaction.cpp | 146 +++++++++++++++++- .../peerplays_sidechain/ethereum/utils.cpp | 13 ++ .../peerplays_sidechain/ethereum/encoders.hpp | 15 ++ .../ethereum/transaction.hpp | 67 +++++--- .../peerplays_sidechain/ethereum/types.hpp | 2 + .../peerplays_sidechain/ethereum/utils.hpp | 9 ++ 8 files changed, 321 insertions(+), 27 deletions(-) create mode 100644 libraries/plugins/peerplays_sidechain/ethereum/utils.cpp create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index ca995c10..e3449d93 100755 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -22,6 +22,7 @@ add_library( peerplays_sidechain ethereum/decoders.cpp ethereum/transaction.cpp ethereum/types.cpp + ethereum/utils.cpp hive/asset.cpp hive/operations.cpp hive/transaction.cpp diff --git a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp index e17bbdfb..218f0be5 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -54,4 +55,98 @@ std::string withdrawal_encoder::encode(const std::string& to, boost::multiprecis } +//! rlp_encoder +std::string rlp_encoder::encode(const std::string& s) +{ + return encode_rlp(hex2bytes(s)); +} + +std::string rlp_encoder::encode_length(int len, int offset) +{ + if(len<56) + { + std::string temp; + temp=(char)(len+offset); + return temp; + } + else + { + std::string hexLength = int2Hex(len); + int lLength = hexLength.size()/2; + std::string fByte = int2Hex(offset+55+lLength); + return hex2bytes(fByte+hexLength); + } +} + +std::string rlp_encoder::hex2bytes(const std::string& s) +{ + std::string dest; + dest.resize(s.size()/2); + hex2bin(s.c_str(), &dest[0]); + return dest; +} + +std::string rlp_encoder::bytes2hex(const std::string& s) +{ + std::string dest; + for( const auto& i : s ) + dest += uchar2Hex((unsigned char)i); + + return dest; +} + +std::string rlp_encoder::encode_rlp(const std::string& s) +{ + if(s.size()==1 && (unsigned char)s[0]<128) + return s; + else + return encode_length(s.size(), 128) + s; +} + +std::string rlp_encoder::int2Hex(int n) +{ + std::stringstream stream; + stream << std::hex << n; + std::string result( stream.str() ); + if(result.size() % 2) + result = "0"+result; + return result; +} + +std::string rlp_encoder::uchar2Hex(unsigned char n) +{ + std::string dest; + dest.resize(2); + sprintf(&dest[0], "%X", n); + + if(n < (unsigned char)16) + { + dest[1] = dest[0]; + dest[0] = '0'; + } + + return dest; +} + +int rlp_encoder::char2int(char input) +{ + if(input >= '0' && input <= '9') + return input - '0'; + if(input >= 'A' && input <= 'F') + return input - 'A' + 10; + if(input >= 'a' && input <= 'f') + return input - 'a' + 10; + + return -1; +} + +void rlp_encoder::hex2bin(const char* src, char* target) +{ + while(*src && src[1]) + { + *(target++) = char2int(*src)*16 + char2int(src[1]); + src += 2; + } +} + }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp index f2d117b5..8d13a648 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp @@ -1,16 +1,61 @@ #include +#include #include #include +#include +#include + +#include +#include + +#include +#include +#include + namespace graphene { namespace peerplays_sidechain { namespace ethereum { -const transaction& transaction::sign(std::string private_key) const + +namespace +{ + +int is_even(char last_char) +{ + switch (last_char) + { + case '0': + return 1; + case '2': + return 1; + case '4': + return 1; + case '6': + return 1; + case '8': + return 1; + case 'A': + return 1; + case 'C': + return 1; + case 'E': + return 1; + default: + return -1; + } +} + +} + +const secp256k1_context *eth_context() { + static secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + return ctx; +} + +//! transaction + +const transaction& transaction::sign(const std::string& private_key) const { -/* v = "signed"; - r = "transaction"; - s = "signed-transaction"; - return v + "|" + r + "|" + s;*/ return *this; } @@ -26,7 +71,7 @@ std::string transaction::serialize() const return ss.str(); } -void transaction::deserialize(std::string raw_tx) +void transaction::deserialize(const std::string& raw_tx) { std::stringstream ss_tx(raw_tx); boost::property_tree::ptree tx_json; @@ -40,4 +85,93 @@ void transaction::deserialize(std::string raw_tx) data = tx_json.get("data"); } +//! raw_transaction + +signed_transaction raw_transaction::sign(const std::string& private_key) const +{ + //! Prepare signed transaction + signed_transaction tr; + tr.nonce = nonce; + tr.gas_price = gas_price; + tr.gas_limit = gas_limit; + tr.to = to; + tr.value = value; + tr.data = data; + + //! Calculate keccak hash of transaction + bytes hash; + hash.resize(32); + const auto transaction_string = boost::algorithm::unhex( serialize() ); + keccak_256((const unsigned char *) transaction_string.data(), transaction_string.size(), (unsigned char *) hash.data()); + + const bytes priv_key = parse_hex(private_key); + + secp256k1_ecdsa_signature sign; + FC_ASSERT(secp256k1_ecdsa_sign(eth_context(), &sign, (const unsigned char *)hash.data(), (const unsigned char *)priv_key.data(), secp256k1_nonce_function_rfc6979, nullptr)); + + fc::ecc::compact_signature result; + FC_ASSERT(secp256k1_ecdsa_signature_serialize_compact(eth_context(), (unsigned char*) result.begin() + 1, &sign)); + + bytes r; + for(int i = 1; i < 33; i++) + r.emplace_back((char) result.at(i)); + bytes s; + for(int i = 33; i < 65; i++) + s.emplace_back((char) result.at(i)); + + bytes v; + if(is_even(r.back())) + v = {37}; + else + v = {38}; + + tr.v = fc::to_hex((char *)&v[0], v.size()); + tr.r = fc::to_hex((char *)&r[0], r.size()); + tr.s = fc::to_hex((char *)&s[0], s.size()); + + return tr; +} + +std::string raw_transaction::serialize() const +{ + rlp_encoder encoder; + const std::string serialized = encoder.encode(nonce) + + encoder.encode(gas_price) + + encoder.encode(gas_limit) + + encoder.encode(to) + + encoder.encode(value) + + encoder.encode(data) + + encoder.encode(chain_id) + + encoder.encode("8") + + encoder.encode("0"); + + return encoder.bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized ); +} + +void raw_transaction::deserialize(const std::string& raw_tx) +{ +} + +//! signed_transaction + +std::string signed_transaction::serialize() const +{ + rlp_encoder encoder; + const std::string serialized = encoder.encode(nonce) + + encoder.encode(gas_price) + + encoder.encode(gas_limit) + + encoder.encode(to) + + encoder.encode(value) + + encoder.encode(data) + + encoder.encode(v) + + encoder.encode(r) + + encoder.encode(s); + + return encoder.bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized ); +} + +void signed_transaction::deserialize(const std::string& raw_tx) +{ +} + }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp new file mode 100644 index 00000000..9d6961bc --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp @@ -0,0 +1,13 @@ +#include + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +bytes parse_hex(const std::string &str) { + bytes vec(str.size() / 2); + fc::from_hex(str, vec.data(), vec.size()); + return vec; +} + +}}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp index 48330aa7..7fcb15ee 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/encoders.hpp @@ -27,6 +27,21 @@ public: std::string encode(const std::string& to, boost::multiprecision::uint256_t amount, const std::string& object_id) const; }; +class rlp_encoder { +public: + static std::string encode(const std::string& s); + static std::string encode_length(int len, int offset); + static std::string hex2bytes(const std::string& s); + static std::string bytes2hex(const std::string& s); + +private: + static std::string encode_rlp(const std::string& s); + static std::string int2Hex(int n); + static std::string uchar2Hex(unsigned char n); + static int char2int(char input); + static void hex2bin(const char* src, char* target); +}; + /*class ethereum_function_call_encoder { public: enum operation_t { diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp index 7c65b7d2..50c3e2f7 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/transaction.hpp @@ -6,33 +6,58 @@ namespace graphene { namespace peerplays_sidechain { namespace ethereum { -class transaction { +class base_transaction +{ + virtual std::string serialize() const = 0; + virtual void deserialize(const std::string& raw_tx) = 0; +}; + +class transaction : base_transaction +{ public: - //std::string block_hash; - //std::string block_number; std::string from; - //std::string gas; - //std::string gas_price; - //std::string max_fee_per_gas; - //std::string max_priority_fee_per_gas; - //std::string hash; - //std::string input; - //std::string nonce; std::string to; std::string data; - //std::string transaction_index; - //std::string value; - //std::string type; - //std::vector access_list; - //std::string chain_id; - //std::string v; - //std::string r; - //std::string s; - const transaction& sign(std::string private_key) const; + const transaction& sign(const std::string& private_key) const; - std::string serialize() const; - void deserialize(std::string raw_tx); + virtual std::string serialize() const override; + virtual void deserialize(const std::string& raw_tx) override; +}; + +class signed_transaction; +class raw_transaction : base_transaction +{ +public: + std::string nonce; + std::string gas_price; + std::string gas_limit; + std::string to; + std::string value; + std::string data; + std::string chain_id; + + signed_transaction sign(const std::string& private_key) const; + + virtual std::string serialize() const override; + virtual void deserialize(const std::string& raw_tx) override; +}; + +class signed_transaction : base_transaction +{ +public: + std::string nonce; + std::string gas_price; + std::string gas_limit; + std::string to; + std::string value; + std::string data; + std::string v; + std::string r; + std::string s; + + virtual std::string serialize() const override; + virtual void deserialize(const std::string& raw_tx) override; }; }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp index def91947..963244fa 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/types.hpp @@ -7,4 +7,6 @@ namespace graphene { namespace peerplays_sidechain { namespace ethereum { typedef uint64_t chain_id_type; typedef uint64_t network_id_type; +using bytes = std::vector; + }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp new file mode 100644 index 00000000..1e2c8802 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace graphene { namespace peerplays_sidechain { namespace ethereum { + +bytes parse_hex(const std::string &str); + +}}} // namespace graphene::peerplays_sidechain::ethereum