mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
Merge #6152: backport: bitcoin#20459, #20842, #21557, #21840, #21867, #21897, #21900, #21945, #22048, #22057
b73f48f3b9
Merge bitcoin/bitcoin#22057: test: use MiniWallet (P2PK mode) for feature_dersig.py (MarcoFalke)d5a8d5e6a0
Merge bitcoin/bitcoin#22048: test: MiniWallet: introduce enum type for output mode (MarcoFalke)f4cd20b115
Merge bitcoin/bitcoin#21945: test: add P2PK support to MiniWallet (MarcoFalke)7be6db6dca
docs: add an explanation for vsize in MiniWallet (Konstantin Akimov)5d10b41302
Merge bitcoin/bitcoin#21840: test: Misc refactor to get rid of &foo[0] raw pointers (MarcoFalke)7522ee9868
Merge bitcoin/bitcoin#21900: test: use MiniWallet for feature_csv_activation.py (MarcoFalke)c6f603c26f
Merge bitcoin/bitcoin#21897: rpc: adjust incorrect RPCHelpMan types (MarcoFalke)1dffe3ab9f
Merge bitcoin/bitcoin#21867: test: use MiniWallet for p2p_blocksonly.py (MarcoFalke)81d21eea14
Merge #21557: test: small cleanup in RPCNestedTests tests (MarcoFalke)cc169c2457
partial Merge #20842: docs: consolidate typo & url fixing (MarcoFalke)2be1604405
Merge #20459: rpc: Fail to return undocumented return values (MarcoFalke) Pull request description: ## What was done? Backports from v22 bitcoin. Mostly related to MiniWallet and RPC improvements, see commits ## How Has This Been Tested? Run unit/functional tests ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone ACKs for top commit: UdjinM6: utACKb73f48f3b9
PastaPastaPasta: utACKb73f48f3b9
Tree-SHA512: 588f3a30697c0d77dadcc463aba71a00bf26eeef41b0cb8b9197799a217ebeb1d1ce7b5021ccc4576f0e9ca0e75ad840820cdc682fe8f120596788a528727a0b
This commit is contained in:
commit
28e20b31eb
@ -2,7 +2,7 @@ commit 7b6eb33ecd88768b28c67ce5d2d68a7eed5936b6
|
||||
Author: fanquake <fanquake@gmail.com>
|
||||
Date: Tue Aug 25 14:34:53 2020 +0800
|
||||
|
||||
Remove rule that causes inadvertant header regeneration
|
||||
Remove rule that causes inadvertent header regeneration
|
||||
|
||||
Otherwise the makefile will needlessly attempt to re-generate the
|
||||
headers with gperf. This can be dropped once the upstream build is fixed.
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <consensus/validation.h>
|
||||
#include <script/standard.h>
|
||||
#include <test/util/mining.h>
|
||||
#include <test/util/script.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <test/util/wallet.h>
|
||||
#include <txmempool.h>
|
||||
@ -31,7 +32,7 @@ static void AssembleBlock(benchmark::Bench& bench)
|
||||
std::array<CTransactionRef, NUM_BLOCKS - COINBASE_MATURITY + 1> txs;
|
||||
for (size_t b{0}; b < NUM_BLOCKS; ++b) {
|
||||
CMutableTransaction tx;
|
||||
tx.vin.push_back(MineBlock(test_setup->m_node, SCRIPT_PUB));
|
||||
tx.vin.push_back(MineBlock(test_setup->m_node, P2SH_OP_TRUE));
|
||||
tx.vin.back().scriptSig = scriptSig;
|
||||
tx.vout.emplace_back(1337, SCRIPT_PUB);
|
||||
if (NUM_BLOCKS - b >= COINBASE_MATURITY)
|
||||
@ -47,7 +48,7 @@ static void AssembleBlock(benchmark::Bench& bench)
|
||||
}
|
||||
|
||||
bench.minEpochIterations(700).run([&] {
|
||||
PrepareBlock(test_setup->m_node, SCRIPT_PUB);
|
||||
PrepareBlock(test_setup->m_node, P2SH_OP_TRUE);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
|
||||
/**
|
||||
* Parse a hex string into 256 bits
|
||||
* @param[in] strHex a hex-formatted, 64-character string
|
||||
* @param[out] result the result of the parasing
|
||||
* @param[out] result the result of the parsing
|
||||
* @returns true if successful, false if not
|
||||
*
|
||||
* @see ParseHashV for an RPC-oriented version of this
|
||||
|
@ -73,7 +73,7 @@ uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::ve
|
||||
//if we do not have this assert, we can hit a memory access violation when indexing into vTxid
|
||||
assert(vTxid.size() != 0);
|
||||
if (height == 0) {
|
||||
// hash at height 0 is the txids themself
|
||||
// hash at height 0 is the txids themselves
|
||||
return vTxid[pos];
|
||||
} else {
|
||||
// calculate left hash
|
||||
|
@ -85,7 +85,7 @@ static void RegisterMetaTypes()
|
||||
#ifdef ENABLE_WALLET
|
||||
qRegisterMetaType<WalletModel*>();
|
||||
#endif
|
||||
// Register typedefs (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
|
||||
// Register typedefs (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType)
|
||||
// IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
|
||||
qRegisterMetaType<CAmount>("CAmount");
|
||||
qRegisterMetaType<size_t>("size_t");
|
||||
|
@ -11,10 +11,10 @@
|
||||
#include <univalue.h>
|
||||
#include <util/system.h>
|
||||
|
||||
#include <llmq/context.h>
|
||||
#include <QTest>
|
||||
|
||||
#include <QDir>
|
||||
#include <QtGlobal>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
static RPCHelpMan rpcNestedTest_rpc()
|
||||
{
|
||||
@ -26,23 +26,25 @@ static RPCHelpMan rpcNestedTest_rpc()
|
||||
{"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
|
||||
{"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""},
|
||||
},
|
||||
{},
|
||||
RPCResult{RPCResult::Type::ANY, "", ""},
|
||||
RPCExamples{""},
|
||||
[](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
|
||||
return request.params.write(0, 0);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static const CRPCCommand vRPCCommands[] = {
|
||||
{"test", &rpcNestedTest_rpc},
|
||||
{"rpcNestedTest", &rpcNestedTest_rpc},
|
||||
};
|
||||
|
||||
void RPCNestedTests::rpcNestedTests()
|
||||
{
|
||||
// do some test setup
|
||||
// could be moved to a more generic place when we add more tests on QT level
|
||||
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
|
||||
for (const auto& c : vRPCCommands) {
|
||||
tableRPC.appendCommand(c.name, &c);
|
||||
}
|
||||
|
||||
TestingSetup test;
|
||||
m_node.setContext(&test.m_node);
|
||||
@ -70,13 +72,13 @@ void RPCNestedTests::rpcNestedTests()
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo "); //whitespace at the end will be tolerated
|
||||
QVERIFY(result.substr(0,1) == "{");
|
||||
|
||||
(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child containing the quotes in the key
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()[\"chain\"]"); //Quote path identifier are allowed, but look after a child containing the quotes in the key
|
||||
QVERIFY(result == "null");
|
||||
|
||||
(RPCConsole::RPCExecuteCommandLine(m_node, result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed
|
||||
(RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result, "createrawtransaction [] {} 0"); //parameter not in brackets are allowed
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction([],{},0)"); //parameter in brackets are allowed
|
||||
QVERIFY(result == result2);
|
||||
(RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parameters is allowed
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result2, "createrawtransaction( [], {} , 0 )"); //whitespace between parameters is allowed
|
||||
QVERIFY(result == result2);
|
||||
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result, "getblock(getbestblockhash())[tx][0]", &filtered);
|
||||
@ -125,14 +127,13 @@ void RPCNestedTests::rpcNestedTests()
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest( abc , cba )");
|
||||
QVERIFY(result == "[\"abc\",\"cba\"]");
|
||||
|
||||
// do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3)
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax
|
||||
(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments
|
||||
(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()()()")); //tolerate non command brackts
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo("); //tolerate non closing brackets if we have no arguments
|
||||
RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo()()()"); //tolerate non command brackets
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "getblockchaininfo(True)"), UniValue); //invalid argument
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "a(getblockchaininfo(True))"), UniValue); //method not found
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using ,
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using ,
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using ,
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tolerate empty arguments when using ,
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tolerate empty arguments when using ,
|
||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(m_node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tolerate empty arguments when using ,
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
|
||||
|
||||
connect(wallet_model, &WalletModel::unload, this, [this, wallet_model] {
|
||||
// Defer removeAndDeleteWallet when no modal widget is active.
|
||||
// TODO: remove this workaround by removing usage of QDiallog::exec.
|
||||
// TODO: remove this workaround by removing usage of QDialog::exec.
|
||||
if (QApplication::activeModalWidget()) {
|
||||
connect(qApp, &QApplication::focusWindowChanged, wallet_model, [this, wallet_model]() {
|
||||
if (!QApplication::activeModalWidget()) {
|
||||
|
@ -1392,7 +1392,7 @@ static RPCHelpMan echo(const std::string& name)
|
||||
{"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
{"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, ""},
|
||||
},
|
||||
RPCResult{RPCResult::Type::NONE, "", "Returns whatever was passed in"},
|
||||
RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"},
|
||||
RPCExamples{""},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
|
@ -753,7 +753,7 @@ static RPCHelpMan createrawtransaction()
|
||||
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
|
||||
" accepted as second parameter.",
|
||||
{
|
||||
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
|
||||
{"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "",
|
||||
{
|
||||
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the Dash address, the value (float or string) is the amount in " + CURRENCY_UNIT},
|
||||
},
|
||||
|
@ -171,8 +171,9 @@ static RPCHelpMan help()
|
||||
{"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"},
|
||||
{"subcommand", RPCArg::Type::STR, /* default */ "all subcommands", "The subcommand to get help on."},
|
||||
},
|
||||
RPCResult{
|
||||
RPCResult::Type::STR, "", "The help text"
|
||||
{
|
||||
RPCResult{RPCResult::Type::STR, "", "The help text"},
|
||||
RPCResult{RPCResult::Type::ANY, "", ""},
|
||||
},
|
||||
RPCExamples{""},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
|
||||
|
@ -476,6 +476,7 @@ std::string RPCResults::ToDescriptionString() const
|
||||
{
|
||||
std::string result;
|
||||
for (const auto& r : m_results) {
|
||||
if (r.m_type == RPCResult::Type::ANY) continue; // for testing only
|
||||
if (r.m_cond.empty()) {
|
||||
result += "\nResult:\n";
|
||||
} else {
|
||||
@ -493,7 +494,7 @@ std::string RPCExamples::ToDescriptionString() const
|
||||
return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
|
||||
}
|
||||
|
||||
UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request)
|
||||
UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
|
||||
{
|
||||
if (request.mode == JSONRPCRequest::GET_ARGS) {
|
||||
return GetArgMap();
|
||||
@ -710,6 +711,9 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
|
||||
sections.PushSection({indent + "..." + maybe_separator, m_description});
|
||||
return;
|
||||
}
|
||||
case Type::ANY: {
|
||||
CHECK_NONFATAL(false); // Only for testing
|
||||
}
|
||||
case Type::NONE: {
|
||||
sections.PushSection({indent + "null" + maybe_separator, Description("json null")});
|
||||
return;
|
||||
@ -775,6 +779,42 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
|
||||
CHECK_NONFATAL(false);
|
||||
}
|
||||
|
||||
bool RPCResult::MatchesType(const UniValue& result) const
|
||||
{
|
||||
switch (m_type) {
|
||||
case Type::ELISION: {
|
||||
return false;
|
||||
}
|
||||
case Type::ANY: {
|
||||
return true;
|
||||
}
|
||||
case Type::NONE: {
|
||||
return UniValue::VNULL == result.getType();
|
||||
}
|
||||
case Type::STR:
|
||||
case Type::STR_HEX: {
|
||||
return UniValue::VSTR == result.getType();
|
||||
}
|
||||
case Type::NUM:
|
||||
case Type::STR_AMOUNT:
|
||||
case Type::NUM_TIME: {
|
||||
return UniValue::VNUM == result.getType();
|
||||
}
|
||||
case Type::BOOL: {
|
||||
return UniValue::VBOOL == result.getType();
|
||||
}
|
||||
case Type::ARR_FIXED:
|
||||
case Type::ARR: {
|
||||
return UniValue::VARR == result.getType();
|
||||
}
|
||||
case Type::OBJ_DYN:
|
||||
case Type::OBJ: {
|
||||
return UniValue::VOBJ == result.getType();
|
||||
}
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
CHECK_NONFATAL(false);
|
||||
}
|
||||
|
||||
std::string RPCArg::ToStringObj(const bool oneline) const
|
||||
{
|
||||
std::string res;
|
||||
|
@ -174,7 +174,7 @@ struct RPCArg {
|
||||
m_oneline_description{std::move(oneline_description)},
|
||||
m_type_str{std::move(type_str)}
|
||||
{
|
||||
CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ);
|
||||
CHECK_NONFATAL(type != Type::ARR && type != Type::OBJ && type != Type::OBJ_USER_KEYS);
|
||||
}
|
||||
|
||||
RPCArg(
|
||||
@ -194,7 +194,7 @@ struct RPCArg {
|
||||
m_oneline_description{std::move(oneline_description)},
|
||||
m_type_str{std::move(type_str)}
|
||||
{
|
||||
CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ);
|
||||
CHECK_NONFATAL(type == Type::ARR || type == Type::OBJ || type == Type::OBJ_USER_KEYS);
|
||||
}
|
||||
|
||||
bool IsOptional() const;
|
||||
@ -230,6 +230,7 @@ struct RPCResult {
|
||||
NUM,
|
||||
BOOL,
|
||||
NONE,
|
||||
ANY, //!< Special type to disable type checks (for testing only)
|
||||
STR_AMOUNT, //!< Special string to represent a floating point amount
|
||||
STR_HEX, //!< Special string with only hex chars
|
||||
OBJ_DYN, //!< Special dictionary with keys that are not literals
|
||||
@ -302,6 +303,8 @@ struct RPCResult {
|
||||
std::string ToStringObj() const;
|
||||
/** Return the description string, including the result type. */
|
||||
std::string ToDescriptionString() const;
|
||||
/** Check whether the result JSON type matches. */
|
||||
bool MatchesType(const UniValue& result) const;
|
||||
};
|
||||
|
||||
struct RPCResults {
|
||||
@ -340,7 +343,7 @@ public:
|
||||
using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
|
||||
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun);
|
||||
|
||||
UniValue HandleRequest(const JSONRPCRequest& request);
|
||||
UniValue HandleRequest(const JSONRPCRequest& request) const;
|
||||
std::string ToString() const;
|
||||
/** Return the named args that need to be converted from string to another JSON type */
|
||||
UniValue GetArgMap() const;
|
||||
|
@ -159,23 +159,20 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
|
||||
s.clear();
|
||||
s << ToByteVector(pubkey) << OP_CHECKSIG;
|
||||
BOOST_CHECK(ExtractDestination(s, address));
|
||||
BOOST_CHECK(std::get_if<PKHash>(&address) &&
|
||||
*std::get_if<PKHash>(&address) == PKHash(pubkey));
|
||||
BOOST_CHECK(std::get<PKHash>(address) == PKHash(pubkey));
|
||||
|
||||
// TxoutType::PUBKEYHASH
|
||||
s.clear();
|
||||
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
BOOST_CHECK(ExtractDestination(s, address));
|
||||
BOOST_CHECK(std::get_if<PKHash>(&address) &&
|
||||
*std::get_if<PKHash>(&address) == PKHash(pubkey));
|
||||
BOOST_CHECK(std::get<PKHash>(address) == PKHash(pubkey));
|
||||
|
||||
// TxoutType::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(std::get_if<ScriptHash>(&address) &&
|
||||
*std::get_if<ScriptHash>(&address) == ScriptHash(redeemScript));
|
||||
BOOST_CHECK(std::get<ScriptHash>(address) == ScriptHash(redeemScript));
|
||||
|
||||
// TxoutType::MULTISIG
|
||||
s.clear();
|
||||
@ -209,8 +206,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
||||
BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY);
|
||||
BOOST_CHECK_EQUAL(addresses.size(), 1U);
|
||||
BOOST_CHECK_EQUAL(nRequired, 1);
|
||||
BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) &&
|
||||
*std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
|
||||
BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
|
||||
|
||||
// TxoutType::PUBKEYHASH
|
||||
s.clear();
|
||||
@ -219,8 +215,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
||||
BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH);
|
||||
BOOST_CHECK_EQUAL(addresses.size(), 1U);
|
||||
BOOST_CHECK_EQUAL(nRequired, 1);
|
||||
BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) &&
|
||||
*std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
|
||||
BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
|
||||
|
||||
// TxoutType::SCRIPTHASH
|
||||
CScript redeemScript(s); // initialize with leftover P2PKH script
|
||||
@ -230,8 +225,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
||||
BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH);
|
||||
BOOST_CHECK_EQUAL(addresses.size(), 1U);
|
||||
BOOST_CHECK_EQUAL(nRequired, 1);
|
||||
BOOST_CHECK(std::get_if<ScriptHash>(&addresses[0]) &&
|
||||
*std::get_if<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript));
|
||||
BOOST_CHECK(std::get<ScriptHash>(addresses[0]) == ScriptHash(redeemScript));
|
||||
|
||||
// TxoutType::MULTISIG
|
||||
s.clear();
|
||||
@ -243,10 +237,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
|
||||
BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG);
|
||||
BOOST_CHECK_EQUAL(addresses.size(), 2U);
|
||||
BOOST_CHECK_EQUAL(nRequired, 2);
|
||||
BOOST_CHECK(std::get_if<PKHash>(&addresses[0]) &&
|
||||
*std::get_if<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
|
||||
BOOST_CHECK(std::get_if<PKHash>(&addresses[1]) &&
|
||||
*std::get_if<PKHash>(&addresses[1]) == PKHash(pubkeys[1]));
|
||||
BOOST_CHECK(std::get<PKHash>(addresses[0]) == PKHash(pubkeys[0]));
|
||||
BOOST_CHECK(std::get<PKHash>(addresses[1]) == PKHash(pubkeys[1]));
|
||||
|
||||
// TxoutType::NULL_DATA
|
||||
s.clear();
|
||||
|
@ -535,7 +535,7 @@ static RPCHelpMan listaddressgroupings()
|
||||
{
|
||||
{RPCResult::Type::ARR, "", "",
|
||||
{
|
||||
{RPCResult::Type::ARR, "", "",
|
||||
{RPCResult::Type::ARR_FIXED, "", "",
|
||||
{
|
||||
{RPCResult::Type::STR, "address", "The Dash address"},
|
||||
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
explicit SQLiteBatch(SQLiteDatabase& database);
|
||||
~SQLiteBatch() override { Close(); }
|
||||
|
||||
/* No-op. See commeng on SQLiteDatabase::Flush */
|
||||
/* No-op. See comment on SQLiteDatabase::Flush */
|
||||
void Flush() override {}
|
||||
|
||||
void Close() override;
|
||||
|
@ -487,7 +487,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
||||
uint256 checksum;
|
||||
ssValue >> checksum;
|
||||
if (!(checksum_valid = Hash(vchPrivKey) == checksum)) {
|
||||
strErr = "Error reading wallet database: Crypted key corrupt";
|
||||
strErr = "Error reading wallet database: Encrypted key corrupt";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,10 @@ from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
from test_framework.wallet import MiniWallet
|
||||
from test_framework.wallet import (
|
||||
MiniWallet,
|
||||
MiniWalletMode,
|
||||
)
|
||||
|
||||
CLTV_HEIGHT = 1351
|
||||
|
||||
@ -101,7 +104,7 @@ class BIP65Test(BitcoinTestFramework):
|
||||
|
||||
def run_test(self):
|
||||
peer = self.nodes[0].add_p2p_connection(P2PInterface())
|
||||
wallet = MiniWallet(self.nodes[0], raw_script=True)
|
||||
wallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_OP_TRUE)
|
||||
|
||||
self.test_cltv_info(is_active=False)
|
||||
|
||||
|
@ -37,12 +37,13 @@ bip112txs_vary_OP_CSV_9 - 16 txs with nSequence = 9 evaluated against varying {r
|
||||
bip112tx_special - test negative argument to OP_CSV
|
||||
bip112tx_emptystack - test empty stack (= no argument) OP_CSV
|
||||
"""
|
||||
from decimal import Decimal
|
||||
from itertools import product
|
||||
from io import BytesIO
|
||||
|
||||
from test_framework.blocktools import create_coinbase, create_block, create_transaction, TIME_GENESIS_BLOCK
|
||||
from test_framework.messages import CTransaction
|
||||
from test_framework.blocktools import (
|
||||
create_block,
|
||||
create_coinbase,
|
||||
TIME_GENESIS_BLOCK,
|
||||
)
|
||||
from test_framework.p2p import P2PDataStore
|
||||
from test_framework.script import (
|
||||
CScript,
|
||||
@ -52,9 +53,12 @@ from test_framework.script import (
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
hex_str_to_bytes,
|
||||
softfork_active,
|
||||
)
|
||||
from test_framework.wallet import (
|
||||
MiniWallet,
|
||||
MiniWalletMode,
|
||||
)
|
||||
|
||||
TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above)
|
||||
COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs
|
||||
@ -82,66 +86,6 @@ def relative_locktime(sdf, srhb, stf, srlb):
|
||||
def all_rlt_txs(txs):
|
||||
return [tx['tx'] for tx in txs]
|
||||
|
||||
def sign_transaction(node, unsignedtx):
|
||||
rawtx = unsignedtx.serialize().hex()
|
||||
signresult = node.signrawtransactionwithwallet(rawtx)
|
||||
tx = CTransaction()
|
||||
f = BytesIO(hex_str_to_bytes(signresult['hex']))
|
||||
tx.deserialize(f)
|
||||
return tx
|
||||
|
||||
def create_bip112special(node, input, txversion, address):
|
||||
tx = create_transaction(node, input, address, amount=Decimal("499.98"))
|
||||
tx.nVersion = txversion
|
||||
signtx = sign_transaction(node, tx)
|
||||
signtx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
|
||||
return signtx
|
||||
|
||||
def create_bip112emptystack(node, input, txversion, address):
|
||||
tx = create_transaction(node, input, address, amount=Decimal("499.98"))
|
||||
tx.nVersion = txversion
|
||||
signtx = sign_transaction(node, tx)
|
||||
signtx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(signtx.vin[0].scriptSig)))
|
||||
return signtx
|
||||
|
||||
def send_generic_input_tx(node, coinbases, address):
|
||||
return node.sendrawtransaction(sign_transaction(node, create_transaction(node, node.getblock(coinbases.pop())['tx'][0], address, amount=Decimal("499.99"))).serialize().hex())
|
||||
|
||||
def create_bip68txs(node, bip68inputs, txversion, address, locktime_delta=0):
|
||||
"""Returns a list of bip68 transactions with different bits set."""
|
||||
txs = []
|
||||
assert len(bip68inputs) >= 16
|
||||
for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
|
||||
locktime = relative_locktime(sdf, srhb, stf, srlb)
|
||||
tx = create_transaction(node, bip68inputs[i], address, amount=Decimal("499.98"))
|
||||
tx.nVersion = txversion
|
||||
tx.vin[0].nSequence = locktime + locktime_delta
|
||||
tx = sign_transaction(node, tx)
|
||||
tx.rehash()
|
||||
txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
|
||||
|
||||
return txs
|
||||
|
||||
def create_bip112txs(node, bip112inputs, varyOP_CSV, txversion, address, locktime_delta=0):
|
||||
"""Returns a list of bip68 transactions with different bits set."""
|
||||
txs = []
|
||||
assert len(bip112inputs) >= 16
|
||||
for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
|
||||
locktime = relative_locktime(sdf, srhb, stf, srlb)
|
||||
tx = create_transaction(node, bip112inputs[i], address, amount=Decimal("499.98"))
|
||||
if (varyOP_CSV): # if varying OP_CSV, nSequence is fixed
|
||||
tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME + locktime_delta
|
||||
else: # vary nSequence instead, OP_CSV is fixed
|
||||
tx.vin[0].nSequence = locktime + locktime_delta
|
||||
tx.nVersion = txversion
|
||||
signtx = sign_transaction(node, tx)
|
||||
if (varyOP_CSV):
|
||||
signtx.vin[0].scriptSig = CScript([locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
|
||||
else:
|
||||
signtx.vin[0].scriptSig = CScript([BASE_RELATIVE_LOCKTIME, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
|
||||
tx.rehash()
|
||||
txs.append({'tx': signtx, 'sdf': sdf, 'stf': stf})
|
||||
return txs
|
||||
|
||||
class BIP68_112_113Test(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
@ -159,8 +103,65 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
def setup_network(self):
|
||||
self.setup_nodes()
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
def create_self_transfer_from_utxo(self, input_tx):
|
||||
utxo = self.miniwallet.get_utxo(txid=input_tx.rehash(), mark_as_spent=False)
|
||||
tx = self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo)['tx']
|
||||
return tx
|
||||
|
||||
def create_bip112special(self, input, txversion):
|
||||
tx = self.create_self_transfer_from_utxo(input)
|
||||
tx.nVersion = txversion
|
||||
self.miniwallet.sign_tx(tx)
|
||||
tx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
|
||||
return tx
|
||||
|
||||
def create_bip112emptystack(self, input, txversion):
|
||||
tx = self.create_self_transfer_from_utxo(input)
|
||||
tx.nVersion = txversion
|
||||
self.miniwallet.sign_tx(tx)
|
||||
tx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(tx.vin[0].scriptSig)))
|
||||
return tx
|
||||
|
||||
def send_generic_input_tx(self, coinbases):
|
||||
input_txid = self.nodes[0].getblock(coinbases.pop(), 2)['tx'][0]['txid']
|
||||
utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid)
|
||||
return self.miniwallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['tx']
|
||||
|
||||
def create_bip68txs(self, bip68inputs, txversion, locktime_delta=0):
|
||||
"""Returns a list of bip68 transactions with different bits set."""
|
||||
txs = []
|
||||
assert len(bip68inputs) >= 16
|
||||
for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
|
||||
locktime = relative_locktime(sdf, srhb, stf, srlb)
|
||||
tx = self.create_self_transfer_from_utxo(bip68inputs[i])
|
||||
tx.nVersion = txversion
|
||||
tx.vin[0].nSequence = locktime + locktime_delta
|
||||
self.miniwallet.sign_tx(tx)
|
||||
tx.rehash()
|
||||
txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
|
||||
|
||||
return txs
|
||||
|
||||
def create_bip112txs(self, bip112inputs, varyOP_CSV, txversion, locktime_delta=0):
|
||||
"""Returns a list of bip68 transactions with different bits set."""
|
||||
txs = []
|
||||
assert len(bip112inputs) >= 16
|
||||
for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)):
|
||||
locktime = relative_locktime(sdf, srhb, stf, srlb)
|
||||
tx = self.create_self_transfer_from_utxo(bip112inputs[i])
|
||||
if (varyOP_CSV): # if varying OP_CSV, nSequence is fixed
|
||||
tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME + locktime_delta
|
||||
else: # vary nSequence instead, OP_CSV is fixed
|
||||
tx.vin[0].nSequence = locktime + locktime_delta
|
||||
tx.nVersion = txversion
|
||||
self.miniwallet.sign_tx(tx)
|
||||
if (varyOP_CSV):
|
||||
tx.vin[0].scriptSig = CScript([locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
|
||||
else:
|
||||
tx.vin[0].scriptSig = CScript([BASE_RELATIVE_LOCKTIME, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
|
||||
tx.rehash()
|
||||
txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
|
||||
return txs
|
||||
|
||||
def generate_blocks(self, number):
|
||||
test_blocks = []
|
||||
@ -189,16 +190,16 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
|
||||
def run_test(self):
|
||||
self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
|
||||
self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK)
|
||||
|
||||
self.log.info("Generate blocks in the past for coinbase outputs.")
|
||||
self.coinbase_blocks = self.nodes[0].generate(COINBASE_BLOCK_COUNT) # blocks generated for inputs
|
||||
self.coinbase_blocks = self.miniwallet.generate(COINBASE_BLOCK_COUNT) # blocks generated for inputs
|
||||
# set time so that there was enough time to build up to 1000 blocks 10 minutes apart on top of the last one
|
||||
# without worrying about getting into the future
|
||||
self.nodes[0].setmocktime(TIME_GENESIS_BLOCK + 600 * 1000 + 100)
|
||||
self.tipheight = COINBASE_BLOCK_COUNT # height of the next block to build
|
||||
self.last_block_time = TIME_GENESIS_BLOCK
|
||||
self.tip = int(self.nodes[0].getbestblockhash(), 16)
|
||||
self.nodeaddress = self.nodes[0].getnewaddress()
|
||||
|
||||
# Activation height is hardcoded
|
||||
test_blocks = self.generate_blocks(CSV_ACTIVATION_HEIGHT-5 - COINBASE_BLOCK_COUNT)
|
||||
@ -213,14 +214,14 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
# 16 normal inputs
|
||||
bip68inputs = []
|
||||
for _ in range(16):
|
||||
bip68inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
|
||||
bip68inputs.append(self.send_generic_input_tx(self.coinbase_blocks))
|
||||
|
||||
# 2 sets of 16 inputs with 10 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
|
||||
bip112basicinputs = []
|
||||
for _ in range(2):
|
||||
inputs = []
|
||||
for _ in range(16):
|
||||
inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
|
||||
inputs.append(self.send_generic_input_tx(self.coinbase_blocks))
|
||||
bip112basicinputs.append(inputs)
|
||||
|
||||
# 2 sets of 16 varied inputs with (relative_lock_time) OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
|
||||
@ -228,16 +229,16 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
for _ in range(2):
|
||||
inputs = []
|
||||
for _ in range(16):
|
||||
inputs.append(send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress))
|
||||
inputs.append(self.send_generic_input_tx(self.coinbase_blocks))
|
||||
bip112diverseinputs.append(inputs)
|
||||
|
||||
# 1 special input with -1 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
|
||||
bip112specialinput = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
|
||||
bip112specialinput = self.send_generic_input_tx(self.coinbase_blocks)
|
||||
# 1 special input with (empty stack) OP_CSV (actually will be prepended to spending scriptSig)
|
||||
bip112emptystackinput = send_generic_input_tx(self.nodes[0],self.coinbase_blocks, self.nodeaddress)
|
||||
bip112emptystackinput = self.send_generic_input_tx(self.coinbase_blocks)
|
||||
|
||||
# 1 normal input
|
||||
bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
|
||||
bip113input = self.send_generic_input_tx(self.coinbase_blocks)
|
||||
|
||||
self.nodes[0].setmocktime(self.last_block_time + 600)
|
||||
inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 431
|
||||
@ -257,36 +258,36 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
|
||||
# Test both version 1 and version 2 transactions for all tests
|
||||
# BIP113 test transaction will be modified before each use to put in appropriate block time
|
||||
bip113tx_v1 = create_transaction(self.nodes[0], bip113input, self.nodeaddress, amount=Decimal("499.98"))
|
||||
bip113tx_v1 = self.create_self_transfer_from_utxo(bip113input)
|
||||
bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE
|
||||
bip113tx_v1.nVersion = 1
|
||||
bip113tx_v2 = create_transaction(self.nodes[0], bip113input, self.nodeaddress, amount=Decimal("499.98"))
|
||||
bip113tx_v2 = self.create_self_transfer_from_utxo(bip113input)
|
||||
bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE
|
||||
bip113tx_v2.nVersion = 2
|
||||
|
||||
# For BIP68 test all 16 relative sequence locktimes
|
||||
bip68txs_v1 = create_bip68txs(self.nodes[0], bip68inputs, 1, self.nodeaddress)
|
||||
bip68txs_v2 = create_bip68txs(self.nodes[0], bip68inputs, 2, self.nodeaddress)
|
||||
bip68txs_v1 = self.create_bip68txs(bip68inputs, 1)
|
||||
bip68txs_v2 = self.create_bip68txs(bip68inputs, 2)
|
||||
|
||||
# For BIP112 test:
|
||||
# 16 relative sequence locktimes of 10 against 10 OP_CSV OP_DROP inputs
|
||||
bip112txs_vary_nSequence_v1 = create_bip112txs(self.nodes[0], bip112basicinputs[0], False, 1, self.nodeaddress)
|
||||
bip112txs_vary_nSequence_v2 = create_bip112txs(self.nodes[0], bip112basicinputs[0], False, 2, self.nodeaddress)
|
||||
bip112txs_vary_nSequence_v1 = self.create_bip112txs(bip112basicinputs[0], False, 1)
|
||||
bip112txs_vary_nSequence_v2 = self.create_bip112txs(bip112basicinputs[0], False, 2)
|
||||
# 16 relative sequence locktimes of 9 against 10 OP_CSV OP_DROP inputs
|
||||
bip112txs_vary_nSequence_9_v1 = create_bip112txs(self.nodes[0], bip112basicinputs[1], False, 1, self.nodeaddress, -1)
|
||||
bip112txs_vary_nSequence_9_v2 = create_bip112txs(self.nodes[0], bip112basicinputs[1], False, 2, self.nodeaddress, -1)
|
||||
bip112txs_vary_nSequence_9_v1 = self.create_bip112txs(bip112basicinputs[1], False, 1, -1)
|
||||
bip112txs_vary_nSequence_9_v2 = self.create_bip112txs(bip112basicinputs[1], False, 2, -1)
|
||||
# sequence lock time of 10 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
|
||||
bip112txs_vary_OP_CSV_v1 = create_bip112txs(self.nodes[0], bip112diverseinputs[0], True, 1, self.nodeaddress)
|
||||
bip112txs_vary_OP_CSV_v2 = create_bip112txs(self.nodes[0], bip112diverseinputs[0], True, 2, self.nodeaddress)
|
||||
bip112txs_vary_OP_CSV_v1 = self.create_bip112txs(bip112diverseinputs[0], True, 1)
|
||||
bip112txs_vary_OP_CSV_v2 = self.create_bip112txs(bip112diverseinputs[0], True, 2)
|
||||
# sequence lock time of 9 against 16 (relative_lock_time) OP_CSV OP_DROP inputs
|
||||
bip112txs_vary_OP_CSV_9_v1 = create_bip112txs(self.nodes[0], bip112diverseinputs[1], True, 1, self.nodeaddress, -1)
|
||||
bip112txs_vary_OP_CSV_9_v2 = create_bip112txs(self.nodes[0], bip112diverseinputs[1], True, 2, self.nodeaddress, -1)
|
||||
bip112txs_vary_OP_CSV_9_v1 = self.create_bip112txs(bip112diverseinputs[1], True, 1, -1)
|
||||
bip112txs_vary_OP_CSV_9_v2 = self.create_bip112txs(bip112diverseinputs[1], True, 2, -1)
|
||||
# -1 OP_CSV OP_DROP input
|
||||
bip112tx_special_v1 = create_bip112special(self.nodes[0], bip112specialinput, 1, self.nodeaddress)
|
||||
bip112tx_special_v2 = create_bip112special(self.nodes[0], bip112specialinput, 2, self.nodeaddress)
|
||||
bip112tx_special_v1 = self.create_bip112special(bip112specialinput, 1)
|
||||
bip112tx_special_v2 = self.create_bip112special(bip112specialinput, 2)
|
||||
# (empty stack) OP_CSV input
|
||||
bip112tx_emptystack_v1 = create_bip112emptystack(self.nodes[0], bip112emptystackinput, 1, self.nodeaddress)
|
||||
bip112tx_emptystack_v2 = create_bip112emptystack(self.nodes[0], bip112emptystackinput, 2, self.nodeaddress)
|
||||
bip112tx_emptystack_v1 = self.create_bip112emptystack(bip112emptystackinput, 1)
|
||||
bip112tx_emptystack_v2 = self.create_bip112emptystack(bip112emptystackinput, 2)
|
||||
|
||||
self.log.info("TESTING")
|
||||
|
||||
@ -296,8 +297,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
success_txs = []
|
||||
# BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed
|
||||
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
|
||||
bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
|
||||
success_txs.append(bip113signed1)
|
||||
self.miniwallet.sign_tx(bip113tx_v1)
|
||||
success_txs.append(bip113tx_v1)
|
||||
success_txs.append(bip112tx_special_v1)
|
||||
success_txs.append(bip112tx_emptystack_v1)
|
||||
# add BIP 68 txs
|
||||
@ -316,8 +317,8 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
success_txs = []
|
||||
# BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed
|
||||
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
|
||||
bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
|
||||
success_txs.append(bip113signed2)
|
||||
self.miniwallet.sign_tx(bip113tx_v2)
|
||||
success_txs.append(bip113tx_v2)
|
||||
success_txs.append(bip112tx_special_v2)
|
||||
success_txs.append(bip112tx_emptystack_v2)
|
||||
# add BIP 68 txs
|
||||
@ -342,17 +343,22 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
self.log.info("BIP 113 tests")
|
||||
# BIP 113 tests should now fail regardless of version number if nLockTime isn't satisfied by new rules
|
||||
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
|
||||
bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
|
||||
self.miniwallet.sign_tx(bip113tx_v1)
|
||||
bip113tx_v1.rehash()
|
||||
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
|
||||
bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
|
||||
for bip113tx in [bip113signed1, bip113signed2]:
|
||||
self.miniwallet.sign_tx(bip113tx_v2)
|
||||
bip113tx_v2.rehash()
|
||||
for bip113tx in [bip113tx_v1, bip113tx_v2]:
|
||||
self.send_blocks([self.create_test_block([bip113tx])], success=False, reject_reason='bad-txns-nonfinal')
|
||||
|
||||
# BIP 113 tests should now pass if the locktime is < MTP
|
||||
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
|
||||
bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
|
||||
self.miniwallet.sign_tx(bip113tx_v1)
|
||||
bip113tx_v1.rehash()
|
||||
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
|
||||
bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
|
||||
for bip113tx in [bip113signed1, bip113signed2]:
|
||||
self.miniwallet.sign_tx(bip113tx_v2)
|
||||
bip113tx_v2.rehash()
|
||||
for bip113tx in [bip113tx_v1, bip113tx_v2]:
|
||||
self.send_blocks([self.create_test_block([bip113tx])])
|
||||
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
|
||||
|
||||
@ -475,8 +481,9 @@ class BIP68_112_113Test(BitcoinTestFramework):
|
||||
time_txs = []
|
||||
for tx in [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]:
|
||||
tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME | SEQ_TYPE_FLAG
|
||||
signtx = sign_transaction(self.nodes[0], tx)
|
||||
time_txs.append(signtx)
|
||||
self.miniwallet.sign_tx(tx)
|
||||
tx.rehash()
|
||||
time_txs.append(tx)
|
||||
|
||||
self.send_blocks([self.create_test_block(time_txs)])
|
||||
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
|
||||
|
@ -7,7 +7,10 @@
|
||||
Test that the DERSIG soft-fork activates at (regtest) height 1251.
|
||||
"""
|
||||
|
||||
from test_framework.blocktools import create_coinbase, create_block, create_transaction
|
||||
from test_framework.blocktools import (
|
||||
create_block,
|
||||
create_coinbase,
|
||||
)
|
||||
from test_framework.messages import msg_block
|
||||
from test_framework.p2p import P2PInterface
|
||||
from test_framework.script import CScript
|
||||
@ -16,6 +19,10 @@ from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
from test_framework.wallet import (
|
||||
MiniWallet,
|
||||
MiniWalletMode,
|
||||
)
|
||||
|
||||
DERSIG_HEIGHT = 1251
|
||||
|
||||
@ -44,6 +51,10 @@ class BIP66Test(BitcoinTestFramework):
|
||||
self.setup_clean_chain = True
|
||||
self.rpc_timeout = 240
|
||||
|
||||
def create_tx(self, input_txid):
|
||||
utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid, mark_as_spent=False)
|
||||
return self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['tx']
|
||||
|
||||
def test_dersig_info(self, *, is_active):
|
||||
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'],
|
||||
{
|
||||
@ -53,22 +64,18 @@ class BIP66Test(BitcoinTestFramework):
|
||||
},
|
||||
)
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
def run_test(self):
|
||||
peer = self.nodes[0].add_p2p_connection(P2PInterface())
|
||||
self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK)
|
||||
|
||||
self.test_dersig_info(is_active=False)
|
||||
|
||||
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
|
||||
self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2)]
|
||||
self.nodeaddress = self.nodes[0].getnewaddress()
|
||||
self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.miniwallet.generate(DERSIG_HEIGHT - 2)]
|
||||
|
||||
self.log.info("Test that a transaction with non-DER signature can still appear in a block")
|
||||
|
||||
spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0],
|
||||
self.nodeaddress, amount=1.0)
|
||||
spendtx = self.create_tx(self.coinbase_txids[0])
|
||||
unDERify(spendtx)
|
||||
spendtx.rehash()
|
||||
|
||||
@ -102,8 +109,7 @@ class BIP66Test(BitcoinTestFramework):
|
||||
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
|
||||
block.nVersion = 3
|
||||
|
||||
spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1],
|
||||
self.nodeaddress, amount=1.0)
|
||||
spendtx = self.create_tx(self.coinbase_txids[1])
|
||||
unDERify(spendtx)
|
||||
spendtx.rehash()
|
||||
|
||||
@ -123,7 +129,7 @@ class BIP66Test(BitcoinTestFramework):
|
||||
peer.sync_with_ping()
|
||||
|
||||
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
|
||||
block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0)
|
||||
block.vtx[1] = self.create_tx(self.coinbase_txids[1])
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
block.rehash()
|
||||
block.solve()
|
||||
|
@ -65,7 +65,7 @@ class NULLDUMMYTest(BitcoinTestFramework):
|
||||
self.pubkey = w0.getaddressinfo(self.address)['pubkey']
|
||||
self.ms_address = wmulti.addmultisigaddress(1, [self.pubkey])['address']
|
||||
if not self.options.descriptors:
|
||||
# Legacy wallets need to import these so that they are watched by the wallet. This is unnecssary (and does not need to be tested) for descriptor wallets
|
||||
# Legacy wallets need to import these so that they are watched by the wallet. This is unnecessary (and does not need to be tested) for descriptor wallets
|
||||
wmulti.importaddress(self.ms_address)
|
||||
|
||||
self.coinbase_blocks = self.nodes[0].generate(2) # block height = 2
|
||||
|
@ -6,22 +6,25 @@
|
||||
|
||||
import time
|
||||
|
||||
from test_framework.blocktools import create_transaction
|
||||
from test_framework.messages import msg_tx
|
||||
from test_framework.p2p import P2PInterface, P2PTxInvStore
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
from test_framework.wallet import MiniWallet
|
||||
|
||||
|
||||
class P2PBlocksOnly(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
self.extra_args = [["-blocksonly"]]
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
def run_test(self):
|
||||
self.miniwallet = MiniWallet(self.nodes[0])
|
||||
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
|
||||
self.miniwallet.generate(2)
|
||||
self.nodes[0].generate(100)
|
||||
|
||||
self.blocksonly_mode_tests()
|
||||
self.blocks_relay_conn_tests()
|
||||
|
||||
@ -96,19 +99,18 @@ class P2PBlocksOnly(BitcoinTestFramework):
|
||||
def check_p2p_tx_violation(self, index=1):
|
||||
self.log.info('Check that txs from P2P are rejected and result in disconnect')
|
||||
input_txid = self.nodes[0].getblock(self.nodes[0].getblockhash(index), 2)['tx'][0]['txid']
|
||||
tx = create_transaction(self.nodes[0], input_txid, self.nodes[0].getnewaddress(), amount=(500 - 0.001))
|
||||
txid = tx.rehash()
|
||||
tx_hex = tx.serialize().hex()
|
||||
utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid)
|
||||
spendtx = self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)
|
||||
|
||||
with self.nodes[0].assert_debug_log(['tx sent in violation of protocol peer=0']):
|
||||
self.nodes[0].p2ps[0].send_message(msg_tx(tx))
|
||||
self.nodes[0].p2ps[0].send_message(msg_tx(spendtx['tx']))
|
||||
self.nodes[0].p2ps[0].wait_for_disconnect()
|
||||
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
|
||||
|
||||
# Remove the disconnected peer
|
||||
del self.nodes[0].p2ps[0]
|
||||
|
||||
return tx, txid, tx_hex
|
||||
return spendtx['tx'], spendtx['txid'], spendtx['hex']
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -5,7 +5,9 @@
|
||||
"""A limited-functionality wallet, which may replace a real wallet in tests"""
|
||||
|
||||
from decimal import Decimal
|
||||
from enum import Enum
|
||||
from test_framework.address import ADDRESS_BCRT1_P2SH_OP_TRUE
|
||||
from test_framework.key import ECKey
|
||||
from typing import Optional
|
||||
from test_framework.messages import (
|
||||
COIN,
|
||||
@ -16,8 +18,11 @@ from test_framework.messages import (
|
||||
)
|
||||
from test_framework.script import (
|
||||
CScript,
|
||||
SignatureHash,
|
||||
OP_CHECKSIG,
|
||||
OP_TRUE,
|
||||
OP_NOP,
|
||||
SIGHASH_ALL,
|
||||
)
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
@ -26,14 +31,46 @@ from test_framework.util import (
|
||||
)
|
||||
|
||||
|
||||
class MiniWalletMode(Enum):
|
||||
"""Determines the transaction type the MiniWallet is creating and spending.
|
||||
|
||||
For most purposes, the default mode ADDRESS_OP_TRUE should be sufficient;
|
||||
it simply uses a fixed P2SH address whose coins are spent with a
|
||||
witness stack of OP_TRUE, i.e. following an anyone-can-spend policy.
|
||||
However, if the transactions need to be modified by the user (e.g. prepending
|
||||
scriptSig for testing opcodes that are activated by a soft-fork), or the txs
|
||||
should contain an actual signature, the raw modes RAW_OP_TRUE and RAW_P2PK
|
||||
can be useful. Summary of modes:
|
||||
|
||||
| output | | tx is | can modify | needs
|
||||
mode | description | address | standard | scriptSig | signing
|
||||
----------------+-------------------+-----------+----------+------------+----------
|
||||
ADDRESS_OP_TRUE | anyone-can-spend | bech32 | yes | no | no
|
||||
RAW_OP_TRUE | anyone-can-spend | - (raw) | no | yes | no
|
||||
RAW_P2PK | pay-to-public-key | - (raw) | yes | yes | yes
|
||||
"""
|
||||
ADDRESS_OP_TRUE = 1
|
||||
RAW_OP_TRUE = 2
|
||||
RAW_P2PK = 3
|
||||
|
||||
|
||||
class MiniWallet:
|
||||
def __init__(self, test_node, *, raw_script=False):
|
||||
def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE):
|
||||
self._test_node = test_node
|
||||
self._utxos = []
|
||||
if raw_script:
|
||||
self._priv_key = None
|
||||
self._address = None
|
||||
|
||||
assert isinstance(mode, MiniWalletMode)
|
||||
if mode == MiniWalletMode.RAW_OP_TRUE:
|
||||
self._scriptPubKey = bytes(CScript([OP_TRUE]))
|
||||
else:
|
||||
elif mode == MiniWalletMode.RAW_P2PK:
|
||||
# use simple deterministic private key (k=1)
|
||||
self._priv_key = ECKey()
|
||||
self._priv_key.set((1).to_bytes(32, 'big'), True)
|
||||
pub_key = self._priv_key.get_pubkey()
|
||||
self._scriptPubKey = bytes(CScript([pub_key.get_bytes(), OP_CHECKSIG]))
|
||||
elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
|
||||
self._address = ADDRESS_BCRT1_P2SH_OP_TRUE
|
||||
self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey'])
|
||||
|
||||
@ -50,6 +87,13 @@ class MiniWallet:
|
||||
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
|
||||
self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']})
|
||||
|
||||
def sign_tx(self, tx):
|
||||
"""Sign tx that has been created by MiniWallet in P2PK mode"""
|
||||
assert self._priv_key is not None
|
||||
(sighash, err) = SignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL)
|
||||
assert err is None
|
||||
tx.vin[0].scriptSig = CScript([self._priv_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))])
|
||||
|
||||
def generate(self, num_blocks):
|
||||
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
|
||||
blocks = self._test_node.generatetodescriptor(num_blocks, f'raw({self._scriptPubKey.hex()})')
|
||||
@ -61,7 +105,7 @@ class MiniWallet:
|
||||
def get_address(self):
|
||||
return self._address
|
||||
|
||||
def get_utxo(self, *, txid: Optional[str]=''):
|
||||
def get_utxo(self, *, txid: Optional[str]='', mark_as_spent=True):
|
||||
"""
|
||||
Returns a utxo and marks it as spent (pops it from the internal list)
|
||||
|
||||
@ -74,7 +118,10 @@ class MiniWallet:
|
||||
if txid:
|
||||
utxo = next(filter(lambda utxo: txid == utxo['txid'], self._utxos))
|
||||
index = self._utxos.index(utxo)
|
||||
if mark_as_spent:
|
||||
return self._utxos.pop(index)
|
||||
else:
|
||||
return self._utxos[index]
|
||||
|
||||
def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None):
|
||||
"""Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
|
||||
@ -96,6 +143,11 @@ class MiniWallet:
|
||||
tx.vout = [CTxOut(int(send_value * COIN), self._scriptPubKey)]
|
||||
if not self._address:
|
||||
# raw script
|
||||
if self._priv_key is not None:
|
||||
# P2PK, need to sign
|
||||
self.sign_tx(tx)
|
||||
else:
|
||||
# anyone-can-spend
|
||||
tx.vin[0].scriptSig = CScript([OP_NOP] * 24) # pad to identical size
|
||||
else:
|
||||
tx.vin[0].scriptSig = CScript([CScript([OP_TRUE])])
|
||||
@ -104,7 +156,10 @@ class MiniWallet:
|
||||
tx_info = from_node.testmempoolaccept([tx_hex])[0]
|
||||
assert_equal(mempool_valid, tx_info['allowed'])
|
||||
if mempool_valid:
|
||||
assert_equal(len(tx_hex) // 2, vsize)
|
||||
# TODO: for P2PK, vsize is not constant due to varying scriptSig length,
|
||||
# so only check this for anyone-can-spend outputs right now
|
||||
if self._priv_key is None:
|
||||
assert_equal(len(tx_hex) // 2, vsize) # 1 byte = 2 character
|
||||
assert_equal(tx_info['fees']['base'], fee)
|
||||
return {'txid': tx_info['txid'], 'hex': tx_hex, 'tx': tx}
|
||||
|
||||
|
@ -79,7 +79,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
|
||||
expected_time = self.mocktime + MAX_VALUE - 600
|
||||
self.nodes[0].walletpassphrase(passphrase2, MAX_VALUE - 600)
|
||||
self.bump_mocktime(1)
|
||||
# give buffer for walletpassphrase, since it iterates over all crypted keys
|
||||
# give buffer for walletpassphrase, since it iterates over all encrypted keys
|
||||
expected_time_with_buffer = self.mocktime + MAX_VALUE - 600
|
||||
actual_time = self.nodes[0].getwalletinfo()['unlocked_until']
|
||||
assert_greater_than_or_equal(actual_time, expected_time)
|
||||
|
Loading…
Reference in New Issue
Block a user