diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp index c330263c..d3c089fe 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp @@ -16,13 +16,33 @@ static const unsigned char OP_CHECKSIG = 0xac; class WriteBytesStream{ public: - WriteBytesStream(bytes& buffer); + WriteBytesStream(bytes& buffer) : storage_(buffer) {} bool write( const char* d, size_t s ) { storage_.insert(storage_.end(), d, d + s); return true; } + void add( const unsigned char* d, size_t s ) { + storage_.insert(storage_.end(), d, d + s); + } + + void add( const bytes& data) { + storage_.insert(storage_.end(), data.begin(), data.end()); + } + + void add(int32_t val) + { + const char* data = reinterpret_cast(&val); + write(data, sizeof(val)); + } + + void add(uint32_t val) + { + const char* data = reinterpret_cast(&val); + write(data, sizeof(val)); + } + bool put(char c) { storage_.push_back(c); return true; @@ -31,6 +51,59 @@ private: bytes& storage_; }; +class ReadBytesStream{ +public: + ReadBytesStream(const bytes& buffer, size_t pos = 0) : storage_(buffer), pos_(pos), end_(buffer.size()) {} + + inline bool read( char* d, size_t s ) { + if( end_ - pos_ >= s ) { + memcpy( d, &storage_[pos_], s ); + pos_ += s; + return true; + } + FC_THROW( "invalid bitcoin tx buffer" ); + } + + void direct_read( unsigned char* d, size_t s ) { + if( end_ - pos_ >= s ) { + memcpy( d, &storage_[pos_], s ); + pos_ += s; + } + FC_THROW( "invalid bitcoin tx buffer" ); + } + + void direct_read( bytes& data, size_t s) { + data.clear(); + data.resize(s); + direct_read(&data[0], s); + } + + void direct_read(int32_t& val) + { + char* data = reinterpret_cast(&val); + read(data, sizeof(val)); + } + + void direct_read(uint32_t& val) + { + char* data = reinterpret_cast(&val); + read(data, sizeof(val)); + } + + inline bool get( unsigned char& c ) { return get( *(char*)&c ); } + inline bool get( char& c ) + { + if( pos_ < end_ ) { + c = storage_[pos_++]; + return true; + } + FC_THROW( "invalid bitcoin tx buffer" ); + } +private: + const bytes& storage_; + size_t pos_; + size_t end_; +}; void add_data_to_script(bytes& script, const bytes& data) { @@ -89,9 +162,123 @@ std::string p2sh_address_from_redeem_script(const bytes& script, bitcoin_network return fc::to_base58(reinterpret_cast(&data[0]), data.size()); } -bytes sign_raw_transaction(const bytes& unsigned_tx, const fc::ecc::private_key& priv_key) +bytes signature_for_raw_transaction(const bytes& unsigned_tx, const fc::ecc::private_key& priv_key) { - return bytes(); + fc::sha256 digest = fc::sha256::hash(fc::sha256::hash(reinterpret_cast(&unsigned_tx[0]), unsigned_tx.size())); + fc::ecc::compact_signature res = priv_key.sign_compact(digest); + return bytes(res.begin(), res.begin() + res.size()); +} + +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 vin; + std::vector vout; + int32_t nVersion; + uint32_t nLockTime; + bool hasWitness; + + bytes to_bytes() const + { + bytes res; + WriteBytesStream str(res); + str.add(nVersion); + if(hasWitness) + { + std::vector 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; + } + + void 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 > &priv_keys) +{ + btc_tx tx; + tx.fill_from_bytes(unsigned_tx); + fc::ecc::compact_signature dummy; + for(auto& in: tx.vin) + { + WriteBytesStream script(in.scriptSig); + for(auto key: priv_keys) + { + if(key) + { + bytes signature = signature_for_raw_transaction(unsigned_tx, *key); + script.add(signature); + } + else + { + script.add(dummy.begin(), dummy.size()); + } + } + script.add(redeem_script); + } + return tx.to_bytes(); } }} + +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)) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp index e9f2bfdb..cf5dcb68 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_utils.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include namespace graphene { namespace peerplays_sidechain { @@ -11,6 +12,10 @@ enum bitcoin_network { bytes generate_redeem_script(fc::flat_map key_data); std::string p2sh_address_from_redeem_script(const bytes& script, bitcoin_network network = mainnet); -bytes sign_raw_transaction(const bytes& unsigned_tx, const fc::ecc::private_key& priv_key); +/* + * unsigned_tx - tx, all inputs of which are spends of the PW P2SH address + * returns signed transaction + */ +bytes sign_pw_transfer_transaction(const bytes& unsigned_tx, const bytes& redeem_script, const std::vector>& priv_keys); }}