unit test for btc tx serialization

This commit is contained in:
gladcow 2020-01-24 14:15:16 +03:00
parent 866e9b6158
commit 1f505d9db9
3 changed files with 111 additions and 81 deletions

View file

@ -169,88 +169,59 @@ bytes signature_for_raw_transaction(const bytes& unsigned_tx, const fc::ecc::pri
return bytes(res.begin(), res.begin() + res.size());
}
struct btc_outpoint
bytes btc_tx::to_bytes() const
{
fc::uint256 hash;
uint32_t n;
};
struct btc_in
{
btc_outpoint prevout;
bytes scriptSig;
uint32_t nSequence;
bytes scriptWitness;
};
struct btc_out
{
int64_t nValue;
bytes scriptPubKey;
};
struct btc_tx
{
std::vector<btc_in> vin;
std::vector<btc_out> vout;
int32_t nVersion;
uint32_t nLockTime;
bool hasWitness;
bytes to_bytes() const
bytes res;
WriteBytesStream str(res);
str.add(nVersion);
if(hasWitness)
{
bytes res;
WriteBytesStream str(res);
str.add(nVersion);
if(hasWitness)
{
std::vector<btc_in> dummy;
fc::raw::pack(str, dummy);
unsigned char flags = 1;
str.put(flags);
}
fc::raw::pack(str, vin);
fc::raw::pack(str, vout);
if(hasWitness)
{
for(const auto& in: vin)
fc::raw::pack(str, in.scriptWitness);
}
str.add(nLockTime);
return res;
std::vector<btc_in> dummy;
fc::raw::pack(str, dummy);
unsigned char flags = 1;
str.put(flags);
}
void fill_from_bytes(const bytes& data)
fc::raw::pack(str, vin);
fc::raw::pack(str, vout);
if(hasWitness)
{
ReadBytesStream ds( data );
ds.direct_read(nVersion);
unsigned char flags = 0;
vin.clear();
vout.clear();
hasWitness = false;
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
fc::raw::unpack(ds, vin);
if (vin.size() == 0) {
/* We read a dummy or an empty vin. */
ds.get(flags);
if (flags != 0) {
fc::raw::unpack(ds, vin);
fc::raw::unpack(ds, vout);
hasWitness = true;
}
} else {
/* We read a non-empty vin. Assume a normal vout follows. */
fc::raw::unpack(ds, vout);
}
if (hasWitness) {
/* The witness flag is present, and we support witnesses. */
for (size_t i = 0; i < vin.size(); i++) {
fc::raw::unpack(ds, vin[i].scriptWitness);
}
}
ds.direct_read(nLockTime);
for(const auto& in: vin)
fc::raw::pack(str, in.scriptWitness);
}
};
str.add(nLockTime);
return res;
}
void btc_tx::fill_from_bytes(const bytes& data)
{
ReadBytesStream ds( data );
ds.direct_read(nVersion);
unsigned char flags = 0;
vin.clear();
vout.clear();
hasWitness = false;
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
fc::raw::unpack(ds, vin);
if (vin.size() == 0) {
/* We read a dummy or an empty vin. */
ds.get(flags);
if (flags != 0) {
fc::raw::unpack(ds, vin);
fc::raw::unpack(ds, vout);
hasWitness = true;
}
} else {
/* We read a non-empty vin. Assume a normal vout follows. */
fc::raw::unpack(ds, vout);
}
if (hasWitness) {
/* The witness flag is present, and we support witnesses. */
for (size_t i = 0; i < vin.size(); i++) {
fc::raw::unpack(ds, vin[i].scriptWitness);
}
}
ds.direct_read(nLockTime);
}
bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, const bytes& redeem_script, const std::vector<fc::optional<fc::ecc::private_key> > &priv_keys)
{
@ -278,7 +249,3 @@ bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, const bytes& redeem
}
}}
FC_REFLECT(graphene::peerplays_sidechain::btc_outpoint, (hash)(n))
FC_REFLECT(graphene::peerplays_sidechain::btc_in, (prevout)(scriptSig)(nSequence))
FC_REFLECT(graphene::peerplays_sidechain::btc_out, (nValue)(scriptPubKey))

View file

@ -18,4 +18,42 @@ std::string p2sh_address_from_redeem_script(const bytes& script, bitcoin_network
*/
bytes sign_pw_transfer_transaction(const bytes& unsigned_tx, const bytes& redeem_script, const std::vector<fc::optional<fc::ecc::private_key>>& priv_keys);
struct btc_outpoint
{
fc::uint256 hash;
uint32_t n;
};
struct btc_in
{
btc_outpoint prevout;
bytes scriptSig;
uint32_t nSequence;
bytes scriptWitness;
};
struct btc_out
{
int64_t nValue;
bytes scriptPubKey;
};
struct btc_tx
{
std::vector<btc_in> vin;
std::vector<btc_out> vout;
int32_t nVersion;
uint32_t nLockTime;
bool hasWitness;
bytes to_bytes() const;
void fill_from_bytes(const bytes& data);
};
}}
FC_REFLECT(graphene::peerplays_sidechain::btc_outpoint, (hash)(n))
// btc_in::scriptWitness is filled only in transaction serialization
FC_REFLECT(graphene::peerplays_sidechain::btc_in, (prevout)(scriptSig)(nSequence))
FC_REFLECT(graphene::peerplays_sidechain::btc_out, (nValue)(scriptPubKey))
// btc_tx is serialized manually

View file

@ -0,0 +1,25 @@
#include <boost/test/unit_test.hpp>
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
#include <fc/crypto/hex.hpp>
using namespace graphene::peerplays_sidechain;
BOOST_AUTO_TEST_CASE(tx_serialization)
{
// use real mainnet transaction
// txid: 6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315
// json: {"txid":"6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315","hash":"6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315","version":1,"size":224,"vsize":224,"weight":896,"locktime":0,"vin":[{"txid":"55d079ca797fee81416b71b373abedd8722e33c9f73177be0166b5d5fdac478b","vout":0,"scriptSig":{"asm":"3045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253[ALL] 02be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4","hex":"483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4"},"sequence":4294967295}],"vout":[{"value":1.26491535,"n":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 95783804d28e528fbc4b48c7700471e6845804eb OP_EQUALVERIFY OP_CHECKSIG","hex":"76a91495783804d28e528fbc4b48c7700471e6845804eb88ac","reqSigs":1,"type":"pubkeyhash","addresses":["1EdKhXv7zjGowPzgDQ4z1wa2ukVrXRXXkP"]}},{"value":0.0002,"n":1,"scriptPubKey":{"asm":"OP_HASH160 fb0670971091da8248b5c900c6515727a20e8662 OP_EQUAL","hex":"a914fb0670971091da8248b5c900c6515727a20e866287","reqSigs":1,"type":"scripthash","addresses":["3QaKF8zobqcqY8aS6nxCD5ZYdiRfL3RCmU"]}}]}
// hex: "01000000018b47acfdd5b56601be7731f7c9332e72d8edab73b3716b4181ee7f79ca79d055000000006b483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4ffffffff028f1b8a07000000001976a91495783804d28e528fbc4b48c7700471e6845804eb88ac204e00000000000017a914fb0670971091da8248b5c900c6515727a20e86628700000000"
fc::string strtx("01000000018b47acfdd5b56601be7731f7c9332e72d8edab73b3716b4181ee7f79ca79d055000000006b483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4ffffffff028f1b8a07000000001976a91495783804d28e528fbc4b48c7700471e6845804eb88ac204e00000000000017a914fb0670971091da8248b5c900c6515727a20e86628700000000");
bytes bintx;
bintx.resize(strtx.length() / 2);
fc::from_hex(strtx, reinterpret_cast<char*>(&bintx[0]), bintx.size());
btc_tx tx;
BOOST_CHECK_NO_THROW(tx.fill_from_bytes(bintx));
BOOST_CHECK(tx.nVersion == 1);
BOOST_CHECK(tx.nLockTime == 0);
BOOST_CHECK(tx.vin.size() == 1);
BOOST_CHECK(tx.vout.size() == 2);
bytes buff = tx.to_bytes();
BOOST_CHECK(bintx == buff);
}