#420 ethereum unit tests #726

Closed
vampik wants to merge 6 commits from feature/420-ethereum-unit-tests into feature/son-for-ethereum
4 changed files with 282 additions and 46 deletions

View file

@ -1,6 +1,7 @@
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
#include <boost/algorithm/hex.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
@ -12,7 +13,6 @@
#include <graphene/peerplays_sidechain/ethereum/decoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
#include <graphene/peerplays_sidechain/ethereum/utils.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
@ -24,6 +24,16 @@ const secp256k1_context *eth_context() {
//! transaction
base_transaction::base_transaction(const std::string &raw_tx) {
}
//! transaction
transaction::transaction(const std::string &raw_tx) :
base_transaction{raw_tx} {
deserialize(raw_tx);
}
const transaction &transaction::sign(const std::string &private_key) const {
return *this;
}
@ -54,6 +64,20 @@ void transaction::deserialize(const std::string &raw_tx) {
//! raw_transaction
raw_transaction::raw_transaction(const std::string &raw_tx) :
base_transaction{raw_tx} {
deserialize(raw_tx);
}
bytes raw_transaction::hash() const {
bytes hash;
hash.resize(32);
const auto transaction_string = boost::algorithm::unhex(remove_0x(serialize()));
keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data());
return hash;
}
signed_transaction raw_transaction::sign(const std::string &private_key) const {
//! Prepare signed transaction
signed_transaction tr;
@ -64,95 +88,142 @@ signed_transaction raw_transaction::sign(const std::string &private_key) const {
tr.value = value;
tr.data = data;
//! Calculate keccak hash of transaction
bytes hash;
hash.resize(32);
const auto transaction_string = boost::algorithm::unhex(remove_0x(serialize()));
keccak_256((const unsigned char *)transaction_string.data(), transaction_string.size(), (unsigned char *)hash.data());
const bytes priv_key = parse_hex(private_key);
int recid = 0;
secp256k1_ecdsa_recoverable_signature sig;
FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash.data(), (const unsigned char *)priv_key.data(), NULL, NULL));
FC_ASSERT(secp256k1_ecdsa_sign_recoverable(eth_context(), &sig, (const unsigned char *)hash().data(), (const unsigned char *)priv_key.data(), NULL, NULL));
fc::ecc::compact_signature result;
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_serialize_compact(eth_context(), (unsigned char *)result.begin() + 1, &recid, &sig));
bytes v = bytes{char(recid + from_hex<int>(chain_id) * 2 + 35)};
bytes r;
for (int i = 1; i < 33; i++)
r.emplace_back((char)result.at(i));
bytes v = bytes{char(recid + from_hex<int>(chain_id) * 2 + 35)};
bytes s;
for (int i = 33; i < 65; i++)
s.emplace_back((char)result.at(i));
tr.v = fc::to_hex((char *)&v[0], v.size());
tr.r = fc::to_hex((char *)&r[0], r.size());
tr.v = fc::to_hex((char *)&v[0], v.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(remove_0x(nonce)) +
encoder.encode(remove_0x(gas_price)) +
encoder.encode(remove_0x(gas_limit)) +
encoder.encode(remove_0x(to)) +
encoder.encode(remove_0x(value)) +
encoder.encode(remove_0x(data)) +
encoder.encode(remove_0x(chain_id)) +
encoder.encode("") +
encoder.encode("");
const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) +
rlp_encoder::encode(remove_0x(gas_price)) +
rlp_encoder::encode(remove_0x(gas_limit)) +
rlp_encoder::encode(remove_0x(to)) +
rlp_encoder::encode(remove_0x(value)) +
rlp_encoder::encode(remove_0x(data)) +
rlp_encoder::encode(remove_0x(chain_id)) +
rlp_encoder::encode("") +
rlp_encoder::encode("");
return add_0x(bytes2hex(encoder.encode_length(serialized.size(), 192) + serialized));
return add_0x(bytes2hex(rlp_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(remove_0x(raw_tx));
const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx));
FC_ASSERT(rlp_array.size() >= 7, "Wrong rlp format");
nonce = add_0x(rlp_array.at(0));
nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0");
boost::algorithm::to_lower(nonce);
gas_price = add_0x(rlp_array.at(1));
boost::algorithm::to_lower(gas_price);
gas_limit = add_0x(rlp_array.at(2));
boost::algorithm::to_lower(gas_limit);
to = add_0x(rlp_array.at(3));
value = add_0x(rlp_array.at(4));
data = add_0x(rlp_array.at(5));
boost::algorithm::to_lower(to);
value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0");
boost::algorithm::to_lower(value);
data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : "";
boost::algorithm::to_lower(data);
chain_id = add_0x(rlp_array.at(6));
boost::algorithm::to_lower(chain_id);
}
//! signed_transaction
std::string signed_transaction::serialize() const {
rlp_encoder encoder;
const std::string serialized = encoder.encode(remove_0x(nonce)) +
encoder.encode(remove_0x(gas_price)) +
encoder.encode(remove_0x(gas_limit)) +
encoder.encode(remove_0x(to)) +
encoder.encode(remove_0x(value)) +
encoder.encode(remove_0x(data)) +
encoder.encode(remove_0x(v)) +
encoder.encode(remove_0x(r)) +
encoder.encode(remove_0x(s));
signed_transaction::signed_transaction(const std::string &raw_tx) :
base_transaction{raw_tx} {
deserialize(raw_tx);
}
return add_0x(bytes2hex(encoder.encode_length(serialized.size(), 192) + serialized));
std::string signed_transaction::recover(const std::string &chain_id) const {
fc::ecc::compact_signature input64;
fc::from_hex(r, (char *)&input64.at(1), 32);
fc::from_hex(v, (char *)&input64.at(0), 1);
int recid = input64.at(0) - from_hex<int>(chain_id) * 2 - 35;
fc::from_hex(s, (char *)&input64.at(33), 32);
secp256k1_ecdsa_recoverable_signature sig;
FC_ASSERT(secp256k1_ecdsa_recoverable_signature_parse_compact(eth_context(), &sig, (const unsigned char *)&input64.data[1], recid));
raw_transaction tr;
tr.nonce = nonce;
tr.gas_price = gas_price;
tr.gas_limit = gas_limit;
tr.to = to;
tr.value = value;
tr.data = data;
tr.chain_id = chain_id;
secp256k1_pubkey rawPubkey;
FC_ASSERT(secp256k1_ecdsa_recover(eth_context(), &rawPubkey, &sig, (const unsigned char *)tr.hash().data()));
std::array<uint8_t, 65> pubkey;
size_t biglen = 65;
FC_ASSERT(secp256k1_ec_pubkey_serialize(eth_context(), pubkey.data(), &biglen, &rawPubkey, SECP256K1_EC_UNCOMPRESSED));
const std::string out = std::string(pubkey.begin(), pubkey.end()).substr(1);
bytes hash;
hash.resize(32);
keccak_256((const unsigned char *)out.data(), out.size(), (unsigned char *)hash.data());
return add_0x(fc::to_hex((char *)&hash[0], hash.size()).substr(24));
}
std::string signed_transaction::serialize() const {
const std::string serialized = rlp_encoder::encode(remove_0x(nonce)) +
rlp_encoder::encode(remove_0x(gas_price)) +
rlp_encoder::encode(remove_0x(gas_limit)) +
rlp_encoder::encode(remove_0x(to)) +
rlp_encoder::encode(remove_0x(value)) +
rlp_encoder::encode(remove_0x(data)) +
rlp_encoder::encode(remove_0x(v)) +
rlp_encoder::encode(remove_0x(r)) +
rlp_encoder::encode(remove_0x(s));
return add_0x(bytes2hex(rlp_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(remove_0x(raw_tx));
const auto rlp_array = rlp_decoder::decode(remove_0x(raw_tx));
FC_ASSERT(rlp_array.size() >= 9, "Wrong rlp format");
nonce = add_0x(rlp_array.at(0));
nonce = !rlp_array.at(0).empty() ? add_0x(rlp_array.at(0)) : add_0x("0");
boost::algorithm::to_lower(nonce);
gas_price = add_0x(rlp_array.at(1));
boost::algorithm::to_lower(gas_price);
gas_limit = add_0x(rlp_array.at(2));
boost::algorithm::to_lower(gas_limit);
to = add_0x(rlp_array.at(3));
value = add_0x(rlp_array.at(4));
data = add_0x(rlp_array.at(5));
boost::algorithm::to_lower(to);
value = !rlp_array.at(4).empty() ? add_0x(rlp_array.at(4)) : add_0x("0");
boost::algorithm::to_lower(value);
data = !rlp_array.at(5).empty() ? add_0x(rlp_array.at(5)) : "";
boost::algorithm::to_lower(data);
v = add_0x(rlp_array.at(6));
boost::algorithm::to_lower(v);
r = add_0x(rlp_array.at(7));
boost::algorithm::to_lower(r);
s = add_0x(rlp_array.at(8));
boost::algorithm::to_lower(s);
}
}}} // namespace graphene::peerplays_sidechain::ethereum

