rlp_decoder class

This commit is contained in:
Vlad Dobromyslov 2022-08-19 17:34:57 +03:00
parent e6d980d092
commit e3098c3fb6
7 changed files with 317 additions and 33 deletions

View file

@ -1,5 +1,241 @@
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp> #include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
#include <fc/exception/exception.hpp>
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum { 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<std::string> rlp_decoder::decode(const std::string& str)
{
size_t consumed = 0;
const auto raw_vec = parse_hex(str);
const std::vector<std::string> rlp_array = decode_rlp(raw_vec.data(), raw_vec.size(), consumed);
std::vector<std::string> 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<std::string> rlp_decoder::decode_rlp(const unsigned char *raw, size_t len, size_t& consumed)
{
std::vector<std::string> 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<unsigned char> 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<std::string>{};
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<std::string>{};
// parsing done; assign data buffer value.
const std::vector<unsigned char> 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<std::string>{};
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<std::string>{};
// 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<std::string>{};
// parsing done; assign data buffer value.
tok_start = raw + prefixlen + uintlen;
const unsigned char *tok_end = tok_start + slen;
const std::vector<unsigned char> 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<std::string>{};
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<std::string>{};
// 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<std::string>{};
// 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<std::string> rlp_decoder::decode_array(const unsigned char *raw, size_t len, size_t uintlen, size_t payloadlen)
{
std::vector<std::string> 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<std::string>{};
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<unsigned char> rlp_decoder::parse_hex(const std::string& str)
{
return parse_hex(str.c_str());
}
std::vector<unsigned char> rlp_decoder::parse_hex(const char* psz)
{
// convert hex dump to vector
std::vector<unsigned char> 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 }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -71,9 +71,9 @@ std::string rlp_encoder::encode_length(int len, int offset)
} }
else else
{ {
std::string hexLength = int2Hex(len); const std::string hexLength = int2Hex(len);
int lLength = hexLength.size()/2; const int lLength = hexLength.size()/2;
std::string fByte = int2Hex(offset+55+lLength); const std::string fByte = int2Hex(offset+55+lLength);
return hex2bytes(fByte+hexLength); return hex2bytes(fByte+hexLength);
} }
} }
@ -86,15 +86,6 @@ std::string rlp_encoder::hex2bytes(const std::string& s)
return dest; 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) std::string rlp_encoder::encode_rlp(const std::string& s)
{ {
if(s.size()==1 && (unsigned char)s[0]<128) if(s.size()==1 && (unsigned char)s[0]<128)
@ -113,21 +104,6 @@ std::string rlp_encoder::int2Hex(int n)
return 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) int rlp_encoder::char2int(char input)
{ {
if(input >= '0' && input <= '9') if(input >= '0' && input <= '9')

View file

@ -11,6 +11,7 @@
#include <fc/crypto/hex.hpp> #include <fc/crypto/hex.hpp>
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp> #include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/types.hpp> #include <graphene/peerplays_sidechain/ethereum/types.hpp>
#include <graphene/peerplays_sidechain/ethereum/utils.hpp> #include <graphene/peerplays_sidechain/ethereum/utils.hpp>
@ -142,14 +143,25 @@ std::string raw_transaction::serialize() const
encoder.encode(value) + encoder.encode(value) +
encoder.encode(data) + encoder.encode(data) +
encoder.encode(chain_id) + encoder.encode(chain_id) +
encoder.encode("8") + encoder.encode("") +
encoder.encode("0"); 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) 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 //! signed_transaction
@ -167,11 +179,24 @@ std::string signed_transaction::serialize() const
encoder.encode(r) + encoder.encode(r) +
encoder.encode(s); 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) 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 }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -10,4 +10,28 @@ bytes parse_hex(const std::string &str) {
return vec; 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 }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -5,4 +5,25 @@
namespace graphene { namespace peerplays_sidechain { namespace ethereum { 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<std::string> decode(const std::string& str);
private:
static std::vector<std::string> decode_rlp(const unsigned char *raw, size_t len, size_t& consumed);
static std::vector<std::string> 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<unsigned char> parse_hex(const std::string& str);
static std::vector<unsigned char> parse_hex(const char* psz);
static signed char hex_digit(char c);
};
}}} // namespace graphene::peerplays_sidechain::ethereum }}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -32,12 +32,10 @@ public:
static std::string encode(const std::string& s); static std::string encode(const std::string& s);
static std::string encode_length(int len, int offset); static std::string encode_length(int len, int offset);
static std::string hex2bytes(const std::string& s); static std::string hex2bytes(const std::string& s);
static std::string bytes2hex(const std::string& s);
private: private:
static std::string encode_rlp(const std::string& s); static std::string encode_rlp(const std::string& s);
static std::string int2Hex(int n); static std::string int2Hex(int n);
static std::string uchar2Hex(unsigned char n);
static int char2int(char input); static int char2int(char input);
static void hex2bin(const char* src, char* target); static void hex2bin(const char* src, char* target);
}; };

View file

@ -6,4 +6,8 @@ namespace graphene { namespace peerplays_sidechain { namespace ethereum {
bytes parse_hex(const std::string &str); 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 }}} // namespace graphene::peerplays_sidechain::ethereum