diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp index c3fcf371..8398c7eb 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin_utils.cpp @@ -4,11 +4,28 @@ #include #include #include -#include +#include 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 > 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 > 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(p.second)}); + add_number_to_script(result, static_cast(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(threshold_weight)}); + add_number_to_script(result, static_cast(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 signature_for_raw_transaction(const bytes& unsigned_tx, @@ -564,11 +600,8 @@ std::vector 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(&hashPrevouts[0]), hashPrevouts.size()))); bytes hashSequence = hash_sequence(tx); - ilog("hashSequence ${h}", ("h", fc::to_hex(reinterpret_cast(&hashSequence[0]), hashSequence.size()))); bytes hashOutputs = hash_outputs(tx); - ilog("hashOutputs ${h}", ("h", fc::to_hex(reinterpret_cast(&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 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(&serializedScript[0]), serializedScript.size()); uint64_t amount = *cur_amount++; hasher.write(reinterpret_cast(&amount), sizeof(amount)); @@ -595,10 +627,11 @@ std::vector signature_for_raw_transaction(const bytes& unsigned_tx, hasher.write(reinterpret_cast(&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(&results.back()[0]), results.back().size()))); + //std::vector 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; } diff --git a/tests/peerplays_sidechain/bitcoin_utils_test.cpp b/tests/peerplays_sidechain/bitcoin_utils_test.cpp index b7e42c70..4362d399 100644 --- a/tests/peerplays_sidechain/bitcoin_utils_test.cpp +++ b/tests/peerplays_sidechain/bitcoin_utils_test.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include 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 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 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(&unsigned_tx[0]), unsigned_tx.size()))); std::vector in_amounts({20000}); std::vector> keys_to_sign; for(auto key: priv_old) keys_to_sign.push_back(fc::optional(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(&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"); } +