View file

@ -4,9 +4,15 @@
#include <string>
#include <vector>
#include <graphene/peerplays_sidechain/ethereum/types.hpp>
namespace graphene { namespace peerplays_sidechain { namespace ethereum {
class base_transaction {
public:
base_transaction() = default;
base_transaction(const std::string &raw_tx);
virtual std::string serialize() const = 0;
virtual void deserialize(const std::string &raw_tx) = 0;
};
@ -17,6 +23,9 @@ public:
std::string to;
std::string data;
transaction() = default;
transaction(const std::string &raw_tx);
const transaction &sign(const std::string &private_key) const;
virtual std::string serialize() const override;
@ -34,6 +43,10 @@ public:
std::string data;
std::string chain_id;
raw_transaction() = default;
raw_transaction(const std::string &raw_tx);
bytes hash() const;
signed_transaction sign(const std::string &private_key) const;
virtual std::string serialize() const override;
@ -52,6 +65,11 @@ public:
std::string r;
std::string s;
signed_transaction() = default;
signed_transaction(const std::string &raw_tx);
std::string recover(const std::string &chain_id) const;
virtual std::string serialize() const override;
virtual void deserialize(const std::string &raw_tx) override;
};

View file

@ -546,7 +546,7 @@ std::string sidechain_net_handler_ethereum::send_sidechain_transaction(const sid
const auto &transaction = signature.second;
//! Check if we have this signed transaction, if not, don't send it
if(transaction.empty())
if (transaction.empty())
continue;
#ifdef SEND_RAW_TRANSACTION
@ -625,7 +625,7 @@ std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(co
owners_weights.emplace_back(std::make_pair(pub_key_str, son.weight));
}
ethereum::update_owners_encoder encoder;
const ethereum::update_owners_encoder encoder;
return encoder.encode(owners_weights, object_id);
}
@ -634,7 +634,7 @@ std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son
}
std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) {
ethereum::withdrawal_encoder encoder;
const ethereum::withdrawal_encoder encoder;
return encoder.encode(swwo.withdraw_address.substr(2), swwo.withdraw_amount.value * 10000000000, swwo.id.operator std::string());
}

