diff --git a/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp index fcf468ce..cadb3f65 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/decoders.cpp @@ -1,5 +1,241 @@ #include +#include +#include + namespace graphene { namespace peerplays_sidechain { namespace ethereum { +//! rlp_decoder + +namespace +{ +const signed char p_util_hexdigit[256] = + { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; +} + +std::vector rlp_decoder::decode(const std::string& str) +{ + size_t consumed = 0; + const auto raw_vec = parse_hex(str); + const std::vector rlp_array = decode_rlp(raw_vec.data(), raw_vec.size(), consumed); + std::vector result_array; + for(const auto& rlp : decode_rlp(raw_vec.data(), raw_vec.size(), consumed)) + { + result_array.emplace_back( bytes2hex( rlp ) ); + } + return result_array; +} + +std::vector rlp_decoder::decode_rlp(const unsigned char *raw, size_t len, size_t& consumed) +{ + std::vector rlp_result; + + consumed = 0; + + const unsigned char* end = raw + len; + const size_t prefixlen = 1; + unsigned char ch = *raw; + + if (len < 1) + { + return rlp_result; + } + + // Case 1: [prefix is 1-byte data buffer] + if (ch <= 0x7f) + { + const unsigned char *tok_start = raw; + const unsigned char *tok_end = tok_start + prefixlen; + FC_ASSERT(tok_end <= end); + + // parsing done; assign data buffer value. + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back( buf.cbegin(), buf.cend() ); + + consumed = buf.size(); + } + // Case 2: [prefix, including buffer length][data] + else if ((ch >= 0x80) && (ch <= 0xb7)) + { + const size_t blen = ch - 0x80; + const size_t expected = prefixlen + blen; + + if (len < expected) + return std::vector{}; + + const unsigned char *tok_start = raw + 1; + const unsigned char *tok_end = tok_start + blen; + FC_ASSERT(tok_end <= end); + + // require minimal encoding + if ((blen == 1) && (tok_start[0] <= 0x7f)) + return std::vector{}; + + // parsing done; assign data buffer value. + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back( buf.cbegin(), buf.cend() ); + + consumed = expected; + } + // Case 3: [prefix][buffer length][data] + else if ((ch >= 0xb8) && (ch <= 0xbf)) + { + const size_t uintlen = ch - 0xb7; + size_t expected = prefixlen + uintlen; + + if (len < expected) + return std::vector{}; + + FC_ASSERT(uintlen > 0 && uintlen <= RLP_maxUintLen); + + const unsigned char *tok_start = raw + prefixlen; + if ((uintlen > 1) && (tok_start[0] == 0)) // no leading zeroes + return std::vector{}; + + // read buffer length + const uint64_t slen = to_int(tok_start, uintlen); + + // validate buffer length, including possible addition overflows. + expected = prefixlen + uintlen + slen; + if ((slen < (RLP_listStart - RLP_bufferLenStart - RLP_maxUintLen)) || (expected > len) || (slen > len)) + return std::vector{}; + + // parsing done; assign data buffer value. + tok_start = raw + prefixlen + uintlen; + const unsigned char *tok_end = tok_start + slen; + const std::vector buf{tok_start, tok_end}; + rlp_result.emplace_back( buf.cbegin(), buf.cend() ); + + consumed = expected; + } + // Case 4: [prefix][list] + else if ((ch >= 0xc0) && (ch <= 0xf7)) + { + const size_t payloadlen = ch - 0xc0; + const size_t expected = prefixlen + payloadlen; + + // read list payload + const auto array = decode_array(raw, len, 0, payloadlen); + rlp_result.insert(rlp_result.end(), array.cbegin(), array.cend()); + + consumed = expected; + } + // Case 5: [prefix][list length][list] + else + { + FC_ASSERT((ch >= 0xf8) && (ch <= 0xff)); + + const size_t uintlen = ch - 0xf7; + const size_t expected = prefixlen + uintlen; + + if (len < expected) + return std::vector{}; + + FC_ASSERT(uintlen > 0 && uintlen <= RLP_maxUintLen); + + const unsigned char *tok_start = raw + prefixlen; + if ((uintlen > 1) && (tok_start[0] == 0)) // no leading zeroes + return std::vector{}; + + // read list length + const size_t payloadlen = to_int(tok_start, uintlen); + + // special requirement for non-immediate length + if (payloadlen < (0x100 - RLP_listStart - RLP_maxUintLen)) + return std::vector{}; + + // read list payload + const auto array = decode_array(raw, len, uintlen, payloadlen); + rlp_result.insert(rlp_result.end(), array.cbegin(), array.cend()); + + consumed = prefixlen + uintlen + payloadlen; + } + + return rlp_result; +} + +std::vector rlp_decoder::decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen) +{ + std::vector rlp_result; + const size_t prefixlen = 1; + + // validate list length, including possible addition overflows. + const size_t expected = prefixlen + uintlen + payloadlen; + if ((expected > len) || (payloadlen > len)) + return std::vector{}; + + size_t child_len = payloadlen; + const unsigned char *list_ent = raw + prefixlen + uintlen; + + // recursively read until payloadlen bytes parsed, or error + while (child_len > 0) + { + size_t child_consumed = 0; + + const auto val = decode_rlp(list_ent, child_len, child_consumed); + rlp_result.insert(rlp_result.end(), val.cbegin(), val.cend()); + + list_ent += child_consumed; + child_len -= child_consumed; + } + + return rlp_result; +} + +uint64_t rlp_decoder::to_int(const unsigned char *raw, size_t len) +{ + if (len == 0) + return 0; + else if (len == 1) + return *raw; + else + return (raw[len - 1]) + (to_int(raw, len - 1) * 256); +} + +std::vector rlp_decoder::parse_hex(const std::string& str) +{ + return parse_hex(str.c_str()); +} + +std::vector rlp_decoder::parse_hex(const char* psz) +{ + // convert hex dump to vector + std::vector vch; + while (true) + { + while (isspace(*psz)) + psz++; + signed char c = hex_digit(*psz++); + if (c == (signed char)-1) + break; + unsigned char n = (c << 4); + c = hex_digit(*psz++); + if (c == (signed char)-1) + break; + n |= c; + vch.push_back(n); + } + return vch; +} + +signed char rlp_decoder::hex_digit(char c) +{ + return p_util_hexdigit[(unsigned char)c]; +} + }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp index 218f0be5..456a1be6 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/encoders.cpp @@ -71,9 +71,9 @@ std::string rlp_encoder::encode_length(int len, int offset) } else { - std::string hexLength = int2Hex(len); - int lLength = hexLength.size()/2; - std::string fByte = int2Hex(offset+55+lLength); + const std::string hexLength = int2Hex(len); + const int lLength = hexLength.size()/2; + const std::string fByte = int2Hex(offset+55+lLength); return hex2bytes(fByte+hexLength); } } @@ -86,15 +86,6 @@ std::string rlp_encoder::hex2bytes(const std::string& s) 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) @@ -113,21 +104,6 @@ std::string rlp_encoder::int2Hex(int n) 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') diff --git a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp index 8d13a648..5ca8c81d 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/transaction.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -142,14 +143,25 @@ std::string raw_transaction::serialize() const encoder.encode(value) + encoder.encode(data) + encoder.encode(chain_id) + - encoder.encode("8") + - encoder.encode("0"); + encoder.encode("") + + encoder.encode(""); - return encoder.bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized ); + return bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized ); } void raw_transaction::deserialize(const std::string& raw_tx) { + rlp_decoder decoder; + const auto rlp_array = decoder.decode(raw_tx); + FC_ASSERT(rlp_array.size() >= 7, "Wrong rlp format"); + + nonce = rlp_array.at(0); + gas_price = rlp_array.at(1); + gas_limit = rlp_array.at(2); + to = rlp_array.at(3); + value = rlp_array.at(4); + data = rlp_array.at(5); + chain_id = rlp_array.at(6); } //! signed_transaction @@ -167,11 +179,24 @@ std::string signed_transaction::serialize() const encoder.encode(r) + encoder.encode(s); - return encoder.bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized ); + return bytes2hex( encoder.encode_length(serialized.size(), 192) + serialized ); } void signed_transaction::deserialize(const std::string& raw_tx) { + rlp_decoder decoder; + const auto rlp_array = decoder.decode(raw_tx); + FC_ASSERT(rlp_array.size() >= 9, "Wrong rlp format"); + + nonce = rlp_array.at(0); + gas_price = rlp_array.at(1); + gas_limit = rlp_array.at(2); + to = rlp_array.at(3); + value = rlp_array.at(4); + data = rlp_array.at(5); + v = rlp_array.at(6); + r = rlp_array.at(7); + s = rlp_array.at(8); } }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp index 9d6961bc..0bfe136a 100644 --- a/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp +++ b/libraries/plugins/peerplays_sidechain/ethereum/utils.cpp @@ -10,4 +10,28 @@ bytes parse_hex(const std::string &str) { return vec; } +std::string bytes2hex(const std::string& s) +{ + std::string dest; + for( const auto& i : s ) + dest += uchar2Hex((unsigned char)i); + + return dest; +} + +std::string 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; +} + }}} // namespace graphene::peerplays_sidechain::ethereum diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp index b9a7e927..0f30038f 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/decoders.hpp @@ -5,4 +5,25 @@ namespace graphene { namespace peerplays_sidechain { namespace ethereum { +class rlp_decoder { +private: + enum RLP_constants + { + RLP_maxUintLen = 8, + RLP_bufferLenStart = 0x80, + RLP_listStart = 0xc0, + }; + +public: + static std::vector decode(const std::string& str); + +private: + static std::vector decode_rlp(const unsigned char *raw, size_t len, size_t& consumed); + static std::vector decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen); + static uint64_t to_int(const unsigned char *raw, size_t len); + static std::vector parse_hex(const std::string& str); + static std::vector parse_hex(const char* psz); + static signed char hex_digit(char c); +}; + }}} // 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 7fcb15ee..8c92b2fe 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 @@ -32,12 +32,10 @@ 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); }; 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 index 1e2c8802..4a2e33d1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/ethereum/utils.hpp @@ -6,4 +6,8 @@ namespace graphene { namespace peerplays_sidechain { namespace ethereum { bytes parse_hex(const std::string &str); +std::string bytes2hex(const std::string& s); + +std::string uchar2Hex(unsigned char n); + }}} // namespace graphene::peerplays_sidechain::ethereum