121 lines
4.4 KiB
C++
121 lines
4.4 KiB
C++
#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;
|
|
}
|
|
|
|
}
|