View file

@ -0,0 +1,147 @@
#include <boost/test/unit_test.hpp>
#include <fc/crypto/hex.hpp>
#include <graphene/peerplays_sidechain/ethereum/encoders.hpp>
#include <graphene/peerplays_sidechain/ethereum/transaction.hpp>
using namespace graphene::peerplays_sidechain::ethereum;
BOOST_AUTO_TEST_SUITE(ethereum_transaction_tests)
BOOST_AUTO_TEST_CASE(withdrawal_encoder_test) {
const withdrawal_encoder encoder;
const auto tx = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.0");
BOOST_CHECK_EQUAL(tx, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E300000000000000000000000000000000000000000000000000000");
const auto tx1 = encoder.encode("5fbbb31be52608d2f52247e8400b7fcaa9e0bc12", 10000000000, "1.36.1");
BOOST_CHECK_EQUAL(tx1, "0xe088747b0000000000000000000000005fbbb31be52608d2f52247e8400b7fcaa9e0bc1200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000");
}
BOOST_AUTO_TEST_CASE(update_owners_encoder_test) {
std::vector<std::pair<std::string, uint16_t>> owners_weights;
owners_weights.emplace_back("5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12", 1);
owners_weights.emplace_back("76ce31bd03f601c3fc13732def921c5bac282676", 1);
const update_owners_encoder encoder;
const auto tx = encoder.encode(owners_weights, "1.35.0");
BOOST_CHECK_EQUAL(tx, "0x23ab6adf000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac28267600000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33352E300000000000000000000000000000000000000000000000000000");
owners_weights.emplace_back("09ee460834498a4ee361beb819470061b7381b49", 1);
const auto tx1 = encoder.encode(owners_weights, "1.36.1");
BOOST_CHECK_EQUAL(tx1, "0x23ab6adf0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000030000000000000000000000005FbBb31BE52608D2F52247E8400B7fCaA9E0bC12000000000000000000000000000000000000000000000000000000000000000100000000000000000000000076ce31bd03f601c3fc13732def921c5bac282676000000000000000000000000000000000000000000000000000000000000000100000000000000000000000009ee460834498a4ee361beb819470061b7381b4900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000006312E33362E310000000000000000000000000000000000000000000000000000");
}
BOOST_AUTO_TEST_CASE(raw_transaction_serialization_test) {
raw_transaction raw_tr;
raw_tr.nonce = "0x0";
raw_tr.gas_price = "0x3b9aca07";
raw_tr.gas_limit = "0x7a1200";
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
raw_tr.value = "";
raw_tr.data = "";
raw_tr.chain_id = "0x21";
const auto tx = raw_tr.serialize();
BOOST_CHECK_EQUAL(tx, "0xE480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080");
//! Change value
raw_tr.value = "0x1BC16D674EC80000";
const auto tx1 = raw_tr.serialize();
BOOST_CHECK_EQUAL(tx1, "0xEC80843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348881BC16D674EC8000080218080");
//! Change data
raw_tr.data = "0x893d20e8";
const auto tx2 = raw_tr.serialize();
BOOST_CHECK_EQUAL(tx2, "0xF080843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348881BC16D674EC8000084893D20E8218080");
}
BOOST_AUTO_TEST_CASE(raw_transaction_deserialization_test) {
const raw_transaction raw_tr{"E480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080"};
BOOST_CHECK_EQUAL(raw_tr.nonce, "0x0");
BOOST_CHECK_EQUAL(raw_tr.gas_price, "0x3b9aca07");
BOOST_CHECK_EQUAL(raw_tr.gas_limit, "0x7a1200");
BOOST_CHECK_EQUAL(raw_tr.to, "0x875a7e0efe5140c80c5c822f99c02281c0290348");
BOOST_CHECK_EQUAL(raw_tr.value, "0x0");
BOOST_CHECK_EQUAL(raw_tr.data, "");
BOOST_CHECK_EQUAL(raw_tr.chain_id, "0x21");
}
BOOST_AUTO_TEST_CASE(raw_transaction_hash_test) {
raw_transaction raw_tr;
raw_tr.nonce = "0x0";
raw_tr.gas_price = "0x3b9aca07";
raw_tr.gas_limit = "0x7a1200";
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
raw_tr.value = "";
raw_tr.data = "";
raw_tr.chain_id = "0x21";
const auto tx = raw_tr.serialize();
BOOST_CHECK_EQUAL(tx, "0xE480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C02903488080218080");
const auto hash = raw_tr.hash();
const auto hash_str = fc::to_hex((char *)&hash[0], hash.size());
BOOST_CHECK_EQUAL(hash_str, "34934410cd305f4fa4e75a2c9294d625d6fbba729b5642ed2ca757ead50bb1fb");
}
BOOST_AUTO_TEST_CASE(sign_transaction_test) {
raw_transaction raw_tr;
raw_tr.nonce = "0x0";
raw_tr.gas_price = "0x3b9aca07";
raw_tr.gas_limit = "0x7a1200";
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
raw_tr.value = "";
raw_tr.data = "";
raw_tr.chain_id = "0x21";
const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060");
BOOST_CHECK_EQUAL(sign_tr.r, "5f09de6ac850b2a9e94acd709c12d4e9adbabc6b72281ec0bbe13bca7e57c7ce");
BOOST_CHECK_EQUAL(sign_tr.v, "65");
BOOST_CHECK_EQUAL(sign_tr.s, "7ca5f26c5b3e25f14a32b18ac9a2a41b7c68efd3b04b118e1b1f4bf1c4e299b0");
}
BOOST_AUTO_TEST_CASE(sign_transaction_serialization_test) {
raw_transaction raw_tr;
raw_tr.nonce = "0x0";
raw_tr.gas_price = "0x3b9aca07";
raw_tr.gas_limit = "0x7a1200";
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
raw_tr.value = "";
raw_tr.data = "";
raw_tr.chain_id = "0x21";
const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060");
const auto tx = sign_tr.serialize();
BOOST_CHECK_EQUAL(tx, "0xF86480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348808065A05F09DE6AC850B2A9E94ACD709C12D4E9ADBABC6B72281EC0BBE13BCA7E57C7CEA07CA5F26C5B3E25F14A32B18AC9A2A41B7C68EFD3B04B118E1B1F4BF1C4E299B0");
}
BOOST_AUTO_TEST_CASE(sign_transaction_deserialization_test) {
const signed_transaction sign_tr{"0xF86480843B9ACA07837A120094875A7E0EFE5140C80C5C822F99C02281C0290348808065A05F09DE6AC850B2A9E94ACD709C12D4E9ADBABC6B72281EC0BBE13BCA7E57C7CEA07CA5F26C5B3E25F14A32B18AC9A2A41B7C68EFD3B04B118E1B1F4BF1C4E299B0"};
BOOST_CHECK_EQUAL(sign_tr.nonce, "0x0");
BOOST_CHECK_EQUAL(sign_tr.gas_price, "0x3b9aca07");
BOOST_CHECK_EQUAL(sign_tr.gas_limit, "0x7a1200");
BOOST_CHECK_EQUAL(sign_tr.to, "0x875a7e0efe5140c80c5c822f99c02281c0290348");
BOOST_CHECK_EQUAL(sign_tr.value, "0x0");
BOOST_CHECK_EQUAL(sign_tr.data, "");
}
BOOST_AUTO_TEST_CASE(sign_transaction_recover_test) {
const std::string chain_id = "0x21";
raw_transaction raw_tr;
raw_tr.nonce = "0x0";
raw_tr.gas_price = "0x3b9aca07";
raw_tr.gas_limit = "0x7a1200";
raw_tr.to = "0x875a7e0eFe5140c80C5c822f99C02281C0290348";
raw_tr.value = "";
raw_tr.data = "";
raw_tr.chain_id = chain_id;
const auto sign_tr = raw_tr.sign("eb5749a569e6141a3b08249d4a0d84f9ef22c67651ba29adb8eb6fd43fc83060");
const auto from = sign_tr.recover(chain_id);
BOOST_CHECK_EQUAL(from, "0x5fbbb31be52608d2f52247e8400b7fcaa9e0bc12");
}
BOOST_AUTO_TEST_SUITE_END()