Browse Source
0.15019c492
qa: Fix lcov for out-of-tree builds (MarcoFalke)e169349
qa: Restore bitcoin-util-test py2 compatibility (MarcoFalke)806c78f
add functional test for mempoolreplacement command line arg (Gregory Sanders)a825d4a
Fix bip68-sequence rpc test (Johnson Lau)a36f332
Verify DBWrapper iterators are taking snapshots (Matt Corallo)8d2e51d
qa: Fix bug introduced in p2p-segwit.py (Suhas Daftuar)2f0b30a
qa: Treat mininode p2p exceptions as fatal (Suhas Daftuar)e4605d9
Tests for zmqpubrawtx and zmqpubrawblock (Andrew Chow)2c4ff35
[script] Unit tests for IsMine (Jim Posen)794a80e
[script] Unit tests for script/standard functions (Jim Posen)f9cf7b5
[tests] Check connectivity before sending in assumevalid.py (John Newbery)f1ced0d
[tests] Make p2p-leaktests.py more robust (John Newbery)2e1ac70
[qa] zapwallettxes: Wait up to 3s for mempool reload (MarcoFalke)b6468d3
Add listwallets RPC test to multiwallet.py (Cristian Mircea Messel)d8dd8e7
[tests] fixup dbcrash interaction with add_nodes() (John Newbery)2b97b36
[test] Replace check_output with low level version (João Barbosa)e38211f
[test] Add assert_raises_process_error to assert process errors (João Barbosa)e0bfd28
[test] Add support for custom arguments to TestNodeCLI (João Barbosa)812c870
[test] Improve assert_raises_jsonrpc docstring (João Barbosa)eeb24a3
[qa] TestNode: Add wait_until_stopped helper method (MarcoFalke)f3f7891
Stop test_bitcoin-qt touching ~/.bitcoin (MeshCollider)f0b6795
Remove redundant testutil files (MeshCollider)4424176
Improve signmessages functional test (Cristian Mircea Messel)cef0319
[tests] fixups from set_test_params() (John Newbery)82bf6fc
[tests] Functional tests must explicitly set num_nodes (John Newbery)801d2ae
[tests] don't override __init__() in individual tests (John Newbery)bb5e7cb
[tests] Avoid passing around member variables in test_framework (John Newbery)4d3ba18
[tests] TestNode: separate add_node from start_node (John Newbery)11a5992
[tests] fix - use rpc_timeout as rpc timeout (John Newbery)847c75e
Add getmininginfo functional test (Cristian Mircea Messel)2a5d099
RPC: gettxout: Slightly improve doc and tests (Jorge Timón)716066d
[tests] Add bitcoin_cli.py test script (John Newbery)016b9ad
[tests] add TestNodeCLI class for calling bitcoin-cli for a node (John Newbery)5398f20
qa: Move wait_until to util (MarcoFalke)1d80d1e
[tests] fix timeout issues from TestNode (John Newbery)c276c1e
test: Increase initial RPC timeout to 60 seconds (Wladimir J. van der Laan)fc2aa09
[tests] Introduce TestNode (John Newbery) Pull request description: This includes test related backports for 0.15.1. The motivation is twofold: * Make backporting new tests written for current master easier * Fix the most common test issues that happen(ed) frequently on travis Even though this includes the new TestNode class, which comes with a lot of refactoring, I believe that the issues caused by refactoring are found and fixed by now. Tree-SHA512: 6a0c4e5246da83ff0b3f7d2cb8df358d105ed548fb3857e5d882f26cc336553aa07b39e38c281879bf82f95078298b775334f9a60c0b23140f77c50174bd8347
Wladimir J. van der Laan
7 years ago
102 changed files with 1658 additions and 880 deletions
@ -0,0 +1,740 @@ |
|||||||
|
// Copyright (c) 2017 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "key.h" |
||||||
|
#include "keystore.h" |
||||||
|
#include "script/ismine.h" |
||||||
|
#include "script/script.h" |
||||||
|
#include "script/script_error.h" |
||||||
|
#include "script/standard.h" |
||||||
|
#include "test/test_bitcoin.h" |
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp> |
||||||
|
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup) |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_standard_Solver_success) |
||||||
|
{ |
||||||
|
CKey keys[3]; |
||||||
|
CPubKey pubkeys[3]; |
||||||
|
for (int i = 0; i < 3; i++) { |
||||||
|
keys[i].MakeNewKey(true); |
||||||
|
pubkeys[i] = keys[i].GetPubKey(); |
||||||
|
} |
||||||
|
|
||||||
|
CScript s; |
||||||
|
txnouttype whichType; |
||||||
|
std::vector<std::vector<unsigned char> > solutions; |
||||||
|
|
||||||
|
// TX_PUBKEY
|
||||||
|
s.clear(); |
||||||
|
s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); |
||||||
|
BOOST_CHECK_EQUAL(solutions.size(), 1); |
||||||
|
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0])); |
||||||
|
|
||||||
|
// TX_PUBKEYHASH
|
||||||
|
s.clear(); |
||||||
|
s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); |
||||||
|
BOOST_CHECK_EQUAL(solutions.size(), 1); |
||||||
|
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); |
||||||
|
|
||||||
|
// TX_SCRIPTHASH
|
||||||
|
CScript redeemScript(s); // initialize with leftover P2PKH script
|
||||||
|
s.clear(); |
||||||
|
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; |
||||||
|
BOOST_CHECK(Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); |
||||||
|
BOOST_CHECK_EQUAL(solutions.size(), 1); |
||||||
|
BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript))); |
||||||
|
|
||||||
|
// TX_MULTISIG
|
||||||
|
s.clear(); |
||||||
|
s << OP_1 << |
||||||
|
ToByteVector(pubkeys[0]) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
OP_2 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); |
||||||
|
BOOST_CHECK_EQUAL(solutions.size(), 4); |
||||||
|
BOOST_CHECK(solutions[0] == std::vector<unsigned char>({1})); |
||||||
|
BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); |
||||||
|
BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); |
||||||
|
BOOST_CHECK(solutions[3] == std::vector<unsigned char>({2})); |
||||||
|
|
||||||
|
s.clear(); |
||||||
|
s << OP_2 << |
||||||
|
ToByteVector(pubkeys[0]) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
ToByteVector(pubkeys[2]) << |
||||||
|
OP_3 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); |
||||||
|
BOOST_CHECK_EQUAL(solutions.size(), 5); |
||||||
|
BOOST_CHECK(solutions[0] == std::vector<unsigned char>({2})); |
||||||
|
BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); |
||||||
|
BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); |
||||||
|
BOOST_CHECK(solutions[3] == ToByteVector(pubkeys[2])); |
||||||
|
BOOST_CHECK(solutions[4] == std::vector<unsigned char>({3})); |
||||||
|
|
||||||
|
// TX_NULL_DATA
|
||||||
|
s.clear(); |
||||||
|
s << OP_RETURN << |
||||||
|
std::vector<unsigned char>({0}) << |
||||||
|
std::vector<unsigned char>({75}) << |
||||||
|
std::vector<unsigned char>({255}); |
||||||
|
BOOST_CHECK(Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); |
||||||
|
BOOST_CHECK_EQUAL(solutions.size(), 0); |
||||||
|
|
||||||
|
// TX_WITNESS_V0_KEYHASH
|
||||||
|
s.clear(); |
||||||
|
s << OP_0 << ToByteVector(pubkeys[0].GetID()); |
||||||
|
BOOST_CHECK(Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_KEYHASH); |
||||||
|
BOOST_CHECK_EQUAL(solutions.size(), 1); |
||||||
|
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); |
||||||
|
|
||||||
|
// TX_WITNESS_V0_SCRIPTHASH
|
||||||
|
uint256 scriptHash; |
||||||
|
CSHA256().Write(&redeemScript[0], redeemScript.size()) |
||||||
|
.Finalize(scriptHash.begin()); |
||||||
|
|
||||||
|
s.clear(); |
||||||
|
s << OP_0 << ToByteVector(scriptHash); |
||||||
|
BOOST_CHECK(Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_SCRIPTHASH); |
||||||
|
BOOST_CHECK_EQUAL(solutions.size(), 1); |
||||||
|
BOOST_CHECK(solutions[0] == ToByteVector(scriptHash)); |
||||||
|
|
||||||
|
// TX_NONSTANDARD
|
||||||
|
s.clear(); |
||||||
|
s << OP_9 << OP_ADD << OP_11 << OP_EQUAL; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) |
||||||
|
{ |
||||||
|
CKey key; |
||||||
|
CPubKey pubkey; |
||||||
|
key.MakeNewKey(true); |
||||||
|
pubkey = key.GetPubKey(); |
||||||
|
|
||||||
|
CScript s; |
||||||
|
txnouttype whichType; |
||||||
|
std::vector<std::vector<unsigned char> > solutions; |
||||||
|
|
||||||
|
// TX_PUBKEY with incorrectly sized pubkey
|
||||||
|
s.clear(); |
||||||
|
s << std::vector<unsigned char>(30, 0x01) << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_PUBKEYHASH with incorrectly sized key hash
|
||||||
|
s.clear(); |
||||||
|
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_SCRIPTHASH with incorrectly sized script hash
|
||||||
|
s.clear(); |
||||||
|
s << OP_HASH160 << std::vector<unsigned char>(21, 0x01) << OP_EQUAL; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_MULTISIG 0/2
|
||||||
|
s.clear(); |
||||||
|
s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_MULTISIG 2/1
|
||||||
|
s.clear(); |
||||||
|
s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_MULTISIG n = 2 with 1 pubkey
|
||||||
|
s.clear(); |
||||||
|
s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_MULTISIG n = 1 with 0 pubkeys
|
||||||
|
s.clear(); |
||||||
|
s << OP_1 << OP_1 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_NULL_DATA with other opcodes
|
||||||
|
s.clear(); |
||||||
|
s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD; |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_WITNESS with unknown version
|
||||||
|
s.clear(); |
||||||
|
s << OP_1 << ToByteVector(pubkey); |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
|
||||||
|
// TX_WITNESS with incorrect program size
|
||||||
|
s.clear(); |
||||||
|
s << OP_0 << std::vector<unsigned char>(19, 0x01); |
||||||
|
BOOST_CHECK(!Solver(s, whichType, solutions)); |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) |
||||||
|
{ |
||||||
|
CKey key; |
||||||
|
CPubKey pubkey; |
||||||
|
key.MakeNewKey(true); |
||||||
|
pubkey = key.GetPubKey(); |
||||||
|
|
||||||
|
CScript s; |
||||||
|
CTxDestination address; |
||||||
|
|
||||||
|
// TX_PUBKEY
|
||||||
|
s.clear(); |
||||||
|
s << ToByteVector(pubkey) << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(ExtractDestination(s, address)); |
||||||
|
BOOST_CHECK(boost::get<CKeyID>(&address) && |
||||||
|
*boost::get<CKeyID>(&address) == pubkey.GetID()); |
||||||
|
|
||||||
|
// TX_PUBKEYHASH
|
||||||
|
s.clear(); |
||||||
|
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(ExtractDestination(s, address)); |
||||||
|
BOOST_CHECK(boost::get<CKeyID>(&address) && |
||||||
|
*boost::get<CKeyID>(&address) == pubkey.GetID()); |
||||||
|
|
||||||
|
// TX_SCRIPTHASH
|
||||||
|
CScript redeemScript(s); // initialize with leftover P2PKH script
|
||||||
|
s.clear(); |
||||||
|
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; |
||||||
|
BOOST_CHECK(ExtractDestination(s, address)); |
||||||
|
BOOST_CHECK(boost::get<CScriptID>(&address) && |
||||||
|
*boost::get<CScriptID>(&address) == CScriptID(redeemScript)); |
||||||
|
|
||||||
|
// TX_MULTISIG
|
||||||
|
s.clear(); |
||||||
|
s << OP_1 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(!ExtractDestination(s, address)); |
||||||
|
|
||||||
|
// TX_NULL_DATA
|
||||||
|
s.clear(); |
||||||
|
s << OP_RETURN << std::vector<unsigned char>({75}); |
||||||
|
BOOST_CHECK(!ExtractDestination(s, address)); |
||||||
|
|
||||||
|
// TX_WITNESS_V0_KEYHASH
|
||||||
|
s.clear(); |
||||||
|
s << OP_0 << ToByteVector(pubkey); |
||||||
|
BOOST_CHECK(!ExtractDestination(s, address)); |
||||||
|
|
||||||
|
// TX_WITNESS_V0_SCRIPTHASH
|
||||||
|
s.clear(); |
||||||
|
s << OP_0 << ToByteVector(CScriptID(redeemScript)); |
||||||
|
BOOST_CHECK(!ExtractDestination(s, address)); |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) |
||||||
|
{ |
||||||
|
CKey keys[3]; |
||||||
|
CPubKey pubkeys[3]; |
||||||
|
for (int i = 0; i < 3; i++) { |
||||||
|
keys[i].MakeNewKey(true); |
||||||
|
pubkeys[i] = keys[i].GetPubKey(); |
||||||
|
} |
||||||
|
|
||||||
|
CScript s; |
||||||
|
txnouttype whichType; |
||||||
|
std::vector<CTxDestination> addresses; |
||||||
|
int nRequired; |
||||||
|
|
||||||
|
// TX_PUBKEY
|
||||||
|
s.clear(); |
||||||
|
s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); |
||||||
|
BOOST_CHECK_EQUAL(addresses.size(), 1); |
||||||
|
BOOST_CHECK_EQUAL(nRequired, 1); |
||||||
|
BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && |
||||||
|
*boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); |
||||||
|
|
||||||
|
// TX_PUBKEYHASH
|
||||||
|
s.clear(); |
||||||
|
s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); |
||||||
|
BOOST_CHECK_EQUAL(addresses.size(), 1); |
||||||
|
BOOST_CHECK_EQUAL(nRequired, 1); |
||||||
|
BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && |
||||||
|
*boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); |
||||||
|
|
||||||
|
// TX_SCRIPTHASH
|
||||||
|
CScript redeemScript(s); // initialize with leftover P2PKH script
|
||||||
|
s.clear(); |
||||||
|
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; |
||||||
|
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); |
||||||
|
BOOST_CHECK_EQUAL(addresses.size(), 1); |
||||||
|
BOOST_CHECK_EQUAL(nRequired, 1); |
||||||
|
BOOST_CHECK(boost::get<CScriptID>(&addresses[0]) && |
||||||
|
*boost::get<CScriptID>(&addresses[0]) == CScriptID(redeemScript)); |
||||||
|
|
||||||
|
// TX_MULTISIG
|
||||||
|
s.clear(); |
||||||
|
s << OP_2 << |
||||||
|
ToByteVector(pubkeys[0]) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
OP_2 << OP_CHECKMULTISIG; |
||||||
|
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); |
||||||
|
BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); |
||||||
|
BOOST_CHECK_EQUAL(addresses.size(), 2); |
||||||
|
BOOST_CHECK_EQUAL(nRequired, 2); |
||||||
|
BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && |
||||||
|
*boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); |
||||||
|
BOOST_CHECK(boost::get<CKeyID>(&addresses[1]) && |
||||||
|
*boost::get<CKeyID>(&addresses[1]) == pubkeys[1].GetID()); |
||||||
|
|
||||||
|
// TX_NULL_DATA
|
||||||
|
s.clear(); |
||||||
|
s << OP_RETURN << std::vector<unsigned char>({75}); |
||||||
|
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); |
||||||
|
|
||||||
|
// TX_WITNESS_V0_KEYHASH
|
||||||
|
s.clear(); |
||||||
|
s << OP_0 << ToByteVector(pubkeys[0].GetID()); |
||||||
|
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); |
||||||
|
|
||||||
|
// TX_WITNESS_V0_SCRIPTHASH
|
||||||
|
s.clear(); |
||||||
|
s << OP_0 << ToByteVector(CScriptID(redeemScript)); |
||||||
|
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) |
||||||
|
{ |
||||||
|
CKey keys[3]; |
||||||
|
CPubKey pubkeys[3]; |
||||||
|
for (int i = 0; i < 3; i++) { |
||||||
|
keys[i].MakeNewKey(true); |
||||||
|
pubkeys[i] = keys[i].GetPubKey(); |
||||||
|
} |
||||||
|
|
||||||
|
CScript expected, result; |
||||||
|
|
||||||
|
// CKeyID
|
||||||
|
expected.clear(); |
||||||
|
expected << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
result = GetScriptForDestination(pubkeys[0].GetID()); |
||||||
|
BOOST_CHECK(result == expected); |
||||||
|
|
||||||
|
// CScriptID
|
||||||
|
CScript redeemScript(result); |
||||||
|
expected.clear(); |
||||||
|
expected << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; |
||||||
|
result = GetScriptForDestination(CScriptID(redeemScript)); |
||||||
|
BOOST_CHECK(result == expected); |
||||||
|
|
||||||
|
// CNoDestination
|
||||||
|
expected.clear(); |
||||||
|
result = GetScriptForDestination(CNoDestination()); |
||||||
|
BOOST_CHECK(result == expected); |
||||||
|
|
||||||
|
// GetScriptForRawPubKey
|
||||||
|
expected.clear(); |
||||||
|
expected << ToByteVector(pubkeys[0]) << OP_CHECKSIG; |
||||||
|
result = GetScriptForRawPubKey(pubkeys[0]); |
||||||
|
BOOST_CHECK(result == expected); |
||||||
|
|
||||||
|
// GetScriptForMultisig
|
||||||
|
expected.clear(); |
||||||
|
expected << OP_2 << |
||||||
|
ToByteVector(pubkeys[0]) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
ToByteVector(pubkeys[2]) << |
||||||
|
OP_3 << OP_CHECKMULTISIG; |
||||||
|
result = GetScriptForMultisig(2, std::vector<CPubKey>(pubkeys, pubkeys + 3)); |
||||||
|
BOOST_CHECK(result == expected); |
||||||
|
|
||||||
|
// GetScriptForWitness
|
||||||
|
CScript witnessScript; |
||||||
|
|
||||||
|
witnessScript << ToByteVector(pubkeys[0]) << OP_CHECKSIG; |
||||||
|
expected.clear(); |
||||||
|
expected << OP_0 << ToByteVector(pubkeys[0].GetID()); |
||||||
|
result = GetScriptForWitness(witnessScript); |
||||||
|
BOOST_CHECK(result == expected); |
||||||
|
|
||||||
|
witnessScript.clear(); |
||||||
|
witnessScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
result = GetScriptForWitness(witnessScript); |
||||||
|
BOOST_CHECK(result == expected); |
||||||
|
|
||||||
|
witnessScript.clear(); |
||||||
|
witnessScript << OP_1 << ToByteVector(pubkeys[0]) << OP_1 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
uint256 scriptHash; |
||||||
|
CSHA256().Write(&witnessScript[0], witnessScript.size()) |
||||||
|
.Finalize(scriptHash.begin()); |
||||||
|
|
||||||
|
expected.clear(); |
||||||
|
expected << OP_0 << ToByteVector(scriptHash); |
||||||
|
result = GetScriptForWitness(witnessScript); |
||||||
|
BOOST_CHECK(result == expected); |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(script_standard_IsMine) |
||||||
|
{ |
||||||
|
CKey keys[2]; |
||||||
|
CPubKey pubkeys[2]; |
||||||
|
for (int i = 0; i < 2; i++) { |
||||||
|
keys[i].MakeNewKey(true); |
||||||
|
pubkeys[i] = keys[i].GetPubKey(); |
||||||
|
} |
||||||
|
|
||||||
|
CKey uncompressedKey; |
||||||
|
uncompressedKey.MakeNewKey(false); |
||||||
|
CPubKey uncompressedPubkey = uncompressedKey.GetPubKey(); |
||||||
|
|
||||||
|
CScript scriptPubKey; |
||||||
|
isminetype result; |
||||||
|
bool isInvalid; |
||||||
|
|
||||||
|
// P2PK compressed
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << ToByteVector(pubkeys[0]) << OP_CHECKSIG; |
||||||
|
|
||||||
|
// Keystore does not have key
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has key
|
||||||
|
keystore.AddKey(keys[0]); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2PK uncompressed
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << ToByteVector(uncompressedPubkey) << OP_CHECKSIG; |
||||||
|
|
||||||
|
// Keystore does not have key
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has key
|
||||||
|
keystore.AddKey(uncompressedKey); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2PKH compressed
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
|
||||||
|
// Keystore does not have key
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has key
|
||||||
|
keystore.AddKey(keys[0]); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2PKH uncompressed
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(uncompressedPubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
|
||||||
|
// Keystore does not have key
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has key
|
||||||
|
keystore.AddKey(uncompressedKey); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2SH
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
|
||||||
|
CScript redeemScript; |
||||||
|
redeemScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; |
||||||
|
|
||||||
|
// Keystore does not have redeemScript or key
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has redeemScript but no key
|
||||||
|
keystore.AddCScript(redeemScript); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has redeemScript and key
|
||||||
|
keystore.AddKey(keys[0]); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2WPKH compressed
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
keystore.AddKey(keys[0]); |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID()); |
||||||
|
|
||||||
|
// Keystore has key, but no P2SH redeemScript
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has key and P2SH redeemScript
|
||||||
|
keystore.AddCScript(scriptPubKey); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2WPKH uncompressed
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
keystore.AddKey(uncompressedKey); |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_0 << ToByteVector(uncompressedPubkey.GetID()); |
||||||
|
|
||||||
|
// Keystore has key, but no P2SH redeemScript
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has key and P2SH redeemScript
|
||||||
|
keystore.AddCScript(scriptPubKey); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// scriptPubKey multisig
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_2 << |
||||||
|
ToByteVector(uncompressedPubkey) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
// Keystore does not have any keys
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has 1/2 keys
|
||||||
|
keystore.AddKey(uncompressedKey); |
||||||
|
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has 2/2 keys
|
||||||
|
keystore.AddKey(keys[1]); |
||||||
|
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2SH multisig
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
keystore.AddKey(uncompressedKey); |
||||||
|
keystore.AddKey(keys[1]); |
||||||
|
|
||||||
|
CScript redeemScript; |
||||||
|
redeemScript << OP_2 << |
||||||
|
ToByteVector(uncompressedPubkey) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; |
||||||
|
|
||||||
|
// Keystore has no redeemScript
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has redeemScript
|
||||||
|
keystore.AddCScript(redeemScript); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2WSH multisig with compressed keys
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
keystore.AddKey(keys[0]); |
||||||
|
keystore.AddKey(keys[1]); |
||||||
|
|
||||||
|
CScript witnessScript; |
||||||
|
witnessScript << OP_2 << |
||||||
|
ToByteVector(pubkeys[0]) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
uint256 scriptHash; |
||||||
|
CSHA256().Write(&witnessScript[0], witnessScript.size()) |
||||||
|
.Finalize(scriptHash.begin()); |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_0 << ToByteVector(scriptHash); |
||||||
|
|
||||||
|
// Keystore has keys, but no witnessScript or P2SH redeemScript
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has keys and witnessScript, but no P2SH redeemScript
|
||||||
|
keystore.AddCScript(witnessScript); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||||
|
keystore.AddCScript(scriptPubKey); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2WSH multisig with uncompressed key
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
keystore.AddKey(uncompressedKey); |
||||||
|
keystore.AddKey(keys[1]); |
||||||
|
|
||||||
|
CScript witnessScript; |
||||||
|
witnessScript << OP_2 << |
||||||
|
ToByteVector(uncompressedPubkey) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
uint256 scriptHash; |
||||||
|
CSHA256().Write(&witnessScript[0], witnessScript.size()) |
||||||
|
.Finalize(scriptHash.begin()); |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_0 << ToByteVector(scriptHash); |
||||||
|
|
||||||
|
// Keystore has keys, but no witnessScript or P2SH redeemScript
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has keys and witnessScript, but no P2SH redeemScript
|
||||||
|
keystore.AddCScript(witnessScript); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||||
|
keystore.AddCScript(scriptPubKey); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// P2WSH multisig wrapped in P2SH
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
|
||||||
|
CScript witnessScript; |
||||||
|
witnessScript << OP_2 << |
||||||
|
ToByteVector(pubkeys[0]) << |
||||||
|
ToByteVector(pubkeys[1]) << |
||||||
|
OP_2 << OP_CHECKMULTISIG; |
||||||
|
|
||||||
|
uint256 scriptHash; |
||||||
|
CSHA256().Write(&witnessScript[0], witnessScript.size()) |
||||||
|
.Finalize(scriptHash.begin()); |
||||||
|
|
||||||
|
CScript redeemScript; |
||||||
|
redeemScript << OP_0 << ToByteVector(scriptHash); |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; |
||||||
|
|
||||||
|
// Keystore has no witnessScript, P2SH redeemScript, or keys
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has witnessScript and P2SH redeemScript, but no keys
|
||||||
|
keystore.AddCScript(redeemScript); |
||||||
|
keystore.AddCScript(witnessScript); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
|
||||||
|
// Keystore has keys, witnessScript, P2SH redeemScript
|
||||||
|
keystore.AddKey(keys[0]); |
||||||
|
keystore.AddKey(keys[1]); |
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// OP_RETURN
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
keystore.AddKey(keys[0]); |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]); |
||||||
|
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
|
||||||
|
// Nonstandard
|
||||||
|
{ |
||||||
|
CBasicKeyStore keystore; |
||||||
|
keystore.AddKey(keys[0]); |
||||||
|
|
||||||
|
scriptPubKey.clear(); |
||||||
|
scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL; |
||||||
|
|
||||||
|
result = IsMine(keystore, scriptPubKey, isInvalid); |
||||||
|
BOOST_CHECK_EQUAL(result, ISMINE_NO); |
||||||
|
BOOST_CHECK(!isInvalid); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END() |
@ -1,15 +0,0 @@ |
|||||||
// Copyright (c) 2009-2016 The Bitcoin Core developers
|
|
||||||
// Distributed under the MIT software license, see the accompanying
|
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
||||||
|
|
||||||
#include "testutil.h" |
|
||||||
|
|
||||||
#ifdef WIN32 |
|
||||||
#include <shlobj.h> |
|
||||||
#endif |
|
||||||
|
|
||||||
#include "fs.h" |
|
||||||
|
|
||||||
fs::path GetTempPath() { |
|
||||||
return fs::temp_directory_path(); |
|
||||||
} |
|
@ -1,15 +0,0 @@ |
|||||||
// Copyright (c) 2009-2016 The Bitcoin Core developers
|
|
||||||
// Distributed under the MIT software license, see the accompanying
|
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility functions shared by unit tests |
|
||||||
*/ |
|
||||||
#ifndef BITCOIN_TEST_TESTUTIL_H |
|
||||||
#define BITCOIN_TEST_TESTUTIL_H |
|
||||||
|
|
||||||
#include "fs.h" |
|
||||||
|
|
||||||
fs::path GetTempPath(); |
|
||||||
|
|
||||||
#endif // BITCOIN_TEST_TESTUTIL_H
|
|
@ -0,0 +1,25 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
# Copyright (c) 2017 The Bitcoin Core developers |
||||||
|
# Distributed under the MIT software license, see the accompanying |
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
||||||
|
"""Test bitcoin-cli""" |
||||||
|
from test_framework.test_framework import BitcoinTestFramework |
||||||
|
from test_framework.util import assert_equal |
||||||
|
|
||||||
|
class TestBitcoinCli(BitcoinTestFramework): |
||||||
|
|
||||||
|
def set_test_params(self): |
||||||
|
self.setup_clean_chain = True |
||||||
|
self.num_nodes = 1 |
||||||
|
|
||||||
|
def run_test(self): |
||||||
|
"""Main test logic""" |
||||||
|
|
||||||
|
self.log.info("Compare responses from getinfo RPC and `bitcoin-cli getinfo`") |
||||||
|
cli_get_info = self.nodes[0].cli.getinfo() |
||||||
|
rpc_get_info = self.nodes[0].getinfo() |
||||||
|
|
||||||
|
assert_equal(cli_get_info, rpc_get_info) |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
TestBitcoinCli().main() |
@ -0,0 +1,190 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
# Copyright (c) 2017 The Bitcoin Core developers |
||||||
|
# Distributed under the MIT software license, see the accompanying |
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
||||||
|
"""Class for bitcoind node under test""" |
||||||
|
|
||||||
|
import decimal |
||||||
|
import errno |
||||||
|
import http.client |
||||||
|
import json |
||||||
|
import logging |
||||||
|
import os |
||||||
|
import subprocess |
||||||
|
import time |
||||||
|
|
||||||
|
from .util import ( |
||||||
|
assert_equal, |
||||||
|
get_rpc_proxy, |
||||||
|
rpc_url, |
||||||
|
wait_until, |
||||||
|
) |
||||||
|
from .authproxy import JSONRPCException |
||||||
|
|
||||||
|
BITCOIND_PROC_WAIT_TIMEOUT = 60 |
||||||
|
|
||||||
|
class TestNode(): |
||||||
|
"""A class for representing a bitcoind node under test. |
||||||
|
|
||||||
|
This class contains: |
||||||
|
|
||||||
|
- state about the node (whether it's running, etc) |
||||||
|
- a Python subprocess.Popen object representing the running process |
||||||
|
- an RPC connection to the node |
||||||
|
|
||||||
|
To make things easier for the test writer, a bit of magic is happening under the covers. |
||||||
|
Any unrecognised messages will be dispatched to the RPC connection.""" |
||||||
|
|
||||||
|
def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir): |
||||||
|
self.index = i |
||||||
|
self.datadir = os.path.join(dirname, "node" + str(i)) |
||||||
|
self.rpchost = rpchost |
||||||
|
if timewait: |
||||||
|
self.rpc_timeout = timewait |
||||||
|
else: |
||||||
|
# Wait for up to 60 seconds for the RPC server to respond |
||||||
|
self.rpc_timeout = 60 |
||||||
|
if binary is None: |
||||||
|
self.binary = os.getenv("BITCOIND", "bitcoind") |
||||||
|
else: |
||||||
|
self.binary = binary |
||||||
|
self.stderr = stderr |
||||||
|
self.coverage_dir = coverage_dir |
||||||
|
# Most callers will just need to add extra args to the standard list below. For those callers that need more flexibity, they can just set the args property directly. |
||||||
|
self.extra_args = extra_args |
||||||
|
self.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i] |
||||||
|
|
||||||
|
self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir) |
||||||
|
|
||||||
|
self.running = False |
||||||
|
self.process = None |
||||||
|
self.rpc_connected = False |
||||||
|
self.rpc = None |
||||||
|
self.url = None |
||||||
|
self.log = logging.getLogger('TestFramework.node%d' % i) |
||||||
|
|
||||||
|
def __getattr__(self, *args, **kwargs): |
||||||
|
"""Dispatches any unrecognised messages to the RPC connection.""" |
||||||
|
assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" |
||||||
|
return self.rpc.__getattr__(*args, **kwargs) |
||||||
|
|
||||||
|
def start(self, extra_args=None, stderr=None): |
||||||
|
"""Start the node.""" |
||||||
|
if extra_args is None: |
||||||
|
extra_args = self.extra_args |
||||||
|
if stderr is None: |
||||||
|
stderr = self.stderr |
||||||
|
self.process = subprocess.Popen(self.args + extra_args, stderr=stderr) |
||||||
|
self.running = True |
||||||
|
self.log.debug("bitcoind started, waiting for RPC to come up") |
||||||
|
|
||||||
|
def wait_for_rpc_connection(self): |
||||||
|
"""Sets up an RPC connection to the bitcoind process. Returns False if unable to connect.""" |
||||||
|
# Poll at a rate of four times per second |
||||||
|
poll_per_s = 4 |
||||||
|
for _ in range(poll_per_s * self.rpc_timeout): |
||||||
|
assert self.process.poll() is None, "bitcoind exited with status %i during initialization" % self.process.returncode |
||||||
|
try: |
||||||
|
self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir) |
||||||
|
self.rpc.getblockcount() |
||||||
|
# If the call to getblockcount() succeeds then the RPC connection is up |
||||||
|
self.rpc_connected = True |
||||||
|
self.url = self.rpc.url |
||||||
|
self.log.debug("RPC successfully started") |
||||||
|
return |
||||||
|
except IOError as e: |
||||||
|
if e.errno != errno.ECONNREFUSED: # Port not yet open? |
||||||
|
raise # unknown IO error |
||||||
|
except JSONRPCException as e: # Initialization phase |
||||||
|
if e.error['code'] != -28: # RPC in warmup? |
||||||
|
raise # unknown JSON RPC exception |
||||||
|
except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting |
||||||
|
if "No RPC credentials" not in str(e): |
||||||
|
raise |
||||||
|
time.sleep(1.0 / poll_per_s) |
||||||
|
raise AssertionError("Unable to connect to bitcoind") |
||||||
|
|
||||||
|
def get_wallet_rpc(self, wallet_name): |
||||||
|
assert self.rpc_connected |
||||||
|
assert self.rpc |
||||||
|
wallet_path = "wallet/%s" % wallet_name |
||||||
|
return self.rpc / wallet_path |
||||||
|
|
||||||
|
def stop_node(self): |
||||||
|
"""Stop the node.""" |
||||||
|
if not self.running: |
||||||
|
return |
||||||
|
self.log.debug("Stopping node") |
||||||
|
try: |
||||||
|
self.stop() |
||||||
|
except http.client.CannotSendRequest: |
||||||
|
self.log.exception("Unable to stop node.") |
||||||
|
|
||||||
|
def is_node_stopped(self): |
||||||
|
"""Checks whether the node has stopped. |
||||||
|
|
||||||
|
Returns True if the node has stopped. False otherwise. |
||||||
|
This method is responsible for freeing resources (self.process).""" |
||||||
|
if not self.running: |
||||||
|
return True |
||||||
|
return_code = self.process.poll() |
||||||
|
if return_code is None: |
||||||
|
return False |
||||||
|
|
||||||
|
# process has stopped. Assert that it didn't return an error code. |
||||||
|
assert_equal(return_code, 0) |
||||||
|
self.running = False |
||||||
|
self.process = None |
||||||
|
self.rpc_connected = False |
||||||
|
self.rpc = None |
||||||
|
self.log.debug("Node stopped") |
||||||
|
return True |
||||||
|
|
||||||
|
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT): |
||||||
|
wait_until(self.is_node_stopped, timeout=timeout) |
||||||
|
|
||||||
|
def node_encrypt_wallet(self, passphrase): |
||||||
|
""""Encrypts the wallet. |
||||||
|
|
||||||
|
This causes bitcoind to shutdown, so this method takes |
||||||
|
care of cleaning up resources.""" |
||||||
|
self.encryptwallet(passphrase) |
||||||
|
self.wait_until_stopped() |
||||||
|
|
||||||
|
class TestNodeCLI(): |
||||||
|
"""Interface to bitcoin-cli for an individual node""" |
||||||
|
|
||||||
|
def __init__(self, binary, datadir): |
||||||
|
self.args = [] |
||||||
|
self.binary = binary |
||||||
|
self.datadir = datadir |
||||||
|
self.input = None |
||||||
|
|
||||||
|
def __call__(self, *args, input=None): |
||||||
|
# TestNodeCLI is callable with bitcoin-cli command-line args |
||||||
|
self.args = [str(arg) for arg in args] |
||||||
|
self.input = input |
||||||
|
return self |
||||||
|
|
||||||
|
def __getattr__(self, command): |
||||||
|
def dispatcher(*args, **kwargs): |
||||||
|
return self.send_cli(command, *args, **kwargs) |
||||||
|
return dispatcher |
||||||
|
|
||||||
|
def send_cli(self, command, *args, **kwargs): |
||||||
|
"""Run bitcoin-cli command. Deserializes returned string as python object.""" |
||||||
|
|
||||||
|
pos_args = [str(arg) for arg in args] |
||||||
|
named_args = [str(key) + "=" + str(value) for (key, value) in kwargs.items()] |
||||||
|
assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call" |
||||||
|
p_args = [self.binary, "-datadir=" + self.datadir] + self.args |
||||||
|
if named_args: |
||||||
|
p_args += ["-named"] |
||||||
|
p_args += [command] + pos_args + named_args |
||||||
|
process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) |
||||||
|
cli_stdout, cli_stderr = process.communicate(input=self.input) |
||||||
|
returncode = process.poll() |
||||||
|
if returncode: |
||||||
|
# Ignore cli_stdout, raise with cli_stderr |
||||||
|
raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr) |
||||||
|
return json.loads(cli_stdout, parse_float=decimal.Decimal) |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue