mirror of
https://github.com/monero-project/monero.git
synced 2025-01-24 23:06:39 -05:00
unit_tests: test every single field of JSON tx repr
This commit is contained in:
parent
2d855f8d67
commit
b042ce64a5
BIN
tests/data/txs/rct_bp_compact_tx_10312fd4.bin
Normal file
BIN
tests/data/txs/rct_bp_compact_tx_10312fd4.bin
Normal file
Binary file not shown.
BIN
tests/data/txs/rct_bp_tx_a685d68e.bin
Normal file
BIN
tests/data/txs/rct_bp_tx_a685d68e.bin
Normal file
Binary file not shown.
BIN
tests/data/txs/rct_clsag_tx_200c3215.bin
Normal file
BIN
tests/data/txs/rct_clsag_tx_200c3215.bin
Normal file
Binary file not shown.
BIN
tests/data/txs/rct_full_tx_14056427.bin
Normal file
BIN
tests/data/txs/rct_full_tx_14056427.bin
Normal file
Binary file not shown.
BIN
tests/data/txs/rct_simple_tx_c69861bf.bin
Normal file
BIN
tests/data/txs/rct_simple_tx_c69861bf.bin
Normal file
Binary file not shown.
BIN
tests/data/txs/v1_coinbase_tx_bf4c0300.bin
Normal file
BIN
tests/data/txs/v1_coinbase_tx_bf4c0300.bin
Normal file
Binary file not shown.
BIN
tests/data/txs/v1_tx_hf3_effcceb9.bin
Normal file
BIN
tests/data/txs/v1_tx_hf3_effcceb9.bin
Normal file
Binary file not shown.
BIN
tests/data/txs/v2_coinbase_tx_7f88a52a.bin
Normal file
BIN
tests/data/txs/v2_coinbase_tx_7f88a52a.bin
Normal file
Binary file not shown.
@ -12,9 +12,14 @@
|
||||
#include "cryptonote_basic/cryptonote_basic.h"
|
||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
#include "serialization/json_object.h"
|
||||
#include "file_io_utils.h"
|
||||
#include "rpc/daemon_messages.h"
|
||||
#include "serialization/json_object.h"
|
||||
#include "string_tools.h"
|
||||
#include "unit_tests_utils.h"
|
||||
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "cn"
|
||||
|
||||
namespace test
|
||||
{
|
||||
@ -107,6 +112,430 @@ namespace
|
||||
cryptonote::json::fromJsonValue(doc, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
bool compare_tx_to_json_tx(const cryptonote::transaction &tx, const std::string &tx_json)
|
||||
{
|
||||
using namespace epee::string_tools; // for pod_to_hex()
|
||||
|
||||
const crypto::hash txid = get_transaction_hash(tx);
|
||||
MINFO("Asserting validity of JSON representation of tx " << txid);
|
||||
|
||||
rapidjson::Document tx_document;
|
||||
tx_document.Parse(tx_json.data());
|
||||
CHECK_AND_ASSERT_MES(!tx_document.HasParseError(), false, "compare_tx_to_json_tx: failed to parse json");
|
||||
|
||||
// check existence of prefix members
|
||||
CHECK_AND_ASSERT_MES(tx_document.HasMember("version") && tx_document.HasMember("unlock_time") &&
|
||||
tx_document.HasMember("vin") && tx_document.HasMember("vout") && tx_document.HasMember("extra"),
|
||||
false, "compare_tx_to_json_tx: missing prefix members");
|
||||
|
||||
// version
|
||||
CHECK_AND_ASSERT_MES(tx_document["version"].IsInt(), false, "compare_tx_to_json_tx: version isnt integer");
|
||||
CHECK_AND_ASSERT_MES(tx_document["version"].GetInt() == tx.version, false, "compare_tx_to_json_tx: wrong version");
|
||||
|
||||
// unlock_time
|
||||
CHECK_AND_ASSERT_MES(tx_document["unlock_time"].IsUint64(), false, "compare_tx_to_json_tx: wrong unlock time");
|
||||
CHECK_AND_ASSERT_MES(tx_document["unlock_time"].GetUint64() == tx.unlock_time, false, "compare_tx_to_json_tx: wrong unlock time");
|
||||
|
||||
// extra
|
||||
CHECK_AND_ASSERT_MES(tx_document["extra"].IsArray(), false, "compare_tx_to_json_tx: extra is not string");
|
||||
CHECK_AND_ASSERT_MES(tx_document["extra"].Size() == tx.extra.size(), false, "compare_tx_to_json_tx: wrong extra size");
|
||||
for (size_t i = 0; i < tx.extra.size(); ++i)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(tx_document["extra"][i].IsInt(), false, "compare_tx_to_json_tx: extra array contains non-int");
|
||||
CHECK_AND_ASSERT_MES(tx_document["extra"][i].GetInt() == tx.extra[i], false, "compare_tx_to_json_tx: extra array contains wrong int");
|
||||
}
|
||||
|
||||
// vin
|
||||
const size_t num_inputs = tx.vin.size();
|
||||
size_t ring_size = 0;
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"].IsArray(), false, "compare_tx_to_json_tx: vin is not array");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"].Size() == tx.vin.size(), false, "compare_tx_to_json_tx: vin wrong size");
|
||||
for (size_t i = 0; i < tx_document["vin"].Size(); i++)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i].IsObject(), false, "compare_tx_to_json_tx: txin not object");
|
||||
if (tx.vin[i].type() == typeid(cryptonote::txin_to_key))
|
||||
{
|
||||
const cryptonote::txin_to_key &k = boost::get<cryptonote::txin_to_key>(tx.vin[i]);
|
||||
ring_size = k.key_offsets.size();
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i].HasMember("key"), false, "compare_tx_to_json_tx: txin wrong type");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"].IsObject(), false, "compare_tx_to_json_tx: txin is not object");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"].HasMember("amount"), false, "compare_tx_to_json_tx: txin does not have amount");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"]["amount"].IsUint64(), false, "compare_tx_to_json_tx: txin amount is not int");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"]["amount"].GetUint64() == k.amount, false, "compare_tx_to_json_tx: txin wrong amount");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"].HasMember("k_image"), false, "compare_tx_to_json_tx: txin does not have k_image");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"]["k_image"].IsString(), false, "compare_tx_to_json_tx: txin kimage is not string");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"]["k_image"].GetString() == pod_to_hex(k.k_image), false, "compare_tx_to_json_tx: txin wrong k_image");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"].HasMember("key_offsets"), false, "compare_tx_to_json_tx: txin does not have key_offsets");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"]["key_offsets"].IsArray(), false, "compare_tx_to_json_tx: txin key_offsets is not array");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"]["key_offsets"].Size() == k.key_offsets.size(), false, "compare_tx_to_json_tx: txin wrong key_offsets size");
|
||||
for (size_t o = 0; o < k.key_offsets.size(); ++o)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"]["key_offsets"][o].IsInt(), false, "compare_tx_to_json_tx: txin key offset is not int");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["key"]["key_offsets"][o].GetInt() == k.key_offsets[o], false, "compare_tx_to_json_tx: wrong key_offsets entry in txin");
|
||||
}
|
||||
}
|
||||
else if (tx.vin[i].type() == typeid(cryptonote::txin_gen))
|
||||
{
|
||||
const cryptonote::txin_gen &g = boost::get<cryptonote::txin_gen>(tx.vin[i]);
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i].HasMember("gen"), false, "compare_tx_to_json_tx: txin wrong type");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["gen"].IsObject(), false, "compare_tx_to_json_tx: txin is not object");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["gen"].HasMember("height"), false, "compare_tx_to_json_tx: txin does not have height");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["gen"]["height"].IsInt(), false, "compare_tx_to_json_tx: txin height is not int");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vin"][i]["gen"]["height"].GetInt() == g.height, false, "compare_tx_to_json_tx: txin wrong amount");
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(false, false, "compare_tx_to_json_tx: can't check non to_key and gen inputs");
|
||||
}
|
||||
}
|
||||
|
||||
// vout
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"].IsArray(), false, "compare_tx_to_json_tx: vout is not array");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"].Size() == tx.vout.size(), false, "compare_tx_to_json_tx: vout wrong size");
|
||||
for (size_t i = 0; i < tx_document["vout"].Size(); i++)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i].IsObject(), false, "compare_tx_to_json_tx: txout not object");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i].HasMember("amount"), false, "compare_tx_to_json_tx: txout does not have amount");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["amount"].IsUint64(), false, "compare_tx_to_json_tx: txout amount is not int");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["amount"].GetUint64() == tx.vout[i].amount, false, "compare_tx_to_json_tx: txout wrong amount");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i].HasMember("target"), false, "compare_tx_to_json_tx: txout has no target");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"].IsObject(), false, "compare_tx_to_json_tx: txout target is not an object");
|
||||
if (tx.vout[i].target.type() == typeid(cryptonote::txout_to_key))
|
||||
{
|
||||
const cryptonote::txout_to_key &k = boost::get<cryptonote::txout_to_key>(tx.vout[i].target);
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"].HasMember("key"), false, "compare_tx_to_json_tx: txout target wrong type");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["key"].IsString(), false, "compare_tx_to_json_tx: txout target onetime-address not string");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["key"].GetString() == pod_to_hex(k.key), false, "compare_tx_to_json_tx: txout target wrong onetime-address");
|
||||
}
|
||||
else if (tx.vout[i].target.type() == typeid(cryptonote::txout_to_tagged_key))
|
||||
{
|
||||
const cryptonote::txout_to_tagged_key &k = boost::get<cryptonote::txout_to_tagged_key>(tx.vout[i].target);
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"].HasMember("tagged_key"), false, "compare_tx_to_json_tx: txout target wrong type");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["tagged_key"].IsObject(), false, "compare_tx_to_json_tx: txout target tagged key not object");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["tagged_key"].HasMember("key"), false, "compare_tx_to_json_tx: txout target tagged key does not have onetime-address");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["tagged_key"]["key"].IsString(), false, "compare_tx_to_json_tx: txout target tagged key onetime-address not string");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["tagged_key"]["key"].GetString() == pod_to_hex(k.key), false, "compare_tx_to_json_tx: txout target tagged key wrong onetime-address");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["tagged_key"].HasMember("view_tag"), false, "compare_tx_to_json_tx: txout target tagged key does not have view tag");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["tagged_key"]["view_tag"].IsString(), false, "compare_tx_to_json_tx: txout target tagged key view_tag not string");
|
||||
CHECK_AND_ASSERT_MES(tx_document["vout"][i]["target"]["tagged_key"]["view_tag"].GetString() == pod_to_hex(k.view_tag), false, "compare_tx_to_json_tx: txout target tagged key wrong view_tag");
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(false, false, "compare_tx_to_json_tx: can't check non to_key and to_tagged_key outputs");
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
// signatures
|
||||
CHECK_AND_ASSERT_MES(tx_document.HasMember("signatures"), false, "compare_tx_to_json_tx: missing signatures");
|
||||
CHECK_AND_ASSERT_MES(tx_document["signatures"].IsArray(), false, "compare_tx_to_json_tx: signatures is not array");
|
||||
|
||||
if (!cryptonote::is_coinbase(tx))
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(tx_document["signatures"].Size() == tx.signatures.size(), false, "compare_tx_to_json_tx: signatures wrong size");
|
||||
for (size_t i = 0; i < tx.signatures.size(); ++i)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(tx_document["signatures"][i].IsString(), false, "compare_tx_to_json_tx: signatures element not string");
|
||||
const std::string sig_hexbuffer{epee::to_hex::string({reinterpret_cast<const unsigned char*>(tx.signatures[i].data()), tx.signatures[i].size() * sizeof(crypto::signature)})};
|
||||
CHECK_AND_ASSERT_MES(tx_document["signatures"][i].GetString() == sig_hexbuffer, false, "compare_tx_to_json_tx: signatures element wrong");
|
||||
}
|
||||
}
|
||||
else // coinbase
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(tx_document["signatures"].Size() == 0, false, "compare_tx_to_json_tx: coinbase signatures not size 0");
|
||||
}
|
||||
}
|
||||
else // v2 tx
|
||||
{
|
||||
// rct_signatures
|
||||
CHECK_AND_ASSERT_MES(tx_document.HasMember("rct_signatures"), false, "compare_tx_to_json_tx: missing rct_signatures");
|
||||
const rapidjson::Value &jrv = tx_document["rct_signatures"];
|
||||
CHECK_AND_ASSERT_MES(jrv.IsObject(), false, "compare_tx_to_json_tx: rct_signatures is not object");
|
||||
|
||||
// type
|
||||
CHECK_AND_ASSERT_MES(jrv.HasMember("type"), false, "compare_tx_to_json_tx: rct_signatures has no type");
|
||||
CHECK_AND_ASSERT_MES(jrv["type"].IsUint(), false, "compare_tx_to_json_tx: rct_signatures type is not int");
|
||||
CHECK_AND_ASSERT_MES(jrv["type"].GetUint() == tx.rct_signatures.type, false, "compare_tx_to_json_tx: rct_signatures wrong type");
|
||||
|
||||
if (!cryptonote::is_coinbase(tx))
|
||||
{
|
||||
// txnFee
|
||||
CHECK_AND_ASSERT_MES(jrv.HasMember("txnFee"), false, "compare_tx_to_json_tx: rct_signatures has no fee");
|
||||
CHECK_AND_ASSERT_MES(jrv["txnFee"].IsUint64(), false, "compare_tx_to_json_tx: rct_signatures type is not uint64");
|
||||
CHECK_AND_ASSERT_MES(jrv["txnFee"].GetUint64() == tx.rct_signatures.txnFee, false, "compare_tx_to_json_tx: rct_signatures wrong fee");
|
||||
|
||||
// outPk
|
||||
CHECK_AND_ASSERT_MES(jrv.HasMember("outPk"), false, "compare_tx_to_json_tx: rct_signatures has no outPk");
|
||||
CHECK_AND_ASSERT_MES(jrv["outPk"].IsArray(), false, "compare_tx_to_json_tx: rct_signatures outPk is not array");
|
||||
CHECK_AND_ASSERT_MES(jrv["outPk"].Size() == tx.rct_signatures.outPk.size(), false, "compare_tx_to_json_tx: rct_signatures wrong outPk size");
|
||||
for (size_t i = 0; i < tx.rct_signatures.outPk.size(); ++i)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jrv["outPk"][i].IsString(), false, "compare_tx_to_json_tx: rct_signatures outPk entry not string");
|
||||
CHECK_AND_ASSERT_MES(jrv["outPk"][i].GetString() == pod_to_hex(tx.rct_signatures.outPk[i].mask), false, "compare_tx_to_json_tx: rct_signatures outPk wrong mask");
|
||||
}
|
||||
|
||||
// ecdhInfo
|
||||
const bool short_ecdh_info = tx.rct_signatures.type >= rct::RCTTypeBulletproof2;
|
||||
const size_t ecdh_amount_hexlength = 2 * (short_ecdh_info ? 8 : 32);
|
||||
CHECK_AND_ASSERT_MES(jrv.HasMember("ecdhInfo"), false, "compare_tx_to_json_tx: rct_signatures has no ecdhInfo");
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"].IsArray(), false, "compare_tx_to_json_tx: rct_signatures ecdhInfo is not array");
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"].Size() == tx.rct_signatures.ecdhInfo.size(), false, "compare_tx_to_json_tx: rct_signatures wrong ecdhInfo size");
|
||||
for (size_t i = 0; i < tx.rct_signatures.ecdhInfo.size(); ++i)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"][i].IsObject(), false, "compare_tx_to_json_tx: rct_signatures ecdhInfo entry not object");
|
||||
// amount (encrypted)
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"][i].HasMember("amount"), false, "compare_tx_to_json_tx: rct_signatures ecdhInfo entry missing amount");
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"][i]["amount"].IsString(), false, "compare_tx_to_json_tx: rct_signatures ecdhInfo entry amount info not string");
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"][i]["amount"].GetStringLength() == ecdh_amount_hexlength, false, "compare_tx_to_json_tx: rct_signatures ecdhInfo entry amount string wrong size");
|
||||
const auto hex_amount{pod_to_hex(tx.rct_signatures.ecdhInfo[i].amount).substr(0, ecdh_amount_hexlength)};
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"][i]["amount"].GetString() == hex_amount, false, "compare_tx_to_json_tx: rct_signatures ecdhInfo entry wrong amount");
|
||||
if (!short_ecdh_info)
|
||||
{
|
||||
// mask (encrypted blinding factor)
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"][i].HasMember("mask"), false, "compare_tx_to_json_tx: rct_signatures ecdhInfo entry missing mask");
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"][i]["mask"].IsString(), false, "compare_tx_to_json_tx: rct_signatures ecdhInfo entry mask not string");
|
||||
CHECK_AND_ASSERT_MES(jrv["ecdhInfo"][i]["mask"].GetString() == pod_to_hex(tx.rct_signatures.ecdhInfo[i].mask), false, "compare_tx_to_json_tx: rct_signatures ecdhInfo entry wrong mask");
|
||||
}
|
||||
}
|
||||
|
||||
// pseudoOuts (not pruned w/ v2 RingCT)
|
||||
if (tx.rct_signatures.type == rct::RCTTypeSimple)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(tx.rct_signatures.pseudoOuts.size() == tx.vin.size(), false, "sanity check pseudoOuts size");
|
||||
CHECK_AND_ASSERT_MES(jrv.HasMember("pseudoOuts"), false, "compare_tx_to_json_tx: rct_signatures missing pseudoOuts");
|
||||
CHECK_AND_ASSERT_MES(jrv["pseudoOuts"].IsArray(), false, "compare_tx_to_json_tx: rct_signatures pseudoOuts is not array");
|
||||
CHECK_AND_ASSERT_MES(jrv["pseudoOuts"].Size() == tx.rct_signatures.pseudoOuts.size(), false, "compare_tx_to_json_tx: rct_signatures pseudoOuts wrong size");
|
||||
for (size_t i = 0; i < tx.rct_signatures.pseudoOuts.size(); ++i)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jrv["pseudoOuts"][i].IsString(), false, "compare_tx_to_json_tx: rct_signatures pseudoOuts entry not string");
|
||||
CHECK_AND_ASSERT_MES(jrv["pseudoOuts"][i].GetString() == pod_to_hex(tx.rct_signatures.pseudoOuts[i]), false, "compare_tx_to_json_tx: rct_signatures pseudoOuts wrong entry");
|
||||
}
|
||||
}
|
||||
|
||||
// rctsig_prunable
|
||||
CHECK_AND_ASSERT_MES(tx_document.HasMember("rctsig_prunable"), false, "compare_tx_to_json_tx: rct_signatures missing rctsig_prunable");
|
||||
const rapidjson::Value &jrvp = tx_document["rctsig_prunable"];
|
||||
const rct::rctSigPrunable &rvp = tx.rct_signatures.p;
|
||||
|
||||
// range proofs
|
||||
switch (tx.rct_signatures.type)
|
||||
{
|
||||
case rct::RCTTypeFull:
|
||||
case rct::RCTTypeSimple:
|
||||
// boro sig
|
||||
CHECK_AND_ASSERT_MES(rvp.rangeSigs.size() == tx.vout.size(), false, "sanity check rangeSigs length");
|
||||
CHECK_AND_ASSERT_MES(jrvp.HasMember("rangeSigs"), false, "compare_tx_to_json_tx: rctsig_prunable missing rangeSigs");
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"].IsArray(), false, "compare_tx_to_json_tx: rctsig_prunable rangeSigs not array");
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"].Size() == rvp.rangeSigs.size(), false, "compare_tx_to_json_tx: rctsig_prunable rangeSigs wrong size");
|
||||
for (size_t i = 0; i < rvp.rangeSigs.size(); ++i)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"][i].IsObject(), false, "compare_tx_to_json_tx: rctsig_prunable rangeSig not object");
|
||||
// asig
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"][i].HasMember("asig"), false, "compare_tx_to_json_tx: rctsig_prunable rangeSig missing asig");
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"][i]["asig"].IsString(), false, "compare_tx_to_json_tx: rctsig_prunable rangeSig asig is not string");
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"][i]["asig"].GetString() == pod_to_hex(rvp.rangeSigs[i].asig), false, "compare_tx_to_json_tx: rctsig_prunable rangeSig wrong asig");
|
||||
// Ci
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"][i].HasMember("Ci"), false, "compare_tx_to_json_tx: rctsig_prunable rangeSig missing Ci");
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"][i]["Ci"].IsString(), false, "compare_tx_to_json_tx: rctsig_prunable rangeSig Ci is not string");
|
||||
CHECK_AND_ASSERT_MES(jrvp["rangeSigs"][i]["Ci"].GetString() == pod_to_hex(rvp.rangeSigs[i].Ci), false, "compare_tx_to_json_tx: rctsig_prunable rangeSig wrong Ci");
|
||||
}
|
||||
break;
|
||||
case rct::RCTTypeBulletproof:
|
||||
case rct::RCTTypeBulletproof2:
|
||||
case rct::RCTTypeCLSAG:
|
||||
// bulletproof sig
|
||||
CHECK_AND_ASSERT_MES(rvp.bulletproofs.size(), false, "sanity check bulletproofs length");
|
||||
CHECK_AND_ASSERT_MES(jrvp.HasMember("nbp"), false, "compare_tx_to_json_tx: rctsig_prunable missing nbp");
|
||||
CHECK_AND_ASSERT_MES(jrvp["nbp"].IsUint(), false, "compare_tx_to_json_tx: rctsig_prunable nbp not unsigned int");
|
||||
CHECK_AND_ASSERT_MES(jrvp["nbp"].GetUint() == rvp.bulletproofs.size(), false, "compare_tx_to_json_tx: rctsig_prunable nbp wrong value");
|
||||
CHECK_AND_ASSERT_MES(jrvp.HasMember("bp"), false, "compare_tx_to_json_tx: rctsig_prunable missing bp");
|
||||
CHECK_AND_ASSERT_MES(jrvp["bp"].IsArray(), false, "compare_tx_to_json_tx: rctsig_prunable bp not array");
|
||||
CHECK_AND_ASSERT_MES(jrvp["bp"].Size() == jrvp["nbp"].GetUint(), false, "compare_tx_to_json_tx: rctsig_prunable bp size not equal to nbp");
|
||||
for (size_t i = 0; i < jrvp["nbp"].GetUint(); ++i)
|
||||
{
|
||||
/* A, S, T1, T2, taux, mu, L[], R[], a, b, t */
|
||||
const rapidjson::Value &jbp = jrvp["bp"][i];
|
||||
const rct::Bulletproof &bp = rvp.bulletproofs[i];
|
||||
CHECK_AND_ASSERT_MES(jbp.IsObject(), false, "compare_tx_to_json_tx: bp entry not object");
|
||||
CHECK_AND_ASSERT_MES(jbp.HasMember("A") && jbp.HasMember("S") && jbp.HasMember("T1") &&
|
||||
jbp.HasMember("T2") && jbp.HasMember("taux") && jbp.HasMember("mu") &&
|
||||
jbp.HasMember("L") && jbp.HasMember("R") && jbp.HasMember("a") &&
|
||||
jbp.HasMember("b") && jbp.HasMember("t"), false, "compare_tx_to_json_tx: bp entry missing fields");
|
||||
CHECK_AND_ASSERT_MES(jbp["A"].IsString() && jbp["S"].IsString() &&
|
||||
jbp["T1"].IsString() && jbp["T2"].IsString() && jbp["taux"].IsString() &&
|
||||
jbp["mu"].IsString() && jbp["L"].IsArray() && jbp["R"].IsArray() &&
|
||||
jbp["a"].IsString() && jbp["b"].IsString() && jbp["t"].IsString(), false,
|
||||
"compare_tx_to_json_tx: bp entry fields bad type");
|
||||
CHECK_AND_ASSERT_MES(jbp["A"].GetString() == pod_to_hex(bp.A), false, "compare_tx_to_json_tx: bp entry wrong A");
|
||||
CHECK_AND_ASSERT_MES(jbp["S"].GetString() == pod_to_hex(bp.S), false, "compare_tx_to_json_tx: bp entry wrong S");
|
||||
CHECK_AND_ASSERT_MES(jbp["T1"].GetString() == pod_to_hex(bp.T1), false, "compare_tx_to_json_tx: bp entry wrong T1");
|
||||
CHECK_AND_ASSERT_MES(jbp["T2"].GetString() == pod_to_hex(bp.T2), false, "compare_tx_to_json_tx: bp entry wrong T2");
|
||||
CHECK_AND_ASSERT_MES(jbp["taux"].GetString() == pod_to_hex(bp.taux), false, "compare_tx_to_json_tx: bp entry wrong taux");
|
||||
CHECK_AND_ASSERT_MES(jbp["mu"].GetString() == pod_to_hex(bp.mu), false, "compare_tx_to_json_tx: bp entry wrong mu");
|
||||
CHECK_AND_ASSERT_MES(jbp["a"].GetString() == pod_to_hex(bp.a), false, "compare_tx_to_json_tx: bp entry wrong a");
|
||||
CHECK_AND_ASSERT_MES(jbp["b"].GetString() == pod_to_hex(bp.b), false, "compare_tx_to_json_tx: bp entry wrong b");
|
||||
CHECK_AND_ASSERT_MES(jbp["t"].GetString() == pod_to_hex(bp.t), false, "compare_tx_to_json_tx: bp entry wrong t");
|
||||
CHECK_AND_ASSERT_MES(jbp["L"].Size() == bp.L.size(), false, "compare_tx_to_json_tx: bp entry L wrong size");
|
||||
for (size_t j = 0; j < bp.L.size(); ++j)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jbp["L"][j].IsString(), false, "compare_tx_to_json_tx: bp entry L entry not string");
|
||||
CHECK_AND_ASSERT_MES(jbp["L"][j].GetString() == pod_to_hex(bp.L[j]), false, "compare_tx_to_json_tx: bp entry L entry wrong value");
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(jbp["R"].Size() == bp.R.size(), false, "compare_tx_to_json_tx: bp entry R wrong size");
|
||||
for (size_t j = 0; j < bp.R.size(); ++j)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jbp["R"][j].IsString(), false, "compare_tx_to_json_tx: bp entry R entry not string");
|
||||
CHECK_AND_ASSERT_MES(jbp["R"][j].GetString() == pod_to_hex(bp.R[j]), false, "compare_tx_to_json_tx: bp entry R entry wrong value");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case rct::RCTTypeBulletproofPlus:
|
||||
// bulletproof+ sig
|
||||
CHECK_AND_ASSERT_MES(rvp.bulletproofs_plus.size(), false, "sanity check bulletproofs+ length");
|
||||
CHECK_AND_ASSERT_MES(jrvp.HasMember("nbp"), false, "compare_tx_to_json_tx: rctsig_prunable missing nbp");
|
||||
CHECK_AND_ASSERT_MES(jrvp["nbp"].IsUint(), false, "compare_tx_to_json_tx: rctsig_prunable nbp not unsigned int");
|
||||
CHECK_AND_ASSERT_MES(jrvp["nbp"].GetUint() == rvp.bulletproofs_plus.size(), false, "compare_tx_to_json_tx: rctsig_prunable nbp wrong value");
|
||||
CHECK_AND_ASSERT_MES(jrvp.HasMember("bpp"), false, "compare_tx_to_json_tx: rctsig_prunable missing bp");
|
||||
CHECK_AND_ASSERT_MES(jrvp["bpp"].IsArray(), false, "compare_tx_to_json_tx: rctsig_prunable bp not array");
|
||||
CHECK_AND_ASSERT_MES(jrvp["bpp"].Size() == jrvp["nbp"].GetUint(), false, "compare_tx_to_json_tx: rctsig_prunable bp size not equal to nbp");
|
||||
for (size_t i = 0; i < jrvp["nbp"].GetUint(); ++i)
|
||||
{
|
||||
/* A, A1, B, r1, s1, d1, L[], R[] */
|
||||
const rapidjson::Value &jbpp = jrvp["bpp"][i];
|
||||
const rct::BulletproofPlus &bpp = rvp.bulletproofs_plus[i];
|
||||
CHECK_AND_ASSERT_MES(jbpp.IsObject(), false, "compare_tx_to_json_tx: bpp entry not object");
|
||||
CHECK_AND_ASSERT_MES(jbpp.HasMember("A") && jbpp.HasMember("A1") && jbpp.HasMember("B") &&
|
||||
jbpp.HasMember("r1") && jbpp.HasMember("s1") && jbpp.HasMember("d1") &&
|
||||
jbpp.HasMember("L") && jbpp.HasMember("R"), false, "compare_tx_to_json_tx: bpp entry missing fields");
|
||||
CHECK_AND_ASSERT_MES(jbpp["A"].IsString() && jbpp["A1"].IsString() &&
|
||||
jbpp["B"].IsString() && jbpp["r1"].IsString() && jbpp["s1"].IsString() &&
|
||||
jbpp["d1"].IsString() && jbpp["L"].IsArray() && jbpp["R"].IsArray(), false,
|
||||
"compare_tx_to_json_tx: bpp entry fields bad type");
|
||||
CHECK_AND_ASSERT_MES(jbpp["A"].GetString() == pod_to_hex(bpp.A), false, "compare_tx_to_json_tx: bpp entry wrong A");
|
||||
CHECK_AND_ASSERT_MES(jbpp["A1"].GetString() == pod_to_hex(bpp.A1), false, "compare_tx_to_json_tx: bpp entry wrong S");
|
||||
CHECK_AND_ASSERT_MES(jbpp["B"].GetString() == pod_to_hex(bpp.B), false, "compare_tx_to_json_tx: bpp entry wrong T1");
|
||||
CHECK_AND_ASSERT_MES(jbpp["r1"].GetString() == pod_to_hex(bpp.r1), false, "compare_tx_to_json_tx: bpp entry wrong T2");
|
||||
CHECK_AND_ASSERT_MES(jbpp["s1"].GetString() == pod_to_hex(bpp.s1), false, "compare_tx_to_json_tx: bpp entry wrong taux");
|
||||
CHECK_AND_ASSERT_MES(jbpp["d1"].GetString() == pod_to_hex(bpp.d1), false, "compare_tx_to_json_tx: bpp entry wrong mu");
|
||||
CHECK_AND_ASSERT_MES(jbpp["L"].Size() == bpp.L.size(), false, "compare_tx_to_json_tx: bpp entry L wrong size");
|
||||
for (size_t j = 0; j < bpp.L.size(); ++j)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jbpp["L"][j].IsString(), false, "compare_tx_to_json_tx: bp entry L entry not string");
|
||||
CHECK_AND_ASSERT_MES(jbpp["L"][j].GetString() == pod_to_hex(bpp.L[j]), false, "compare_tx_to_json_tx: bpp entry L entry wrong value");
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(jbpp["R"].Size() == bpp.R.size(), false, "compare_tx_to_json_tx: bpp entry R wrong size");
|
||||
for (size_t j = 0; j < bpp.R.size(); ++j)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jbpp["R"][j].IsString(), false, "compare_tx_to_json_tx: bpp entry R entry not string");
|
||||
CHECK_AND_ASSERT_MES(jbpp["R"][j].GetString() == pod_to_hex(bpp.R[j]), false, "compare_tx_to_json_tx: bpp entry R entry wrong value");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MERROR("Unrecognized RCT Type");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ring signatures
|
||||
const bool is_full_rct = tx.rct_signatures.type == rct::RCTTypeFull;
|
||||
const size_t num_mgs = is_full_rct ? 1 : num_inputs;
|
||||
const size_t num_mg_cols = 1 + (is_full_rct ? num_inputs : 1);
|
||||
switch (tx.rct_signatures.type)
|
||||
{
|
||||
case rct::RCTTypeFull:
|
||||
case rct::RCTTypeSimple:
|
||||
case rct::RCTTypeBulletproof:
|
||||
case rct::RCTTypeBulletproof2:
|
||||
// MLSAG sig
|
||||
CHECK_AND_ASSERT_MES(rvp.MGs.size() == num_mgs, false, "sanity check tx has correct num MGs");
|
||||
CHECK_AND_ASSERT_MES(jrvp.HasMember("MGs"), false, "compare_tx_to_json_tx: rctsig_prunable missing MGs");
|
||||
CHECK_AND_ASSERT_MES(jrvp["MGs"].IsArray(), false, "compare_tx_to_json_tx: rctsig_prunable MGs not array");
|
||||
CHECK_AND_ASSERT_MES(jrvp["MGs"].Size() == num_mgs, false, "compare_tx_to_json_tx: rctsig_prunable MGs wrong size");
|
||||
for (size_t i = 0; i < num_mgs; ++i)
|
||||
{
|
||||
const rapidjson::Value &jmg = jrvp["MGs"][i];
|
||||
const rct::mgSig &mg = rvp.MGs[i];
|
||||
CHECK_AND_ASSERT_MES(jmg.IsObject(), false, "compare_tx_to_json_tx: rctsig_prunable MGs entry not object");
|
||||
CHECK_AND_ASSERT_MES(jmg.HasMember("ss") && jmg.HasMember("cc"), false, "compare_tx_to_json_tx: rctsig_prunable MGs entry missing fields");
|
||||
CHECK_AND_ASSERT_MES(jmg["cc"].IsString(), false, "compare_tx_to_json_tx: rctsig_prunable MGs cc not string");
|
||||
CHECK_AND_ASSERT_MES(jmg["cc"].GetString() == pod_to_hex(mg.cc), false, "compare_tx_to_json_tx: rctsig_prunable MGs cc wrong value");
|
||||
CHECK_AND_ASSERT_MES(jmg["ss"].IsArray(), false, "compare_tx_to_json_tx: rctsig_prunable MG ss not array");
|
||||
CHECK_AND_ASSERT_MES(jmg["ss"].Size() == ring_size, false, "compare_tx_to_json_tx: rctsig_prunable MG ss num rows not ring size");
|
||||
for (size_t j = 0; j < ring_size; ++j)
|
||||
{
|
||||
const rapidjson::Value &jrow = jmg["ss"][j];
|
||||
const rct::keyV &row = mg.ss[j];
|
||||
CHECK_AND_ASSERT_MES(jrow.IsArray(), false, "compare_tx_to_json_tx: rctsig_prunable MG ss row not array");
|
||||
CHECK_AND_ASSERT_MES(row.size() == num_mg_cols, false, "sanity check MG ss row size");
|
||||
CHECK_AND_ASSERT_MES(jrow.Size() == num_mg_cols, false, "compare_tx_to_json_tx: rctsig_prunable MG ss row wrong size");
|
||||
for (size_t k = 0; k < num_mg_cols; ++k)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jrow[k].IsString(), false, "compare_tx_to_json_tx: rctsig_prunable MG ss scalar not string");
|
||||
CHECK_AND_ASSERT_MES(jrow[k].GetString() == pod_to_hex(row[k]), false, "compare_tx_to_json_tx: rctsig_prunable MG ss wrong scalar");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case rct::RCTTypeCLSAG:
|
||||
case rct::RCTTypeBulletproofPlus:
|
||||
// CLSAG sig
|
||||
CHECK_AND_ASSERT_MES(rvp.CLSAGs.size() == num_inputs, false, "sanity check tx has correct num CLSAGs");
|
||||
CHECK_AND_ASSERT_MES(jrvp.HasMember("CLSAGs"), false, "compare_tx_to_json_tx: rctsig_prunable missing CLSAGs");
|
||||
CHECK_AND_ASSERT_MES(jrvp["CLSAGs"].IsArray(), false, "compare_tx_to_json_tx: rctsig_prunable CLSAGs not array");
|
||||
CHECK_AND_ASSERT_MES(jrvp["CLSAGs"].Size() == num_inputs, false, "compare_tx_to_json_tx: rctsig_prunable CLSAGs wrong size");
|
||||
for (size_t i = 0; i < num_inputs; ++i)
|
||||
{
|
||||
const rapidjson::Value &jclsag = jrvp["CLSAGs"][i];
|
||||
const rct::clsag &clsag = rvp.CLSAGs[i];
|
||||
CHECK_AND_ASSERT_MES(jclsag.IsObject(), false, "compare_tx_to_json_tx: rctsig_prunable CLSAG is not object");
|
||||
CHECK_AND_ASSERT_MES(jclsag.HasMember("s") && jclsag.HasMember("c1") && jclsag.HasMember("D"),
|
||||
false, "compare_tx_to_json_tx: rctsig_prunable CLSAG missing fields");
|
||||
CHECK_AND_ASSERT_MES(jclsag["s"].IsArray() && jclsag["c1"].IsString() && jclsag["D"].IsString(),
|
||||
false, "compare_tx_to_json_tx: rctsig_prunable CLSAG fields wrong type");
|
||||
CHECK_AND_ASSERT_MES(jclsag["c1"].GetString() == pod_to_hex(clsag.c1),
|
||||
false, "compare_tx_to_json_tx: rctsig_prunable CLSAG wrong c1");
|
||||
CHECK_AND_ASSERT_MES(jclsag["D"].GetString() == pod_to_hex(clsag.D),
|
||||
false, "compare_tx_to_json_tx: rctsig_prunable CLSAG wrong D");
|
||||
CHECK_AND_ASSERT_MES(jclsag["s"].Size() == ring_size,
|
||||
false, "compare_tx_to_json_tx: rctsig_prunable CLSAG.s wrong size");
|
||||
CHECK_AND_ASSERT_MES(clsag.s.size() == ring_size, false, "sanity check CLSAG.s size");
|
||||
for (size_t j = 0; j < ring_size; ++j)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(jclsag["s"][j].IsString(), false, "compare_tx_to_json_tx: rctsig_prunable CLSAG c1 not string");
|
||||
CHECK_AND_ASSERT_MES(jclsag["s"][j].GetString() == pod_to_hex(clsag.s[j]),
|
||||
false, "compare_tx_to_json_tx: rctsig_prunable CLSAG.s wrong key element");
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MERROR("Unrecognized RCT Type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jsonify_tx_from_file_and_parse_compare(const std::string &tx_fname)
|
||||
{
|
||||
const boost::filesystem::path tx_path = unit_test::data_dir / "txs" / tx_fname;
|
||||
|
||||
std::string tx_blob;
|
||||
CHECK_AND_ASSERT_THROW_MES(epee::file_io_utils::load_file_to_string(tx_path.string(), tx_blob),
|
||||
"failed to load tx from file: " << tx_path.string());
|
||||
|
||||
cryptonote::transaction tx;
|
||||
CHECK_AND_ASSERT_THROW_MES(cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx),
|
||||
"failed to parse tx from file: " << tx_path.string());
|
||||
|
||||
// Same JSONification used in /get_transactions RPC endpoint
|
||||
const std::string tx_json{cryptonote::obj_to_json_str(tx)};
|
||||
|
||||
return compare_tx_to_json_tx(tx, tx_json);
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
TEST(JsonSerialization, VectorBytes)
|
||||
@ -247,3 +676,16 @@ TEST(JsonRpcSerialization, HandlerFromJson)
|
||||
cryptonote::rpc::GetHashesFast::Request request{};
|
||||
EXPECT_THROW(request.fromJson(req_full.getMessage()), cryptonote::json::WRONG_TYPE);
|
||||
}
|
||||
|
||||
TEST(JsonSerialization, jsonify_tx_from_file_and_parse_compare)
|
||||
{
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("v1_coinbase_tx_bf4c0300.bin"));
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("v1_tx_hf3_effcceb9.bin"));
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("v2_coinbase_tx_7f88a52a.bin"));
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("rct_full_tx_14056427.bin"));
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("rct_simple_tx_c69861bf.bin"));
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("rct_bp_tx_a685d68e.bin"));
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("rct_bp_compact_tx_10312fd4.bin"));
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("rct_clsag_tx_200c3215.bin"));
|
||||
EXPECT_TRUE(jsonify_tx_from_file_and_parse_compare("bpp_tx_e89415.bin"));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user