working signing

This commit is contained in:
gladcow 2020-02-18 13:02:48 +03:00
parent f3be3e1f82
commit f7dbc8d5f6
2 changed files with 103 additions and 58 deletions

View file

@ -4,11 +4,28 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/ripemd160.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/crypto/hex.hpp>
#include <secp256k1.h>
namespace graphene { namespace peerplays_sidechain {
static const unsigned char OP_0 = 0x00;
static const unsigned char OP_1 = 0x51;
static const unsigned char OP_2 = 0x52;
static const unsigned char OP_3 = 0x53;
static const unsigned char OP_4 = 0x54;
static const unsigned char OP_5 = 0x55;
static const unsigned char OP_6 = 0x56;
static const unsigned char OP_7 = 0x57;
static const unsigned char OP_8 = 0x58;
static const unsigned char OP_9 = 0x59;
static const unsigned char OP_10 = 0x5a;
static const unsigned char OP_11 = 0x5b;
static const unsigned char OP_12 = 0x5c;
static const unsigned char OP_13 = 0x5d;
static const unsigned char OP_14 = 0x5e;
static const unsigned char OP_15 = 0x5f;
static const unsigned char OP_16 = 0x60;
static const unsigned char OP_IF = 0x63;
static const unsigned char OP_ENDIF = 0x68;
static const unsigned char OP_SWAP = 0x7c;
@ -327,11 +344,52 @@ void add_data_to_script(bytes& script, const bytes& data)
str.writedata(data);
}
void add_number_to_script(bytes& script, unsigned char data)
{
WriteBytesStream str(script);
if(data == 0)
str.put(OP_0);
else if(data == 1)
str.put(OP_1);
else if(data == 2)
str.put(OP_2);
else if(data == 3)
str.put(OP_3);
else if(data == 4)
str.put(OP_4);
else if(data == 5)
str.put(OP_5);
else if(data == 6)
str.put(OP_6);
else if(data == 7)
str.put(OP_7);
else if(data == 8)
str.put(OP_8);
else if(data == 9)
str.put(OP_9);
else if(data == 10)
str.put(OP_10);
else if(data == 11)
str.put(OP_11);
else if(data == 12)
str.put(OP_12);
else if(data == 13)
str.put(OP_13);
else if(data == 14)
str.put(OP_14);
else if(data == 15)
str.put(OP_15);
else if(data == 16)
str.put(OP_16);
else
add_data_to_script(script, {data});
}
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int> > key_data)
{
int total_weight = 0;
bytes result;
add_data_to_script(result, {0});
add_number_to_script(result, 0);
for(auto& p: key_data)
{
total_weight += p.second;
@ -340,12 +398,12 @@ bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int> > k
add_data_to_script(result, bytes(raw_data.begin(), raw_data.begin() + raw_data.size()));
result.push_back(OP_CHECKSIG);
result.push_back(OP_IF);
add_data_to_script(result, {static_cast<unsigned char>(p.second)});
add_number_to_script(result, static_cast<unsigned char>(p.second));
result.push_back(OP_ADD);
result.push_back(OP_ENDIF);
}
int threshold_weight = 2 * total_weight / 3;
add_data_to_script(result, {static_cast<unsigned char>(threshold_weight)});
add_number_to_script(result, static_cast<unsigned char>(threshold_weight));
result.push_back(OP_GREATERTHAN);
return result;
}
@ -509,45 +567,23 @@ bytes hash_outputs(const btc_tx& unsigned_tx)
return bytes(res.data(), res.data() + res.data_size());
}
bytes get_r(const fc::ecc::compact_signature& s)
{
const unsigned char* start = s.begin();
const unsigned char* end = s.begin() + 32;
while(*start++ == 0);
bytes res;
if(*start & 0x80)
res.push_back(0);
res.insert(res.end(), start, end);
return res;
const secp256k1_context_t* btc_get_context() {
static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN );
return ctx;
}
bytes get_s(const fc::ecc::compact_signature& s)
bytes der_sign(const fc::ecc::private_key& priv_key, const fc::sha256& digest)
{
const unsigned char* start = s.begin() + 32;
const unsigned char* end = s.begin() + 64;
while(*start++ == 0);
bytes res;
if(*start & 0x80)
res.push_back(0);
res.insert(res.end(), start, end);
return res;
}
bytes to_DER_format(const fc::ecc::compact_signature& s)
{
bytes r_value = get_r(s);
bytes s_value = get_s(s);
bytes der_signature;
der_signature.push_back(0x30);
der_signature.push_back(r_value.size() + s_value.size() + 4); // total len
der_signature.push_back(0x02);
der_signature.push_back(r_value.size());
der_signature.insert(der_signature.end(), r_value.begin(), r_value.end());
der_signature.push_back(0x02);
der_signature.push_back(s_value.size());
der_signature.insert(der_signature.end(), s_value.begin(), s_value.end());
der_signature.push_back(1); // SIGHASH_ALL
return der_signature;
fc::ecc::signature result;
int size = result.size();
FC_ASSERT( secp256k1_ecdsa_sign( btc_get_context(),
(unsigned char*) digest.data(),
(unsigned char*) result.begin(),
&size,
(unsigned char*) priv_key.get_secret().data(),
secp256k1_nonce_function_rfc6979,
nullptr));
return bytes(result.begin(), result.begin() + size);
}
std::vector<bytes> signature_for_raw_transaction(const bytes& unsigned_tx,
@ -564,11 +600,8 @@ std::vector<bytes> signature_for_raw_transaction(const bytes& unsigned_tx,
auto cur_amount = in_amounts.begin();
// pre-calc reused values
bytes hashPrevouts = hash_prevouts(tx);
ilog("hashPrevouts ${h}", ("h", fc::to_hex(reinterpret_cast<char*>(&hashPrevouts[0]), hashPrevouts.size())));
bytes hashSequence = hash_sequence(tx);
ilog("hashSequence ${h}", ("h", fc::to_hex(reinterpret_cast<char*>(&hashSequence[0]), hashSequence.size())));
bytes hashOutputs = hash_outputs(tx);
ilog("hashOutputs ${h}", ("h", fc::to_hex(reinterpret_cast<char*>(&hashOutputs[0]), hashOutputs.size())));
// calc digest for every input according to BIP143
// implement SIGHASH_ALL scheme
for(const auto& in: tx.vin)
@ -583,7 +616,6 @@ std::vector<bytes> signature_for_raw_transaction(const bytes& unsigned_tx,
bytes serializedScript;
WriteBytesStream stream(serializedScript);
stream.writedata(redeem_script);
ilog("Script size: ${s}", ("s", redeem_script.size()));
hasher.write(reinterpret_cast<const char*>(&serializedScript[0]), serializedScript.size());
uint64_t amount = *cur_amount++;
hasher.write(reinterpret_cast<const char*>(&amount), sizeof(amount));
@ -595,10 +627,11 @@ std::vector<bytes> signature_for_raw_transaction(const bytes& unsigned_tx,
hasher.write(reinterpret_cast<const char*>(&sigtype), sizeof(sigtype));
fc::sha256 digest = fc::sha256::hash(hasher.result());
ilog("Digest ${d}", ("d", fc::to_hex(digest.data(), digest.data_size())));
fc::ecc::compact_signature res = priv_key.sign_compact(digest, false);
results.push_back(to_DER_format(res));
ilog("Signature ${s}", ("s", fc::to_hex(reinterpret_cast<char*>(&results.back()[0]), results.back().size())));
//std::vector<char> res = priv_key.sign(digest);
//bytes s_data(res.begin(), res.end());
bytes s_data = der_sign(priv_key, digest);
s_data.push_back(1);
results.push_back(s_data);
}
return results;
}

View file

@ -2,6 +2,8 @@
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <secp256k1.h>
using namespace graphene::peerplays_sidechain;
@ -36,6 +38,15 @@ BOOST_AUTO_TEST_CASE(pw_transfer)
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
}
// print old keys
for(auto key: priv_old)
{
fc::sha256 secret = key.get_secret();
bytes data({239});
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
data.insert(data.end(), cs.data(), cs.data() + 4);
}
std::vector<fc::ecc::public_key> pub_old;
for(auto& key: priv_old)
pub_old.push_back(key.get_public_key());
@ -48,9 +59,10 @@ BOOST_AUTO_TEST_CASE(pw_transfer)
// Old PW address
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
ilog("First wallet address ${a}", ("a", old_pw));
// This address was filled with testnet transaction dc66f205a1bb03cd544c832f1d50e6746860c1223b08213f3cc793742e69c07d
BOOST_REQUIRE(old_pw == "tb1qr2m64awu34wv8l69449ukl4atqylxh77ppdq862kuvfcp740hm7s3a0g6r");
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
// key set for the new Primary Wallet
std::vector<fc::ecc::private_key> priv_new;
@ -71,23 +83,22 @@ BOOST_AUTO_TEST_CASE(pw_transfer)
bytes redeem_new =generate_redeem_script(weights_new);
// New PW address
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
ilog("Second wallet address ${a}", ("a", new_pw));
BOOST_REQUIRE(new_pw == "tb1qd6qfa9cu0gexk2y5xarlw56j542uultr7pepszc6473ksyrtna2q006kzc");
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
// try to move funds from old wallet to new one
// get unspent outputs for old wallet with list_uspent (address should be
// added to wallet with import_address before). It should return
// 1 UTXO: [dc66f205a1bb03cd544c832f1d50e6746860c1223b08213f3cc793742e69c07d:1]
// 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0]
// with 20000 satoshis
// So, we creating a raw transaction with 1 input and one output that gets
// 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
// Here we just serialize the transaction without scriptSig in inputs then sign it.
btc_outpoint outpoint;
outpoint.hash = fc::uint256("dc66f205a1bb03cd544c832f1d50e6746860c1223b08213f3cc793742e69c07d");
outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766");
// reverse hash due to the different from_hex algo
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
outpoint.n = 1;
outpoint.n = 0;
btc_in input;
input.prevout = outpoint;
input.nSequence = 0xffffffff;
@ -102,11 +113,12 @@ BOOST_AUTO_TEST_CASE(pw_transfer)
tx.vout.push_back(output);
bytes unsigned_tx;
tx.to_bytes(unsigned_tx);
ilog("Unsigned tx: [${tx}]", ("tx", fc::to_hex(reinterpret_cast<char*>(&unsigned_tx[0]), unsigned_tx.size())));
std::vector<uint64_t> in_amounts({20000});
std::vector<fc::optional<fc::ecc::private_key>> keys_to_sign;
for(auto key: priv_old)
keys_to_sign.push_back(fc::optional<fc::ecc::private_key>(key));
bytes signed_tx =sign_pw_transfer_transaction(unsigned_tx, in_amounts, redeem_old, keys_to_sign);
ilog("Signed tx: [${tx}]", ("tx", fc::to_hex(reinterpret_cast<char*>(&signed_tx[0]), signed_tx.size())));
// this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850
BOOST_CHECK(fc::to_hex((char*)&signed_tx[0], signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
}