Added mechanism sign bitcoin transaction.
This commit is contained in:
parent
9c7694a5e4
commit
7a6fca9180
9 changed files with 653 additions and 214 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
#include <sidechain/bitcoin_transaction.hpp>
|
#include <sidechain/bitcoin_transaction.hpp>
|
||||||
#include <fc/crypto/base58.hpp>
|
#include <fc/crypto/base58.hpp>
|
||||||
#include <sidechain/bitcoin_script.hpp>
|
#include <sidechain/bitcoin_script.hpp>
|
||||||
|
#include <sidechain/serialize.hpp>
|
||||||
|
|
||||||
namespace sidechain {
|
namespace sidechain {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <sidechain/bitcoin_address.hpp>
|
#include <sidechain/bitcoin_address.hpp>
|
||||||
#include <sidechain/serialize.hpp>
|
|
||||||
|
|
||||||
namespace sidechain {
|
namespace sidechain {
|
||||||
|
|
||||||
|
|
@ -84,161 +83,6 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
inline void pack( Stream& s, const out_point& op )
|
|
||||||
{
|
|
||||||
fc::sha256 reversed( op.hash );
|
|
||||||
std::reverse( reversed.data(), reversed.data() + reversed.data_size() );
|
|
||||||
s.write( reversed.data(), reversed.data_size() );
|
|
||||||
pack( s, op.n );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
inline void unpack( Stream& s, out_point& op )
|
|
||||||
{
|
|
||||||
uint64_t hash_size = op.hash.data_size();
|
|
||||||
std::unique_ptr<char[]> hash_data( new char[hash_size] );
|
|
||||||
s.read( hash_data.get(), hash_size );
|
|
||||||
std::reverse( hash_data.get(), hash_data.get() + hash_size );
|
|
||||||
|
|
||||||
op.hash = fc::sha256( hash_data.get(), hash_size );
|
|
||||||
unpack( s, op.n );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
inline void pack( Stream& s, const tx_in& in )
|
|
||||||
{
|
|
||||||
pack( s, in.prevout );
|
|
||||||
pack( s, in.scriptSig );
|
|
||||||
pack( s, in.nSequence );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
inline void unpack( Stream& s, tx_in& in )
|
|
||||||
{
|
|
||||||
unpack( s, in.prevout );
|
|
||||||
unpack( s, in.scriptSig );
|
|
||||||
unpack( s, in.nSequence );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
inline void pack( Stream& s, const tx_out& out )
|
|
||||||
{
|
|
||||||
pack( s, out.value );
|
|
||||||
pack( s, out.scriptPubKey );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
inline void unpack( Stream& s, tx_out& out )
|
|
||||||
{
|
|
||||||
unpack( s, out.value );
|
|
||||||
unpack( s, out.scriptPubKey );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
inline void pack( Stream& s, const bitcoin_transaction& tx, bool with_witness = true )
|
|
||||||
{
|
|
||||||
uint8_t flags = 0;
|
|
||||||
|
|
||||||
if ( with_witness ) {
|
|
||||||
for ( const auto& in : tx.vin ) {
|
|
||||||
if ( !in.scriptWitness.empty() ) {
|
|
||||||
flags |= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pack( s, tx.nVersion );
|
|
||||||
|
|
||||||
if ( flags ) {
|
|
||||||
pack_compact_size( s, 0 );
|
|
||||||
pack( s, flags );
|
|
||||||
}
|
|
||||||
|
|
||||||
pack_compact_size( s, tx.vin.size() );
|
|
||||||
for ( const auto& in : tx.vin )
|
|
||||||
pack( s, in );
|
|
||||||
|
|
||||||
pack_compact_size( s, tx.vout.size() );
|
|
||||||
for ( const auto& out : tx.vout )
|
|
||||||
pack( s, out );
|
|
||||||
|
|
||||||
if ( flags & 1 ) {
|
|
||||||
for ( const auto in : tx.vin ) {
|
|
||||||
pack_compact_size( s, in.scriptWitness.size() );
|
|
||||||
for ( const auto& sc : in.scriptWitness )
|
|
||||||
pack( s, sc );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pack( s, tx.nLockTime );
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Stream>
|
|
||||||
inline void unpack( Stream& s, bitcoin_transaction& tx ) {
|
|
||||||
uint8_t flags = 0;
|
|
||||||
|
|
||||||
unpack( s, tx.nVersion );
|
|
||||||
|
|
||||||
auto vin_size = unpack_compact_size( s );
|
|
||||||
if ( vin_size == 0 ) {
|
|
||||||
unpack( s, flags );
|
|
||||||
vin_size = unpack_compact_size( s );
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.vin.reserve( vin_size );
|
|
||||||
for ( size_t i = 0; i < vin_size; i++ ) {
|
|
||||||
tx_in in;
|
|
||||||
unpack( s, in );
|
|
||||||
tx.vin.push_back( in );
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto vout_size = unpack_compact_size( s );
|
|
||||||
tx.vout.reserve( vout_size );
|
|
||||||
for ( size_t i = 0; i < vout_size; i++ ) {
|
|
||||||
tx_out out;
|
|
||||||
unpack( s, out );
|
|
||||||
tx.vout.push_back( out );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( flags & 1 ) {
|
|
||||||
for ( auto& in : tx.vin ) {
|
|
||||||
uint64_t stack_size = unpack_compact_size( s );
|
|
||||||
in.scriptWitness.reserve( stack_size );
|
|
||||||
for ( uint64_t i = 0; i < stack_size; i++ ) {
|
|
||||||
std::vector<char> script;
|
|
||||||
unpack( s, script );
|
|
||||||
in.scriptWitness.push_back( script );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unpack( s, tx.nLockTime );
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<char> pack( const bitcoin_transaction& v, bool with_witness = true ) {
|
|
||||||
fc::datastream<size_t> ps;
|
|
||||||
pack( ps, v, with_witness );
|
|
||||||
std::vector<char> vec( ps.tellp() );
|
|
||||||
|
|
||||||
if( !vec.empty() ) {
|
|
||||||
fc::datastream<char*> ds( vec.data(), size_t(vec.size()) );
|
|
||||||
pack( ds, v, with_witness );
|
|
||||||
}
|
|
||||||
return vec;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bitcoin_transaction unpack( const std::vector<char>& s )
|
|
||||||
{ try {
|
|
||||||
bitcoin_transaction tmp;
|
|
||||||
if( !s.empty() ) {
|
|
||||||
fc::datastream<const char*> ds( s.data(), size_t(s.size()) );
|
|
||||||
unpack(ds, tmp);
|
|
||||||
}
|
|
||||||
return tmp;
|
|
||||||
} FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type","transaction" ) ) }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FC_REFLECT( sidechain::out_point, (hash)(n) )
|
FC_REFLECT( sidechain::out_point, (hash)(n) )
|
||||||
|
|
|
||||||
|
|
@ -1,104 +1,360 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <sidechain/types.hpp>
|
#include <sidechain/types.hpp>
|
||||||
|
#include <sidechain/bitcoin_transaction.hpp>
|
||||||
|
|
||||||
namespace sidechain {
|
namespace sidechain {
|
||||||
|
|
||||||
inline void write_compact_size(bytes& vec, size_t size) {
|
inline void write_compact_size( bytes& vec, size_t size )
|
||||||
|
{
|
||||||
bytes sb;
|
bytes sb;
|
||||||
sb.reserve(2);
|
sb.reserve( 2 );
|
||||||
if (size < 253) {
|
if ( size < 253 ) {
|
||||||
sb.insert(sb.end(), static_cast<uint8_t>(size));
|
sb.insert( sb.end(), static_cast<uint8_t>( size ) );
|
||||||
} else if (size <= std::numeric_limits<unsigned short>::max()) {
|
} else if ( size <= std::numeric_limits<unsigned short>::max() ) {
|
||||||
uint16_t tmp = htole16(static_cast<uint16_t>(size));
|
uint16_t tmp = htole16( static_cast<uint16_t>( size ) );
|
||||||
sb.insert(sb.end(), static_cast<uint8_t>(253));
|
sb.insert( sb.end(), static_cast<uint8_t>( 253 ) );
|
||||||
sb.insert(sb.end(), reinterpret_cast<const char*>(tmp), reinterpret_cast<const char*>(tmp) + sizeof(tmp));
|
sb.insert( sb.end(), reinterpret_cast<const char*>( tmp ), reinterpret_cast<const char*>( tmp ) + sizeof( tmp ) );
|
||||||
} else if (size <= std::numeric_limits<unsigned int>::max()) {
|
} else if ( size <= std::numeric_limits<unsigned int>::max() ) {
|
||||||
uint32_t tmp = htole32(static_cast<uint32_t>(size));
|
uint32_t tmp = htole32( static_cast<uint32_t>( size ) );
|
||||||
sb.insert(sb.end(), static_cast<uint8_t>(254));
|
sb.insert( sb.end(), static_cast<uint8_t>( 254 ) );
|
||||||
sb.insert(sb.end(), reinterpret_cast<const char*>(tmp), reinterpret_cast<const char*>(tmp) + sizeof(tmp));
|
sb.insert( sb.end(), reinterpret_cast<const char*>( tmp ), reinterpret_cast<const char*>( tmp ) + sizeof( tmp ) );
|
||||||
} else {
|
} else {
|
||||||
uint64_t tmp = htole64(static_cast<uint64_t>(size));
|
uint64_t tmp = htole64( static_cast<uint64_t>( size ) );
|
||||||
sb.insert(sb.end(), static_cast<uint8_t>(255));
|
sb.insert( sb.end(), static_cast<uint8_t>( 255 ) );
|
||||||
sb.insert(sb.end(), reinterpret_cast<const char*>(tmp), reinterpret_cast<const char*>(tmp) + sizeof(tmp));
|
sb.insert( sb.end(), reinterpret_cast<const char*>( tmp ), reinterpret_cast<const char*>( tmp ) + sizeof( tmp ) );
|
||||||
}
|
}
|
||||||
vec.insert(vec.end(), sb.begin(), sb.end());
|
vec.insert( vec.end(), sb.begin(), sb.end() );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
inline void pack_compact_size(Stream& s, size_t size) {
|
inline void pack_compact_size(Stream& s, size_t size)
|
||||||
if (size < 253) {
|
{
|
||||||
fc::raw::pack(s, static_cast<uint8_t>(size));
|
if ( size < 253 ) {
|
||||||
} else if (size <= std::numeric_limits<unsigned short>::max()) {
|
fc::raw::pack( s, static_cast<uint8_t>( size ) );
|
||||||
fc::raw::pack(s, static_cast<uint8_t>(253));
|
} else if ( size <= std::numeric_limits<unsigned short>::max() ) {
|
||||||
fc::raw::pack(s, htole16(static_cast<uint16_t>(size)));
|
fc::raw::pack( s, static_cast<uint8_t>( 253 ) );
|
||||||
} else if (size <= std::numeric_limits<unsigned int>::max()) {
|
fc::raw::pack( s, htole16( static_cast<uint16_t>( size ) ) );
|
||||||
fc::raw::pack(s, static_cast<uint8_t>(254));
|
} else if ( size <= std::numeric_limits<unsigned int>::max() ) {
|
||||||
fc::raw::pack(s, htole32(static_cast<uint32_t>(size)));
|
fc::raw::pack( s, static_cast<uint8_t>( 254 ) );
|
||||||
|
fc::raw::pack( s, htole32(static_cast<uint32_t>( size ) ) );
|
||||||
} else {
|
} else {
|
||||||
fc::raw::pack(s, static_cast<uint8_t>(255));
|
fc::raw::pack( s, static_cast<uint8_t>( 255 ) );
|
||||||
fc::raw::pack(s, htole64(static_cast<uint64_t>(size)));
|
fc::raw::pack( s, htole64( static_cast<uint64_t>( size ) ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
inline uint64_t unpack_compact_size(Stream& s) {
|
inline uint64_t unpack_compact_size( Stream& s )
|
||||||
|
{
|
||||||
uint8_t size;
|
uint8_t size;
|
||||||
uint64_t size_ret;
|
uint64_t size_ret;
|
||||||
|
|
||||||
fc::raw::unpack(s, size);
|
fc::raw::unpack( s, size );
|
||||||
|
|
||||||
if (size < 253) {
|
if (size < 253) {
|
||||||
size_ret = size;
|
size_ret = size;
|
||||||
} else if (size == 253) {
|
} else if ( size == 253 ) {
|
||||||
uint16_t tmp;
|
uint16_t tmp;
|
||||||
fc::raw::unpack(s, tmp);
|
fc::raw::unpack( s, tmp );
|
||||||
size_ret = le16toh(tmp);
|
size_ret = le16toh( tmp );
|
||||||
if (size_ret < 253)
|
if ( size_ret < 253 )
|
||||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
FC_THROW_EXCEPTION( fc::parse_error_exception, "non-canonical unpack_compact_size()" );
|
||||||
} else if (size == 254) {
|
} else if ( size == 254 ) {
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
fc::raw::unpack(s, tmp);
|
fc::raw::unpack( s, tmp );
|
||||||
size_ret = le32toh(tmp);
|
size_ret = le32toh( tmp );
|
||||||
if (size_ret < 0x10000u)
|
if ( size_ret < 0x10000u )
|
||||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
FC_THROW_EXCEPTION( fc::parse_error_exception, "non-canonical unpack_compact_size()" );
|
||||||
} else {
|
} else {
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
fc::raw::unpack(s, tmp);
|
fc::raw::unpack( s, tmp );
|
||||||
size_ret = le64toh(tmp);
|
size_ret = le64toh( tmp );
|
||||||
if (size_ret < 0x100000000ULL)
|
if ( size_ret < 0x100000000ULL )
|
||||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
FC_THROW_EXCEPTION( fc::parse_error_exception, "non-canonical unpack_compact_size()" );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size_ret > 0x08000000)
|
if ( size_ret > 0x08000000 )
|
||||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "unpack_compact_size(): size too large");
|
FC_THROW_EXCEPTION( fc::parse_error_exception, "unpack_compact_size(): size too large" );
|
||||||
|
|
||||||
return size_ret;
|
return size_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
inline void pack(Stream& s, const std::vector<char>& v) {
|
inline void pack( Stream& s, const std::vector<char>& v )
|
||||||
pack_compact_size(s, v.size());
|
{
|
||||||
if (!v.empty())
|
pack_compact_size( s, v.size() );
|
||||||
s.write(v.data(), v.size());
|
if ( !v.empty() )
|
||||||
|
s.write( v.data(), v.size() );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
inline void unpack(Stream& s, std::vector<char>& v) {
|
inline void unpack( Stream& s, std::vector<char>& v )
|
||||||
const auto size = unpack_compact_size(s);
|
{
|
||||||
v.resize(size);
|
const auto size = unpack_compact_size( s );
|
||||||
if (size)
|
v.resize( size );
|
||||||
s.read(v.data(), size);
|
if ( size )
|
||||||
|
s.read( v.data(), size );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Stream, typename T>
|
template<typename Stream, typename T>
|
||||||
inline void pack(Stream& s, const T& val) {
|
inline void pack( Stream& s, const T& val )
|
||||||
fc::raw::pack(s, val);
|
{
|
||||||
|
fc::raw::pack( s, val );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Stream, typename T>
|
template<typename Stream, typename T>
|
||||||
inline void unpack(Stream& s, T& val) {
|
inline void unpack( Stream& s, T& val )
|
||||||
fc::raw::unpack(s, val);
|
{
|
||||||
|
fc::raw::unpack( s, val );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void pack( Stream& s, const out_point& op )
|
||||||
|
{
|
||||||
|
fc::sha256 reversed( op.hash );
|
||||||
|
std::reverse( reversed.data(), reversed.data() + reversed.data_size() );
|
||||||
|
s.write( reversed.data(), reversed.data_size() );
|
||||||
|
pack( s, op.n );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void unpack( Stream& s, out_point& op )
|
||||||
|
{
|
||||||
|
uint64_t hash_size = op.hash.data_size();
|
||||||
|
std::unique_ptr<char[]> hash_data( new char[hash_size] );
|
||||||
|
s.read( hash_data.get(), hash_size );
|
||||||
|
std::reverse( hash_data.get(), hash_data.get() + hash_size );
|
||||||
|
|
||||||
|
op.hash = fc::sha256( hash_data.get(), hash_size );
|
||||||
|
unpack( s, op.n );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void pack( Stream& s, const tx_in& in )
|
||||||
|
{
|
||||||
|
pack( s, in.prevout );
|
||||||
|
pack( s, in.scriptSig );
|
||||||
|
pack( s, in.nSequence );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void unpack( Stream& s, tx_in& in )
|
||||||
|
{
|
||||||
|
unpack( s, in.prevout );
|
||||||
|
unpack( s, in.scriptSig );
|
||||||
|
unpack( s, in.nSequence );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void pack( Stream& s, const tx_out& out )
|
||||||
|
{
|
||||||
|
pack( s, out.value );
|
||||||
|
pack( s, out.scriptPubKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void unpack( Stream& s, tx_out& out )
|
||||||
|
{
|
||||||
|
unpack( s, out.value );
|
||||||
|
unpack( s, out.scriptPubKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void pack( Stream& s, const bitcoin_transaction& tx, bool with_witness = true )
|
||||||
|
{
|
||||||
|
uint8_t flags = 0;
|
||||||
|
|
||||||
|
if ( with_witness ) {
|
||||||
|
for ( const auto& in : tx.vin ) {
|
||||||
|
if ( !in.scriptWitness.empty() ) {
|
||||||
|
flags |= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pack( s, tx.nVersion );
|
||||||
|
|
||||||
|
if ( flags ) {
|
||||||
|
pack_compact_size( s, 0 );
|
||||||
|
pack( s, flags );
|
||||||
|
}
|
||||||
|
|
||||||
|
pack_compact_size( s, tx.vin.size() );
|
||||||
|
for ( const auto& in : tx.vin )
|
||||||
|
pack( s, in );
|
||||||
|
|
||||||
|
pack_compact_size( s, tx.vout.size() );
|
||||||
|
for ( const auto& out : tx.vout )
|
||||||
|
pack( s, out );
|
||||||
|
|
||||||
|
if ( flags & 1 ) {
|
||||||
|
for ( const auto in : tx.vin ) {
|
||||||
|
pack_compact_size( s, in.scriptWitness.size() );
|
||||||
|
for ( const auto& sc : in.scriptWitness )
|
||||||
|
pack( s, sc );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pack( s, tx.nLockTime );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void unpack( Stream& s, bitcoin_transaction& tx )
|
||||||
|
{
|
||||||
|
uint8_t flags = 0;
|
||||||
|
|
||||||
|
unpack( s, tx.nVersion );
|
||||||
|
|
||||||
|
auto vin_size = unpack_compact_size( s );
|
||||||
|
if ( vin_size == 0 ) {
|
||||||
|
unpack( s, flags );
|
||||||
|
vin_size = unpack_compact_size( s );
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.vin.reserve( vin_size );
|
||||||
|
for ( size_t i = 0; i < vin_size; i++ ) {
|
||||||
|
tx_in in;
|
||||||
|
unpack( s, in );
|
||||||
|
tx.vin.push_back( in );
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto vout_size = unpack_compact_size( s );
|
||||||
|
tx.vout.reserve( vout_size );
|
||||||
|
for ( size_t i = 0; i < vout_size; i++ ) {
|
||||||
|
tx_out out;
|
||||||
|
unpack( s, out );
|
||||||
|
tx.vout.push_back( out );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( flags & 1 ) {
|
||||||
|
for ( auto& in : tx.vin ) {
|
||||||
|
uint64_t stack_size = unpack_compact_size( s );
|
||||||
|
in.scriptWitness.reserve( stack_size );
|
||||||
|
for ( uint64_t i = 0; i < stack_size; i++ ) {
|
||||||
|
std::vector<char> script;
|
||||||
|
unpack( s, script );
|
||||||
|
in.scriptWitness.push_back( script );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unpack( s, tx.nLockTime );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<char> pack( const bitcoin_transaction& v, bool with_witness = true )
|
||||||
|
{
|
||||||
|
fc::datastream<size_t> ps;
|
||||||
|
pack( ps, v, with_witness );
|
||||||
|
std::vector<char> vec( ps.tellp() );
|
||||||
|
|
||||||
|
if( !vec.empty() ) {
|
||||||
|
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
|
||||||
|
pack( ds, v, with_witness );
|
||||||
|
}
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bitcoin_transaction unpack( const std::vector<char>& s )
|
||||||
|
{ try {
|
||||||
|
bitcoin_transaction tmp;
|
||||||
|
if( !s.empty() ) {
|
||||||
|
fc::datastream<const char*> ds( s.data(), size_t( s.size() ) );
|
||||||
|
unpack(ds, tmp);
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
} FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type","transaction" ) ) }
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void pack_tx_signature( Stream& s, const std::vector<char>& scriptPubKey, const bitcoin_transaction& tx, unsigned int in_index, int hash_type )
|
||||||
|
{
|
||||||
|
pack( s, tx.nVersion );
|
||||||
|
|
||||||
|
pack_compact_size( s, tx.vin.size() );
|
||||||
|
for ( size_t i = 0; i < tx.vin.size(); i++ ) {
|
||||||
|
const auto& in = tx.vin[i];
|
||||||
|
pack( s, in.prevout );
|
||||||
|
if ( i == in_index )
|
||||||
|
pack( s, scriptPubKey );
|
||||||
|
else
|
||||||
|
pack_compact_size( s, 0 ); // Blank signature
|
||||||
|
pack( s, in.nSequence );
|
||||||
|
}
|
||||||
|
|
||||||
|
pack_compact_size( s, tx.vout.size() );
|
||||||
|
for ( const auto& out : tx.vout )
|
||||||
|
pack( s, out );
|
||||||
|
|
||||||
|
pack( s, tx.nLockTime );
|
||||||
|
pack( s, hash_type );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Stream>
|
||||||
|
inline void pack_tx_witness_signature( Stream& s, const std::vector<char>& scriptCode, const bitcoin_transaction& tx, unsigned int in_index, int64_t amount, int hash_type )
|
||||||
|
{
|
||||||
|
|
||||||
|
fc::sha256 hash_prevouts;
|
||||||
|
fc::sha256 hash_sequence;
|
||||||
|
fc::sha256 hash_output;
|
||||||
|
|
||||||
|
{
|
||||||
|
fc::datastream<size_t> ps;
|
||||||
|
for ( const auto in : tx.vin )
|
||||||
|
pack( ps, in.prevout );
|
||||||
|
|
||||||
|
std::vector<char> vec( ps.tellp() );
|
||||||
|
if ( vec.size() ) {
|
||||||
|
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
|
||||||
|
for ( const auto in : tx.vin )
|
||||||
|
pack( ds, in.prevout );
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_prevouts = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
fc::datastream<size_t> ps;
|
||||||
|
for ( const auto in : tx.vin )
|
||||||
|
pack( ps, in.nSequence );
|
||||||
|
|
||||||
|
std::vector<char> vec( ps.tellp() );
|
||||||
|
if ( vec.size() ) {
|
||||||
|
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
|
||||||
|
for ( const auto in : tx.vin )
|
||||||
|
pack( ds, in.nSequence );
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_sequence = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
fc::datastream<size_t> ps;
|
||||||
|
for ( const auto out : tx.vout )
|
||||||
|
pack( ps, out );
|
||||||
|
|
||||||
|
std::vector<char> vec( ps.tellp() );
|
||||||
|
if ( vec.size() ) {
|
||||||
|
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
|
||||||
|
for ( const auto out : tx.vout )
|
||||||
|
pack( ds, out );
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_output = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
pack( s, tx.nVersion );
|
||||||
|
pack( s, hash_prevouts );
|
||||||
|
pack( s, hash_sequence );
|
||||||
|
|
||||||
|
pack( s, tx.vin[in_index].prevout );
|
||||||
|
pack( s, scriptCode );
|
||||||
|
pack( s, amount );
|
||||||
|
pack( s, tx.vin[in_index].nSequence );
|
||||||
|
|
||||||
|
pack( s, hash_output );
|
||||||
|
pack( s, tx.nLockTime );
|
||||||
|
pack( s, hash_type );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sidechain/types.hpp>
|
||||||
|
#include <sidechain/input_withdrawal_info.hpp>
|
||||||
|
#include <secp256k1.h>
|
||||||
|
|
||||||
|
namespace sidechain {
|
||||||
|
|
||||||
|
class bitcoin_transaction;
|
||||||
|
|
||||||
|
fc::sha256 get_signature_hash( const bitcoin_transaction& tx, const bytes& scriptPubKey, int64_t amount,
|
||||||
|
size_t in_index, int hash_type, bool is_witness );
|
||||||
|
|
||||||
|
std::vector<char> privkey_sign( const bytes& privkey, const fc::sha256 &hash, const secp256k1_context_t* context_sign = nullptr );
|
||||||
|
|
||||||
|
std::vector< bytes > sign_witness_transaction_part( const bitcoin_transaction& tx, const std::vector<info_for_vin>& info_vins,
|
||||||
|
const bytes& privkey, const secp256k1_context_t* context_sign = nullptr, int hash_type = 1 );
|
||||||
|
|
||||||
|
void sign_witness_transaction_finalize( bitcoin_transaction& tx, const std::vector< bytes >& redeemScripts );
|
||||||
|
|
||||||
|
bool verify_sig( const bytes& sig, const bytes& pubkey, const bytes& msg, const secp256k1_context_t* context );
|
||||||
|
|
||||||
|
std::vector<std::vector<bytes>> sort_sigs( const bitcoin_transaction& tx, const std::vector<info_for_vin>& info_vins,
|
||||||
|
const secp256k1_context_t* context );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -7,4 +7,6 @@ namespace sidechain {
|
||||||
|
|
||||||
bytes parse_hex( const std::string& str );
|
bytes parse_hex( const std::string& str );
|
||||||
|
|
||||||
|
std::vector<bytes> get_pubkey_from_redeemScript( bytes script );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
121
libraries/sidechain/sign_bitcoin_transaction.cpp
Normal file
121
libraries/sidechain/sign_bitcoin_transaction.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
#include <sidechain/sign_bitcoin_transaction.hpp>
|
||||||
|
#include <sidechain/serialize.hpp>
|
||||||
|
|
||||||
|
namespace sidechain {
|
||||||
|
|
||||||
|
fc::sha256 get_signature_hash( const bitcoin_transaction& tx, const bytes& scriptCode, int64_t amount,
|
||||||
|
size_t in_index, int hash_type, bool is_witness )
|
||||||
|
{
|
||||||
|
fc::datastream<size_t> ps;
|
||||||
|
if ( is_witness )
|
||||||
|
pack_tx_witness_signature( ps, scriptCode, tx, in_index, amount, hash_type );
|
||||||
|
else
|
||||||
|
pack_tx_signature( ps, scriptCode, tx, in_index, hash_type );
|
||||||
|
|
||||||
|
std::vector<char> vec( ps.tellp() );
|
||||||
|
if ( !vec.empty() ) {
|
||||||
|
fc::datastream<char*> ds( vec.data(), vec.size() );
|
||||||
|
if ( is_witness )
|
||||||
|
pack_tx_witness_signature( ds, scriptCode, tx, in_index, amount, hash_type );
|
||||||
|
else
|
||||||
|
pack_tx_signature( ds, scriptCode, tx, in_index, hash_type );
|
||||||
|
}
|
||||||
|
|
||||||
|
return fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> privkey_sign( const bytes& privkey, const fc::sha256 &hash, const secp256k1_context_t* context_sign )
|
||||||
|
{
|
||||||
|
bytes sig;
|
||||||
|
sig.resize( 72 );
|
||||||
|
int sig_len = sig.size();
|
||||||
|
|
||||||
|
FC_ASSERT( secp256k1_ecdsa_sign(
|
||||||
|
context_sign,
|
||||||
|
reinterpret_cast<unsigned char*>( hash.data() ),
|
||||||
|
reinterpret_cast<unsigned char*>( sig.data() ),
|
||||||
|
&sig_len,
|
||||||
|
reinterpret_cast<const unsigned char*>( privkey.data() ),
|
||||||
|
secp256k1_nonce_function_rfc6979,
|
||||||
|
nullptr ) ); // TODO: replace assert with exception
|
||||||
|
|
||||||
|
sig.resize( sig_len );
|
||||||
|
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<bytes> sign_witness_transaction_part( const bitcoin_transaction& tx, const std::vector<info_for_vin>& info_vins,
|
||||||
|
const bytes& privkey, const secp256k1_context_t* context_sign, int hash_type )
|
||||||
|
{
|
||||||
|
FC_ASSERT( tx.vin.size() == info_vins.size() );
|
||||||
|
FC_ASSERT( !privkey.empty() );
|
||||||
|
|
||||||
|
std::vector< bytes > signatures;
|
||||||
|
for( size_t i = 0; i < tx.vin.size(); i++ ) {
|
||||||
|
const auto sighash = get_signature_hash( tx, info_vins[i].script, static_cast<int64_t>( info_vins[i].out.amount ), i, hash_type, true );
|
||||||
|
auto sig = privkey_sign( privkey, sighash, context_sign );
|
||||||
|
sig.push_back( static_cast<uint8_t>( hash_type ) );
|
||||||
|
|
||||||
|
signatures.push_back( sig );
|
||||||
|
}
|
||||||
|
return signatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sign_witness_transaction_finalize( bitcoin_transaction& tx, const std::vector<bytes>& redeemScripts )
|
||||||
|
{
|
||||||
|
FC_ASSERT( tx.vin.size() == redeemScripts.size() );
|
||||||
|
|
||||||
|
for( size_t i = 0; i < tx.vin.size(); i++ ) {
|
||||||
|
tx.vin[i].scriptWitness.insert( tx.vin[i].scriptWitness.begin(), bytes() ); // Bitcoin workaround CHECKMULTISIG bug
|
||||||
|
tx.vin[i].scriptWitness.push_back( redeemScripts[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verify_sig( const bytes& sig, const bytes& pubkey, const bytes& msg, const secp256k1_context_t* context )
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> sig_temp( sig.begin(), sig.end() );
|
||||||
|
std::vector<unsigned char> pubkey_temp( pubkey.begin(), pubkey.end() );
|
||||||
|
std::vector<unsigned char> msg_temp( msg.begin(), msg.end() );
|
||||||
|
|
||||||
|
int result = secp256k1_ecdsa_verify( context, msg_temp.data(), sig_temp.data(), sig_temp.size(), pubkey_temp.data(), pubkey_temp.size() );
|
||||||
|
return result == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<bytes>> sort_sigs( const bitcoin_transaction& tx, const std::vector<info_for_vin>& info_vins, const secp256k1_context_t* context )
|
||||||
|
{
|
||||||
|
using data = std::pair<size_t, bytes>;
|
||||||
|
struct comp {
|
||||||
|
bool operator() (const data& lhs, const data& rhs) const { return lhs.first < rhs.first; }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::vector<bytes>> new_stacks;
|
||||||
|
|
||||||
|
for( size_t i = 0; i < info_vins.size(); i++ ) {
|
||||||
|
const std::vector<bytes>& keys = get_pubkey_from_redeemScript( info_vins[i].script );
|
||||||
|
const auto& sighash = get_signature_hash( tx, info_vins[i].script, static_cast<int64_t>( info_vins[i].out.amount ), i, 1, true ).str();
|
||||||
|
bytes sighash_temp( parse_hex( sighash ) );
|
||||||
|
|
||||||
|
std::vector<bytes> stack( tx.vin[i].scriptWitness );
|
||||||
|
std::vector<bool> marker( tx.vin[i].scriptWitness.size(), false );
|
||||||
|
std::set<data, comp> sigs;
|
||||||
|
|
||||||
|
for( size_t j = 0; j < keys.size(); j++ ) {
|
||||||
|
for( size_t l = 0; l < stack.size(); l++ ) {
|
||||||
|
if( !verify_sig( stack[l], keys[j], sighash_temp, context ) || marker[l] )
|
||||||
|
continue;
|
||||||
|
sigs.insert(std::make_pair(j, stack[l]));
|
||||||
|
marker[l] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<bytes> temp_sig;
|
||||||
|
for( auto s : sigs ) {
|
||||||
|
temp_sig.push_back( s.second );
|
||||||
|
}
|
||||||
|
new_stacks.push_back( temp_sig );
|
||||||
|
}
|
||||||
|
return new_stacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -9,4 +9,19 @@ bytes parse_hex( const std::string& str )
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<bytes> get_pubkey_from_redeemScript( bytes script )
|
||||||
|
{
|
||||||
|
FC_ASSERT( script.size() >= 37 );
|
||||||
|
|
||||||
|
script.erase( script.begin() );
|
||||||
|
script.erase( script.end() - 2, script.end() );
|
||||||
|
|
||||||
|
std::vector<bytes> result;
|
||||||
|
uint64_t count = script.size() / 34;
|
||||||
|
for( size_t i = 0; i < count; i++ ) {
|
||||||
|
result.push_back( bytes( script.begin() + (34 * i) + 1, script.begin() + (34 * (i + 1)) ) );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
173
tests/tests/bitcoin_sign_tests.cpp
Normal file
173
tests/tests/bitcoin_sign_tests.cpp
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,7 @@
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <sidechain/utils.hpp>
|
#include <sidechain/utils.hpp>
|
||||||
#include <sidechain/bitcoin_transaction.hpp>
|
#include <sidechain/bitcoin_transaction.hpp>
|
||||||
|
#include <sidechain/serialize.hpp>
|
||||||
|
|
||||||
using namespace sidechain;
|
using namespace sidechain;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue