mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-20 07:09:15 +01:00
Merge #16035: 0.18.1: Backports
bcb27d7b0.python-version: Bump to 3.5.6 (MarcoFalke)af25a757eAdd comments to Python ECDSA implementation (John Newbery)715da91e9Set AA_EnableHighDpiScaling attribute early (Hennadii Stepanov)2800b3d5cgui: Fix open wallet menu initialization order (João Barbosa)e78007fc1Make and get the multisig redeemscript and destination in one function instead of two (Andrew Chow)d9fc969e7Pure python EC (Pieter Wuille)23ba460c1test: Add test that addmultisigaddress fails for watchonly addresses (MarcoFalke)13b3bb564test: Fixup creatmultisig documentation and whitespace (MarcoFalke)79745d175Replace remaining fprintf with tfm::format manually (MarcoFalke)beb09f09bscripted-diff: Replace fprintf with tfm::format (MarcoFalke)e29aa6e72Exceptions should be caught by reference, not by value. (Kristaps Kaupe)f88959ba7tinyformat: Add doc to Bitcoin Core specific strprintf (MarcoFalke)0023c9789rpc: bugfix: Properly use iswitness in converttopsbt (MarcoFalke)832eb4ff5Bugfix: test/functional/rpc_psbt: Correct test description comment (Luke Dashjr)966d8d084Bugfix: test/functional/rpc_psbt: Remove check for specific error message that depends on uncertain assumptions (Luke Dashjr)bb36ac82erpc: Switch touched RPCs to IsValidNumArgs (MarcoFalke)d24d0ec05Add example 2nd arg to signrawtransactionwithkey (Chris Moore)592016ba1fixup: Fix prunning test (João Barbosa)c80a498aeFix RPC/pruneblockchain returned prune height (Jonas Schnelli)b2398240fgui: Enable open wallet menu on setWalletController (João Barbosa)d1f261150Add test for GCC bug 90348 (Pieter Wuille)d80c558e0gui: Set progressDialog to nullptr (João Barbosa)7ed1a6019gui: Enable console line edit on setClientModel (João Barbosa)b55cbe82dqt: fix opening bitcoin.conf via Preferences on macOS; see #15409 (shannon1916)b6c1f9478Disallow extended encoding for non-witness transactions (take 3) (MarcoFalke)86031083cAdd test for superfluous witness record in deserialization (Gregory Sanders)5a58ddb6dFix missing input template by making minimal tx (Gregory Sanders)206f5ee87Disallow extended encoding for non-witness transactions (Pieter Wuille)3dbc7def0Show loaded wallets as disabled in open menu instead of nothing (MeshCollider)a635377b6Install bitcoin-wallet manpage. (Daniel Kraft)eb85ee62bDoc: remove text about txes always relayed from -whitelist (David A. Harding)890a92ebadoc: Mention blocksonly in reduce-traffic.md, unhide option (MarcoFalke)3460555f4test: Add test for p2p_blocksonly (MarcoFalke)8f215c7a2test: Format predicate source as multiline on error (MarcoFalke)9c1a607a0net: Rename ::fRelayTxes to ::g_relay_txes (MarcoFalke)5935f0126build with -fstack-reuse=none (MarcoFalke) Pull request description: Tree-SHA512: 5cd73a4319cb69c92b528239cf97c0ed5fcf2b9e8c7fe154e4679eeec95db433a0223d8dc574e4cdc96c1913cfdf160b10c42dcdbcb5bbc8fb743c07930ef9da
This commit is contained in:
@@ -1 +1 @@
|
||||
3.4.9
|
||||
3.5.6
|
||||
|
||||
@@ -725,6 +725,10 @@ if test x$TARGET_OS != xwindows; then
|
||||
AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"])
|
||||
fi
|
||||
|
||||
# All versions of gcc that we commonly use for building are subject to bug
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348. To work around that, set
|
||||
# -fstack-reuse=none for all gcc builds. (Only gcc understands this flag)
|
||||
AX_CHECK_COMPILE_FLAG([-fstack-reuse=none],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-reuse=none"])
|
||||
if test x$use_hardening != xno; then
|
||||
use_hardening=yes
|
||||
AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"])
|
||||
|
||||
@@ -22,7 +22,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
|
||||
| OpenSSL | [1.0.1k](https://www.openssl.org/source) | | Yes | | |
|
||||
| PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L66) |
|
||||
| protobuf | [2.6.1](https://github.com/google/protobuf/releases) | | No | | |
|
||||
| Python (tests) | | [3.4](https://www.python.org/downloads) | | | |
|
||||
| Python (tests) | | [3.5](https://www.python.org/downloads) | | | |
|
||||
| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | |
|
||||
| Qt | [5.9.7](https://download.qt.io/official_releases/qt/) | [5.5.1](https://github.com/bitcoin/bitcoin/issues/13478) | No | | |
|
||||
| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L87) (Linux only) |
|
||||
|
||||
@@ -15,3 +15,9 @@ endif
|
||||
if BUILD_BITCOIN_TX
|
||||
dist_man1_MANS+=bitcoin-tx.1
|
||||
endif
|
||||
|
||||
if ENABLE_WALLET
|
||||
if BUILD_BITCOIN_WALLET
|
||||
dist_man1_MANS+=bitcoin-wallet.1
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -35,3 +35,16 @@ blocks and transactions to fewer nodes.
|
||||
Reducing the maximum connected nodes to a minimum could be desirable if traffic
|
||||
limits are tiny. Keep in mind that bitcoin's trustless model works best if you are
|
||||
connected to a handful of nodes.
|
||||
|
||||
## 4. Turn off transaction relay (`-blocksonly`)
|
||||
|
||||
Forwarding transactions to peers increases the P2P traffic. To only sync blocks
|
||||
with other peers, you can disable transaction relay.
|
||||
|
||||
Be reminded of the effects of this setting.
|
||||
|
||||
- Fee estimation will no longer work.
|
||||
- Not relaying other's transactions could hurt your privacy if used while a
|
||||
wallet is loaded or if you use the node to broadcast transactions.
|
||||
- It makes block propagation slower because compact block relay can only be
|
||||
used when transaction relay is enabled.
|
||||
|
||||
@@ -78,6 +78,7 @@ BITCOIN_TESTS =\
|
||||
test/bswap_tests.cpp \
|
||||
test/checkqueue_tests.cpp \
|
||||
test/coins_tests.cpp \
|
||||
test/compilerbug_tests.cpp \
|
||||
test/compress_tests.cpp \
|
||||
test/crypto_tests.cpp \
|
||||
test/cuckoocache_tests.cpp \
|
||||
|
||||
@@ -49,7 +49,7 @@ int main(int argc, char** argv)
|
||||
SetupBenchArgs();
|
||||
std::string error;
|
||||
if (!gArgs.ParseParameters(argc, argv, error)) {
|
||||
fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
|
||||
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ int main(int argc, char** argv)
|
||||
|
||||
double scaling_factor;
|
||||
if (!ParseDouble(scaling_str, &scaling_factor)) {
|
||||
fprintf(stderr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str());
|
||||
tfm::format(std::cerr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ static int AppInitRPC(int argc, char* argv[])
|
||||
SetupCliArgs();
|
||||
std::string error;
|
||||
if (!gArgs.ParseParameters(argc, argv, error)) {
|
||||
fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
|
||||
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
|
||||
@@ -115,26 +115,26 @@ static int AppInitRPC(int argc, char* argv[])
|
||||
strUsage += "\n" + gArgs.GetHelpMessage();
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s", strUsage.c_str());
|
||||
tfm::format(std::cout, "%s", strUsage.c_str());
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Error: too few parameters\n");
|
||||
tfm::format(std::cerr, "Error: too few parameters\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (!fs::is_directory(GetDataDir(false))) {
|
||||
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
|
||||
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!gArgs.ReadConfigFiles(error, true)) {
|
||||
fprintf(stderr, "Error reading configuration file: %s\n", error.c_str());
|
||||
tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
|
||||
try {
|
||||
SelectBaseParams(gArgs.GetChainName());
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
tfm::format(std::cerr, "Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return CONTINUE_EXECUTION;
|
||||
@@ -499,7 +499,7 @@ static int CommandLineRPC(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (strPrint != "") {
|
||||
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
|
||||
tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint.c_str());
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
@@ -512,7 +512,7 @@ int main(int argc, char* argv[])
|
||||
#endif
|
||||
SetupEnvironment();
|
||||
if (!SetupNetworking()) {
|
||||
fprintf(stderr, "Error: Initializing networking failed\n");
|
||||
tfm::format(std::cerr, "Error: Initializing networking failed\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
event_set_log_callback(&libevent_log_cb);
|
||||
|
||||
@@ -81,7 +81,7 @@ static int AppInitRawTx(int argc, char* argv[])
|
||||
SetupBitcoinTxArgs();
|
||||
std::string error;
|
||||
if (!gArgs.ParseParameters(argc, argv, error)) {
|
||||
fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
|
||||
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ static int AppInitRawTx(int argc, char* argv[])
|
||||
try {
|
||||
SelectParams(gArgs.GetChainName());
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
tfm::format(std::cerr, "Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -103,10 +103,10 @@ static int AppInitRawTx(int argc, char* argv[])
|
||||
"\n";
|
||||
strUsage += gArgs.GetHelpMessage();
|
||||
|
||||
fprintf(stdout, "%s", strUsage.c_str());
|
||||
tfm::format(std::cout, "%s", strUsage.c_str());
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Error: too few parameters\n");
|
||||
tfm::format(std::cerr, "Error: too few parameters\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
@@ -722,21 +722,21 @@ static void OutputTxJSON(const CTransaction& tx)
|
||||
TxToUniv(tx, uint256(), entry);
|
||||
|
||||
std::string jsonOutput = entry.write(4);
|
||||
fprintf(stdout, "%s\n", jsonOutput.c_str());
|
||||
tfm::format(std::cout, "%s\n", jsonOutput.c_str());
|
||||
}
|
||||
|
||||
static void OutputTxHash(const CTransaction& tx)
|
||||
{
|
||||
std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
|
||||
|
||||
fprintf(stdout, "%s\n", strHexHash.c_str());
|
||||
tfm::format(std::cout, "%s\n", strHexHash.c_str());
|
||||
}
|
||||
|
||||
static void OutputTxHex(const CTransaction& tx)
|
||||
{
|
||||
std::string strHex = EncodeHexTx(tx);
|
||||
|
||||
fprintf(stdout, "%s\n", strHex.c_str());
|
||||
tfm::format(std::cout, "%s\n", strHex.c_str());
|
||||
}
|
||||
|
||||
static void OutputTx(const CTransaction& tx)
|
||||
@@ -827,7 +827,7 @@ static int CommandLineRawTx(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if (strPrint != "") {
|
||||
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
|
||||
tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint.c_str());
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ static bool WalletAppInit(int argc, char* argv[])
|
||||
SetupWalletToolArgs();
|
||||
std::string error_message;
|
||||
if (!gArgs.ParseParameters(argc, argv, error_message)) {
|
||||
fprintf(stderr, "Error parsing command line arguments: %s\n", error_message.c_str());
|
||||
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message.c_str());
|
||||
return false;
|
||||
}
|
||||
if (argc < 2 || HelpRequested(gArgs)) {
|
||||
@@ -49,7 +49,7 @@ static bool WalletAppInit(int argc, char* argv[])
|
||||
" bitcoin-wallet [options] <command>\n\n" +
|
||||
gArgs.GetHelpMessage();
|
||||
|
||||
fprintf(stdout, "%s", usage.c_str());
|
||||
tfm::format(std::cout, "%s", usage.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ static bool WalletAppInit(int argc, char* argv[])
|
||||
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false));
|
||||
|
||||
if (!fs::is_directory(GetDataDir(false))) {
|
||||
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
|
||||
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
|
||||
return false;
|
||||
}
|
||||
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
|
||||
@@ -88,7 +88,7 @@ int main(int argc, char* argv[])
|
||||
for(int i = 1; i < argc; ++i) {
|
||||
if (!IsSwitchChar(argv[i][0])) {
|
||||
if (!method.empty()) {
|
||||
fprintf(stderr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]);
|
||||
tfm::format(std::cerr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method.c_str(), argv[i]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
method = argv[i];
|
||||
@@ -96,13 +96,13 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
if (method.empty()) {
|
||||
fprintf(stderr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
|
||||
tfm::format(std::cerr, "No method provided. Run `bitcoin-wallet -help` for valid methods.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// A name must be provided when creating a file
|
||||
if (method == "create" && !gArgs.IsArgSet("-wallet")) {
|
||||
fprintf(stderr, "Wallet name must be provided when creating a new wallet.\n");
|
||||
tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ static bool AppInit(int argc, char* argv[])
|
||||
SetupServerArgs();
|
||||
std::string error;
|
||||
if (!gArgs.ParseParameters(argc, argv, error)) {
|
||||
fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str());
|
||||
tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ static bool AppInit(int argc, char* argv[])
|
||||
strUsage += "\n" + gArgs.GetHelpMessage();
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s", strUsage.c_str());
|
||||
tfm::format(std::cout, "%s", strUsage.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -97,25 +97,25 @@ static bool AppInit(int argc, char* argv[])
|
||||
{
|
||||
if (!fs::is_directory(GetDataDir(false)))
|
||||
{
|
||||
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
|
||||
tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
|
||||
return false;
|
||||
}
|
||||
if (!gArgs.ReadConfigFiles(error, true)) {
|
||||
fprintf(stderr, "Error reading configuration file: %s\n", error.c_str());
|
||||
tfm::format(std::cerr, "Error reading configuration file: %s\n", error.c_str());
|
||||
return false;
|
||||
}
|
||||
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
|
||||
try {
|
||||
SelectParams(gArgs.GetChainName());
|
||||
} catch (const std::exception& e) {
|
||||
fprintf(stderr, "Error: %s\n", e.what());
|
||||
tfm::format(std::cerr, "Error: %s\n", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Error out when loose non-argument tokens are encountered on command line
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!IsSwitchChar(argv[i][0])) {
|
||||
fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
|
||||
tfm::format(std::cerr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -147,18 +147,18 @@ static bool AppInit(int argc, char* argv[])
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
fprintf(stdout, "Bitcoin server starting\n");
|
||||
tfm::format(std::cout, "Bitcoin server starting\n");
|
||||
|
||||
// Daemonize
|
||||
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
|
||||
fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
|
||||
tfm::format(std::cerr, "Error: daemon() failed: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#if defined(MAC_OSX)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#else
|
||||
fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
|
||||
tfm::format(std::cerr, "Error: -daemon is not supported on this operating system\n");
|
||||
return false;
|
||||
#endif // HAVE_DECL_DAEMON
|
||||
}
|
||||
|
||||
15
src/init.cpp
15
src/init.cpp
@@ -106,14 +106,13 @@ static fs::path GetPidFile()
|
||||
|
||||
NODISCARD static bool CreatePidFile()
|
||||
{
|
||||
FILE* file = fsbridge::fopen(GetPidFile(), "w");
|
||||
fsbridge::ofstream file{GetPidFile()};
|
||||
if (file) {
|
||||
#ifdef WIN32
|
||||
fprintf(file, "%d\n", GetCurrentProcessId());
|
||||
tfm::format(file, "%d\n", GetCurrentProcessId());
|
||||
#else
|
||||
fprintf(file, "%d\n", getpid());
|
||||
tfm::format(file, "%d\n", getpid());
|
||||
#endif
|
||||
fclose(file);
|
||||
return true;
|
||||
} else {
|
||||
return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno)));
|
||||
@@ -376,7 +375,7 @@ void SetupServerArgs()
|
||||
gArgs.AddArg("-blocksdir=<dir>", "Specify blocks directory (default: <datadir>/blocks)", false, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", false, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), false, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-blocksonly", strprintf("Whether to operate in a blocks only mode (default: %u)", DEFAULT_BLOCKSONLY), true, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet or RPC are not affected. (default: %u)", DEFAULT_BLOCKSONLY), false, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS);
|
||||
@@ -446,7 +445,7 @@ void SetupServerArgs()
|
||||
#endif
|
||||
gArgs.AddArg("-whitebind=<addr>", "Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION);
|
||||
gArgs.AddArg("-whitelist=<IP address or network>", "Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times."
|
||||
" Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway", false, OptionsCategory::CONNECTION);
|
||||
" Whitelisted peers cannot be DoS banned", false, OptionsCategory::CONNECTION);
|
||||
|
||||
g_wallet_init_interface.AddWalletOptions();
|
||||
|
||||
@@ -518,7 +517,7 @@ void SetupServerArgs()
|
||||
gArgs.AddArg("-mempoolreplacement", strprintf("Enable transaction replacement in the memory pool (default: %u)", DEFAULT_ENABLE_REPLACEMENT), false, OptionsCategory::NODE_RELAY);
|
||||
gArgs.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
|
||||
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), false, OptionsCategory::NODE_RELAY);
|
||||
gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY);
|
||||
gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if the transactions were already in the mempool or violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY);
|
||||
gArgs.AddArg("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), false, OptionsCategory::NODE_RELAY);
|
||||
|
||||
|
||||
@@ -1411,7 +1410,7 @@ bool AppInitMain(InitInterfaces& interfaces)
|
||||
// see Step 2: parameter interactions for more information about these
|
||||
fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN);
|
||||
fDiscover = gArgs.GetBoolArg("-discover", true);
|
||||
fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
|
||||
g_relay_txes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
|
||||
|
||||
for (const std::string& strAddr : gArgs.GetArgs("-externalip")) {
|
||||
CService addrLocal;
|
||||
|
||||
@@ -79,7 +79,7 @@ static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // S
|
||||
//
|
||||
bool fDiscover = true;
|
||||
bool fListen = true;
|
||||
bool fRelayTxes = true;
|
||||
bool g_relay_txes = !DEFAULT_BLOCKSONLY;
|
||||
CCriticalSection cs_mapLocalHost;
|
||||
std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(cs_mapLocalHost);
|
||||
static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {};
|
||||
|
||||
@@ -523,7 +523,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
|
||||
|
||||
extern bool fDiscover;
|
||||
extern bool fListen;
|
||||
extern bool fRelayTxes;
|
||||
extern bool g_relay_txes;
|
||||
|
||||
extern limitedmap<uint256, int64_t> mapAlreadyAskedFor;
|
||||
|
||||
|
||||
@@ -336,7 +336,7 @@ static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
|
||||
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
|
||||
|
||||
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
|
||||
nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes));
|
||||
nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes));
|
||||
|
||||
if (fLogIPs) {
|
||||
LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
|
||||
@@ -2011,7 +2011,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fBlocksOnly = !fRelayTxes;
|
||||
bool fBlocksOnly = !g_relay_txes;
|
||||
|
||||
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
|
||||
if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
|
||||
@@ -2266,7 +2266,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
if (strCommand == NetMsgType::TX) {
|
||||
// Stop processing the transaction early if
|
||||
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
|
||||
if (!fRelayTxes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
|
||||
if (!g_relay_txes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
|
||||
{
|
||||
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
|
||||
return true;
|
||||
@@ -3096,23 +3096,22 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
|
||||
if (m_enable_bip61) {
|
||||
connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")));
|
||||
}
|
||||
if (strstr(e.what(), "end of data"))
|
||||
{
|
||||
if (strstr(e.what(), "end of data")) {
|
||||
// Allow exceptions from under-length message on vRecv
|
||||
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
|
||||
}
|
||||
else if (strstr(e.what(), "size too large"))
|
||||
{
|
||||
} else if (strstr(e.what(), "size too large")) {
|
||||
// Allow exceptions from over-long size
|
||||
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
|
||||
}
|
||||
else if (strstr(e.what(), "non-canonical ReadCompactSize()"))
|
||||
{
|
||||
} else if (strstr(e.what(), "non-canonical ReadCompactSize()")) {
|
||||
// Allow exceptions from non-canonical encoding
|
||||
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else if (strstr(e.what(), "Superfluous witness record")) {
|
||||
// Allow exceptions from illegal witness encoding
|
||||
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
|
||||
} else if (strstr(e.what(), "Unknown transaction optional data")) {
|
||||
// Allow exceptions from unknown witness encoding
|
||||
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what());
|
||||
} else {
|
||||
PrintExceptionContinue(&e, "ProcessMessages()");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& ca
|
||||
|
||||
if (!fSecure)
|
||||
LogPrintf("%s: %s\n", strCaption, message);
|
||||
fprintf(stderr, "%s: %s\n", strCaption.c_str(), message.c_str());
|
||||
tfm::format(std::cerr, "%s: %s\n", strCaption.c_str(), message.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,6 +222,10 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
|
||||
for (size_t i = 0; i < tx.vin.size(); i++) {
|
||||
s >> tx.vin[i].scriptWitness.stack;
|
||||
}
|
||||
if (!tx.HasWitness()) {
|
||||
/* It's illegal to encode witnesses when all witness stacks are empty. */
|
||||
throw std::ios_base::failure("Superfluous witness record");
|
||||
}
|
||||
}
|
||||
if (flags) {
|
||||
/* Unknown flag in the serialization */
|
||||
|
||||
@@ -437,16 +437,17 @@ int GuiMain(int argc, char* argv[])
|
||||
Q_INIT_RESOURCE(bitcoin);
|
||||
Q_INIT_RESOURCE(bitcoin_locale);
|
||||
|
||||
BitcoinApplication app(*node, argc, argv);
|
||||
// Generate high-dpi pixmaps
|
||||
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
#if QT_VERSION >= 0x050600
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
|
||||
#endif
|
||||
|
||||
BitcoinApplication app(*node, argc, argv);
|
||||
|
||||
// Register meta types used for QMetaObject::invokeMethod
|
||||
qRegisterMetaType< bool* >();
|
||||
// Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
|
||||
|
||||
@@ -335,8 +335,9 @@ void BitcoinGUI::createActions()
|
||||
openAction->setStatusTip(tr("Open a bitcoin: URI or payment request"));
|
||||
|
||||
m_open_wallet_action = new QAction(tr("Open Wallet"), this);
|
||||
m_open_wallet_action->setMenu(new QMenu(this));
|
||||
m_open_wallet_action->setEnabled(false);
|
||||
m_open_wallet_action->setStatusTip(tr("Open a wallet"));
|
||||
m_open_wallet_menu = new QMenu(this);
|
||||
|
||||
m_close_wallet_action = new QAction(tr("Close Wallet..."), this);
|
||||
m_close_wallet_action->setStatusTip(tr("Close wallet"));
|
||||
@@ -368,11 +369,20 @@ void BitcoinGUI::createActions()
|
||||
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
|
||||
connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
|
||||
connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked);
|
||||
connect(m_open_wallet_action->menu(), &QMenu::aboutToShow, [this] {
|
||||
m_open_wallet_action->menu()->clear();
|
||||
for (std::string path : m_wallet_controller->getWalletsAvailableToOpen()) {
|
||||
connect(m_open_wallet_menu, &QMenu::aboutToShow, [this] {
|
||||
m_open_wallet_menu->clear();
|
||||
std::vector<std::string> available_wallets = m_wallet_controller->getWalletsAvailableToOpen();
|
||||
std::vector<std::string> wallets = m_node.listWalletDir();
|
||||
for (const auto& path : wallets) {
|
||||
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
|
||||
QAction* action = m_open_wallet_action->menu()->addAction(name);
|
||||
QAction* action = m_open_wallet_menu->addAction(name);
|
||||
|
||||
if (std::find(available_wallets.begin(), available_wallets.end(), path) == available_wallets.end()) {
|
||||
// This wallet is already loaded
|
||||
action->setEnabled(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
connect(action, &QAction::triggered, [this, name, path] {
|
||||
OpenWalletActivity* activity = m_wallet_controller->openWallet(path);
|
||||
|
||||
@@ -400,6 +410,10 @@ void BitcoinGUI::createActions()
|
||||
assert(invoked);
|
||||
});
|
||||
}
|
||||
if (wallets.empty()) {
|
||||
QAction* action = m_open_wallet_menu->addAction(tr("No wallets available"));
|
||||
action->setEnabled(false);
|
||||
}
|
||||
});
|
||||
connect(m_close_wallet_action, &QAction::triggered, [this] {
|
||||
m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this);
|
||||
@@ -620,6 +634,9 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller)
|
||||
|
||||
m_wallet_controller = wallet_controller;
|
||||
|
||||
m_open_wallet_action->setEnabled(true);
|
||||
m_open_wallet_action->setMenu(m_open_wallet_menu);
|
||||
|
||||
connect(wallet_controller, &WalletController::walletAdded, this, &BitcoinGUI::addWallet);
|
||||
connect(wallet_controller, &WalletController::walletRemoved, this, &BitcoinGUI::removeWallet);
|
||||
|
||||
@@ -1328,6 +1345,7 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress)
|
||||
if (progressDialog) {
|
||||
progressDialog->close();
|
||||
progressDialog->deleteLater();
|
||||
progressDialog = nullptr;
|
||||
}
|
||||
} else if (progressDialog) {
|
||||
progressDialog->setValue(nProgress);
|
||||
|
||||
@@ -148,6 +148,7 @@ private:
|
||||
QAction* openAction = nullptr;
|
||||
QAction* showHelpMessageAction = nullptr;
|
||||
QAction* m_open_wallet_action{nullptr};
|
||||
QMenu* m_open_wallet_menu{nullptr};
|
||||
QAction* m_close_wallet_action{nullptr};
|
||||
QAction* m_wallet_selector_label_action = nullptr;
|
||||
QAction* m_wallet_selector_action = nullptr;
|
||||
|
||||
@@ -636,6 +636,9 @@
|
||||
<property name="placeholderText">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
|
||||
#include <objc/objc-runtime.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <QProcess>
|
||||
#endif
|
||||
|
||||
namespace GUIUtil {
|
||||
@@ -392,7 +393,15 @@ bool openBitcoinConf()
|
||||
configFile.close();
|
||||
|
||||
/* Open bitcoin.conf with the associated application */
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
|
||||
bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
|
||||
#ifdef Q_OS_MAC
|
||||
// Workaround for macOS-specific behavior; see #15409.
|
||||
if (!res) {
|
||||
res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", boostPathToQString(pathConfig)});
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ToolTipToRichTextFilter::ToolTipToRichTextFilter(int _size_threshold, QObject *parent) :
|
||||
|
||||
@@ -680,6 +680,9 @@ void RPCConsole::setClientModel(ClientModel *model)
|
||||
wordList.sort();
|
||||
autoCompleter = new QCompleter(wordList, this);
|
||||
autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel);
|
||||
// ui->lineEdit is initially disabled because running commands is only
|
||||
// possible from now on.
|
||||
ui->lineEdit->setEnabled(true);
|
||||
ui->lineEdit->setCompleter(autoCompleter);
|
||||
autoCompleter->popup()->installEventFilter(this);
|
||||
// Start thread to execute RPC commands.
|
||||
|
||||
@@ -129,7 +129,7 @@ HelpMessageDialog::~HelpMessageDialog()
|
||||
void HelpMessageDialog::printToConsole()
|
||||
{
|
||||
// On other operating systems, the expected action is to print the message to the console.
|
||||
fprintf(stdout, "%s\n", qPrintable(text));
|
||||
tfm::format(std::cout, "%s\n", qPrintable(text));
|
||||
}
|
||||
|
||||
void HelpMessageDialog::showOrPrint()
|
||||
|
||||
@@ -316,6 +316,7 @@ void WalletView::showProgress(const QString &title, int nProgress)
|
||||
if (progressDialog) {
|
||||
progressDialog->close();
|
||||
progressDialog->deleteLater();
|
||||
progressDialog = nullptr;
|
||||
}
|
||||
} else if (progressDialog) {
|
||||
if (progressDialog->wasCanceled()) {
|
||||
|
||||
@@ -1030,7 +1030,12 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
|
||||
}
|
||||
|
||||
PruneBlockFilesManual(height);
|
||||
return uint64_t(height);
|
||||
const CBlockIndex* block = ::chainActive.Tip();
|
||||
assert(block);
|
||||
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
|
||||
block = block->pprev;
|
||||
}
|
||||
return uint64_t(block->nHeight);
|
||||
}
|
||||
|
||||
static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
|
||||
|
||||
@@ -128,9 +128,9 @@ static UniValue createmultisig(const JSONRPCRequest& request)
|
||||
}
|
||||
|
||||
// Construct using pay-to-script-hash:
|
||||
const CScript inner = CreateMultisigRedeemscript(required, pubkeys);
|
||||
CBasicKeyStore keystore;
|
||||
const CTxDestination dest = AddAndGetDestinationForScript(keystore, inner, output_type);
|
||||
CScript inner;
|
||||
const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("address", EncodeDestination(dest));
|
||||
|
||||
@@ -495,7 +495,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
|
||||
obj.pushKV("protocolversion",PROTOCOL_VERSION);
|
||||
if(g_connman)
|
||||
obj.pushKV("localservices", strprintf("%016x", g_connman->GetLocalServices()));
|
||||
obj.pushKV("localrelay", fRelayTxes);
|
||||
obj.pushKV("localrelay", g_relay_txes);
|
||||
obj.pushKV("timeoffset", GetTimeOffset());
|
||||
if (g_connman) {
|
||||
obj.pushKV("networkactive", g_connman->GetNetworkActive());
|
||||
|
||||
@@ -535,14 +535,17 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
|
||||
|
||||
static UniValue decoderawtransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
|
||||
throw std::runtime_error(
|
||||
RPCHelpMan{"decoderawtransaction",
|
||||
const RPCHelpMan help{"decoderawtransaction",
|
||||
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n",
|
||||
{
|
||||
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction hex string"},
|
||||
{"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction\n"
|
||||
" If iswitness is not present, heuristic tests will be used in decoding"},
|
||||
{"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
|
||||
"If iswitness is not present, heuristic tests will be used in decoding.\n"
|
||||
"If true, only witness deserialization will be tried.\n"
|
||||
"If false, only non-witness deserialization will be tried.\n"
|
||||
"This boolean should reflect whether the transaction has inputs\n"
|
||||
"(e.g. fully valid, or on-chain transactions), if known by the caller."
|
||||
},
|
||||
},
|
||||
RPCResult{
|
||||
"{\n"
|
||||
@@ -589,7 +592,11 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
|
||||
HelpExampleCli("decoderawtransaction", "\"hexstring\"")
|
||||
+ HelpExampleRpc("decoderawtransaction", "\"hexstring\"")
|
||||
},
|
||||
}.ToString());
|
||||
};
|
||||
|
||||
if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
|
||||
throw std::runtime_error(help.ToString());
|
||||
}
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
|
||||
|
||||
@@ -996,8 +1003,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
|
||||
"}\n"
|
||||
},
|
||||
RPCExamples{
|
||||
HelpExampleCli("signrawtransactionwithkey", "\"myhex\"")
|
||||
+ HelpExampleRpc("signrawtransactionwithkey", "\"myhex\"")
|
||||
HelpExampleCli("signrawtransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"")
|
||||
+ HelpExampleRpc("signrawtransactionwithkey", "\"myhex\", \"[\\\"key1\\\",\\\"key2\\\"]\"")
|
||||
},
|
||||
}.ToString());
|
||||
|
||||
@@ -1643,19 +1650,20 @@ UniValue createpsbt(const JSONRPCRequest& request)
|
||||
|
||||
UniValue converttopsbt(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
|
||||
throw std::runtime_error(
|
||||
RPCHelpMan{"converttopsbt",
|
||||
const RPCHelpMan help{"converttopsbt",
|
||||
"\nConverts a network serialized transaction to a PSBT. This should be used only with createrawtransaction and fundrawtransaction\n"
|
||||
"createpsbt and walletcreatefundedpsbt should be used for new applications.\n",
|
||||
{
|
||||
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of a raw transaction"},
|
||||
{"permitsigdata", RPCArg::Type::BOOL, /* default */ "false", "If true, any signatures in the input will be discarded and conversion.\n"
|
||||
{"permitsigdata", RPCArg::Type::BOOL, /* default */ "false", "If true, any signatures in the input will be discarded and conversion\n"
|
||||
" will continue. If false, RPC will fail if any signatures are present."},
|
||||
{"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
|
||||
" If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n"
|
||||
" will be tried. If false, only non-witness deserialization will be tried. Only has an effect if\n"
|
||||
" permitsigdata is true."},
|
||||
"If iswitness is not present, heuristic tests will be used in decoding.\n"
|
||||
"If true, only witness deserialization will be tried.\n"
|
||||
"If false, only non-witness deserialization will be tried.\n"
|
||||
"This boolean should reflect whether the transaction has inputs\n"
|
||||
"(e.g. fully valid, or on-chain transactions), if known by the caller."
|
||||
},
|
||||
},
|
||||
RPCResult{
|
||||
" \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n"
|
||||
@@ -1666,8 +1674,11 @@ UniValue converttopsbt(const JSONRPCRequest& request)
|
||||
"\nConvert the transaction to a PSBT\n"
|
||||
+ HelpExampleCli("converttopsbt", "\"rawtransaction\"")
|
||||
},
|
||||
}.ToString());
|
||||
};
|
||||
|
||||
if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
|
||||
throw std::runtime_error(help.ToString());
|
||||
}
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VBOOL}, true);
|
||||
|
||||
@@ -1676,8 +1687,8 @@ UniValue converttopsbt(const JSONRPCRequest& request)
|
||||
bool permitsigdata = request.params[1].isNull() ? false : request.params[1].get_bool();
|
||||
bool witness_specified = !request.params[2].isNull();
|
||||
bool iswitness = witness_specified ? request.params[2].get_bool() : false;
|
||||
bool try_witness = permitsigdata ? (witness_specified ? iswitness : true) : false;
|
||||
bool try_no_witness = permitsigdata ? (witness_specified ? !iswitness : true) : true;
|
||||
const bool try_witness = witness_specified ? iswitness : true;
|
||||
const bool try_no_witness = witness_specified ? !iswitness : true;
|
||||
if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <key_io.h>
|
||||
#include <keystore.h>
|
||||
#include <policy/fees.h>
|
||||
#include <outputtype.h>
|
||||
#include <rpc/util.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/strencodings.h>
|
||||
@@ -46,8 +47,8 @@ CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in)
|
||||
return vchPubKey;
|
||||
}
|
||||
|
||||
// Creates a multisig redeemscript from a given list of public keys and number required.
|
||||
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys)
|
||||
// Creates a multisig address from a given list of public keys, number of signatures required, and the address type
|
||||
CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, CKeyStore& keystore, CScript& script_out)
|
||||
{
|
||||
// Gather public keys
|
||||
if (required < 1) {
|
||||
@@ -60,13 +61,24 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Number of keys involved in the multisignature address creation > 16\nReduce the number");
|
||||
}
|
||||
|
||||
CScript result = GetScriptForMultisig(required, pubkeys);
|
||||
script_out = GetScriptForMultisig(required, pubkeys);
|
||||
|
||||
if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE)));
|
||||
if (script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
|
||||
}
|
||||
|
||||
return result;
|
||||
// Check if any keys are uncompressed. If so, the type is legacy
|
||||
for (const CPubKey& pk : pubkeys) {
|
||||
if (!pk.IsCompressed()) {
|
||||
type = OutputType::LEGACY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the address
|
||||
CTxDestination dest = AddAndGetDestinationForScript(keystore, script_out, type);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
class DescribeAddressVisitor : public boost::static_visitor<UniValue>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define BITCOIN_RPC_UTIL_H
|
||||
|
||||
#include <node/transaction.h>
|
||||
#include <outputtype.h>
|
||||
#include <pubkey.h>
|
||||
#include <rpc/protocol.h>
|
||||
#include <script/standard.h>
|
||||
@@ -28,7 +29,7 @@ extern InitInterfaces* g_rpc_interfaces;
|
||||
|
||||
CPubKey HexToPubKey(const std::string& hex_in);
|
||||
CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in);
|
||||
CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys);
|
||||
CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, CKeyStore& keystore, CScript& script_out);
|
||||
|
||||
UniValue DescribeAddress(const CTxDestination& dest);
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
|
||||
LogPrintf(" %s\n", i.second.ToString());
|
||||
}
|
||||
if (g_debug_lockorder_abort) {
|
||||
fprintf(stderr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
|
||||
tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
|
||||
abort();
|
||||
}
|
||||
throw std::logic_error("potential deadlock detected");
|
||||
@@ -162,7 +162,7 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
|
||||
for (const std::pair<void*, CLockLocation>& i : g_lockstack)
|
||||
if (i.first == cs)
|
||||
return;
|
||||
fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
|
||||
tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
|
||||
abort();
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLi
|
||||
{
|
||||
for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
|
||||
if (i.first == cs) {
|
||||
fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
|
||||
tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
43
src/test/compilerbug_tests.cpp
Normal file
43
src/test/compilerbug_tests.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2019 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 <test/test_bitcoin.h>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(compilerbug_tests, BasicTestingSetup)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// This block will also be built under clang, which is fine (as it supports noinline)
|
||||
void __attribute__ ((noinline)) set_one(unsigned char* ptr)
|
||||
{
|
||||
*ptr = 1;
|
||||
}
|
||||
|
||||
int __attribute__ ((noinline)) check_zero(unsigned char const* in, unsigned int len)
|
||||
{
|
||||
for (unsigned int i = 0; i < len; ++i) {
|
||||
if (in[i] != 0) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void set_one_on_stack() {
|
||||
unsigned char buf[1];
|
||||
set_one(buf);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(gccbug_90348) {
|
||||
// Test for GCC bug 90348. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348
|
||||
for (int i = 0; i <= 4; ++i) {
|
||||
unsigned char in[4];
|
||||
for (int j = 0; j < i; ++j) {
|
||||
in[j] = 0;
|
||||
set_one_on_stack(); // Apparently modifies in[0]
|
||||
}
|
||||
BOOST_CHECK(check_zero(in, i));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
@@ -1063,6 +1063,7 @@ std::string format(const std::string &fmt, const Args&... args)
|
||||
|
||||
} // namespace tinyformat
|
||||
|
||||
/** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */
|
||||
#define strprintf tfm::format
|
||||
|
||||
#endif // TINYFORMAT_H_INCLUDED
|
||||
|
||||
@@ -675,7 +675,7 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
|
||||
{
|
||||
std::string message = FormatException(pex, pszThread);
|
||||
LogPrintf("\n\n************************\n%s\n", message);
|
||||
fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
|
||||
tfm::format(std::cerr, "\n\n************************\n%s\n", message.c_str());
|
||||
}
|
||||
|
||||
fs::path GetDefaultDataDir()
|
||||
@@ -935,7 +935,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
|
||||
}
|
||||
}
|
||||
for (const std::string& to_include : includeconf) {
|
||||
fprintf(stderr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str());
|
||||
tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,8 +1028,8 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
|
||||
}
|
||||
|
||||
// Construct using pay-to-script-hash:
|
||||
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
|
||||
CTxDestination dest = AddAndGetDestinationForScript(*pwallet, inner, output_type);
|
||||
CScript inner;
|
||||
CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, *pwallet, inner);
|
||||
pwallet->SetAddressBook(dest, label, "send");
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
@@ -3040,9 +3040,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
|
||||
throw std::runtime_error(
|
||||
RPCHelpMan{"fundrawtransaction",
|
||||
const RPCHelpMan help{"fundrawtransaction",
|
||||
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
|
||||
"This will not modify existing inputs, and will add at most one change output to the outputs.\n"
|
||||
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
|
||||
@@ -3081,8 +3079,13 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
|
||||
" \"CONSERVATIVE\""},
|
||||
},
|
||||
"options"},
|
||||
{"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction \n"
|
||||
" If iswitness is not present, heuristic tests will be used in decoding"},
|
||||
{"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
|
||||
"If iswitness is not present, heuristic tests will be used in decoding.\n"
|
||||
"If true, only witness deserialization will be tried.\n"
|
||||
"If false, only non-witness deserialization will be tried.\n"
|
||||
"This boolean should reflect whether the transaction has inputs\n"
|
||||
"(e.g. fully valid, or on-chain transactions), if known by the caller."
|
||||
},
|
||||
},
|
||||
RPCResult{
|
||||
"{\n"
|
||||
@@ -3101,7 +3104,11 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
|
||||
"\nSend the transaction\n"
|
||||
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
|
||||
},
|
||||
}.ToString());
|
||||
};
|
||||
|
||||
if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
|
||||
throw std::runtime_error(help.ToString());
|
||||
}
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ static void WalletToolReleaseWallet(CWallet* wallet)
|
||||
static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path)
|
||||
{
|
||||
if (fs::exists(path)) {
|
||||
fprintf(stderr, "Error: File exists already\n");
|
||||
tfm::format(std::cerr, "Error: File exists already\n");
|
||||
return nullptr;
|
||||
}
|
||||
// dummy chain interface
|
||||
@@ -33,7 +33,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
|
||||
bool first_run = true;
|
||||
DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run);
|
||||
if (load_wallet_ret != DBErrors::LOAD_OK) {
|
||||
fprintf(stderr, "Error creating %s", name.c_str());
|
||||
tfm::format(std::cerr, "Error creating %s", name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
|
||||
CPubKey seed = wallet_instance->GenerateNewSeed();
|
||||
wallet_instance->SetHDSeed(seed);
|
||||
|
||||
fprintf(stdout, "Topping up keypool...\n");
|
||||
tfm::format(std::cout, "Topping up keypool...\n");
|
||||
wallet_instance->TopUpKeyPool();
|
||||
return wallet_instance;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ static std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::
|
||||
static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path)
|
||||
{
|
||||
if (!fs::exists(path)) {
|
||||
fprintf(stderr, "Error: Wallet files does not exist\n");
|
||||
tfm::format(std::cerr, "Error: Wallet files does not exist\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -62,29 +62,29 @@ static std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::pa
|
||||
try {
|
||||
bool first_run;
|
||||
load_wallet_ret = wallet_instance->LoadWallet(first_run);
|
||||
} catch (const std::runtime_error) {
|
||||
fprintf(stderr, "Error loading %s. Is wallet being used by another process?\n", name.c_str());
|
||||
} catch (const std::runtime_error&) {
|
||||
tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (load_wallet_ret != DBErrors::LOAD_OK) {
|
||||
wallet_instance = nullptr;
|
||||
if (load_wallet_ret == DBErrors::CORRUPT) {
|
||||
fprintf(stderr, "Error loading %s: Wallet corrupted", name.c_str());
|
||||
tfm::format(std::cerr, "Error loading %s: Wallet corrupted", name.c_str());
|
||||
return nullptr;
|
||||
} else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) {
|
||||
fprintf(stderr, "Error reading %s! All keys read correctly, but transaction data"
|
||||
tfm::format(std::cerr, "Error reading %s! All keys read correctly, but transaction data"
|
||||
" or address book entries might be missing or incorrect.",
|
||||
name.c_str());
|
||||
} else if (load_wallet_ret == DBErrors::TOO_NEW) {
|
||||
fprintf(stderr, "Error loading %s: Wallet requires newer version of %s",
|
||||
tfm::format(std::cerr, "Error loading %s: Wallet requires newer version of %s",
|
||||
name.c_str(), PACKAGE_NAME);
|
||||
return nullptr;
|
||||
} else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
|
||||
fprintf(stderr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
|
||||
tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
|
||||
return nullptr;
|
||||
} else {
|
||||
fprintf(stderr, "Error loading %s", name.c_str());
|
||||
tfm::format(std::cerr, "Error loading %s", name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -96,12 +96,12 @@ static void WalletShowInfo(CWallet* wallet_instance)
|
||||
{
|
||||
LOCK(wallet_instance->cs_wallet);
|
||||
|
||||
fprintf(stdout, "Wallet info\n===========\n");
|
||||
fprintf(stdout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
|
||||
fprintf(stdout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes");
|
||||
fprintf(stdout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
|
||||
fprintf(stdout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
|
||||
fprintf(stdout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size());
|
||||
tfm::format(std::cout, "Wallet info\n===========\n");
|
||||
tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
|
||||
tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain().seed_id.IsNull() ? "no" : "yes");
|
||||
tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
|
||||
tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
|
||||
tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size());
|
||||
}
|
||||
|
||||
bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
|
||||
@@ -116,12 +116,12 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
|
||||
}
|
||||
} else if (command == "info") {
|
||||
if (!fs::exists(path)) {
|
||||
fprintf(stderr, "Error: no wallet file at %s\n", name.c_str());
|
||||
tfm::format(std::cerr, "Error: no wallet file at %s\n", name.c_str());
|
||||
return false;
|
||||
}
|
||||
std::string error;
|
||||
if (!WalletBatch::VerifyEnvironment(path, error)) {
|
||||
fprintf(stderr, "Error loading %s. Is wallet being used by other process?\n", name.c_str());
|
||||
tfm::format(std::cerr, "Error loading %s. Is wallet being used by other process?\n", name.c_str());
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
|
||||
@@ -129,7 +129,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
|
||||
WalletShowInfo(wallet_instance.get());
|
||||
wallet_instance->Flush(true);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid command: %s\n", command.c_str());
|
||||
tfm::format(std::cerr, "Invalid command: %s\n", command.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,9 +71,13 @@ class InputMissing(BadTxTemplate):
|
||||
reject_reason = "bad-txns-vin-empty"
|
||||
expect_disconnect = False
|
||||
|
||||
# We use a blank transaction here to make sure
|
||||
# it is interpreted as a non-witness transaction.
|
||||
# Otherwise the transaction will fail the
|
||||
# "surpufluous witness" check during deserialization
|
||||
# rather than the input count check.
|
||||
def get_tx(self):
|
||||
tx = CTransaction()
|
||||
tx.vout.append(CTxOut(0, sc.CScript([sc.OP_TRUE] * 100)))
|
||||
tx.calc_sha256()
|
||||
return tx
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ Start three nodes:
|
||||
import time
|
||||
|
||||
from test_framework.blocktools import (create_block, create_coinbase)
|
||||
from test_framework.key import CECKey
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.messages import (
|
||||
CBlockHeader,
|
||||
COutPoint,
|
||||
@@ -104,9 +104,9 @@ class AssumeValidTest(BitcoinTestFramework):
|
||||
self.blocks = []
|
||||
|
||||
# Get a pubkey for the coinbase TXO
|
||||
coinbase_key = CECKey()
|
||||
coinbase_key.set_secretbytes(b"horsebattery")
|
||||
coinbase_pubkey = coinbase_key.get_pubkey()
|
||||
coinbase_key = ECKey()
|
||||
coinbase_key.generate()
|
||||
coinbase_pubkey = coinbase_key.get_pubkey().get_bytes()
|
||||
|
||||
# Create the first block with a coinbase output to our key
|
||||
height = 1
|
||||
|
||||
@@ -14,7 +14,7 @@ from test_framework.blocktools import (
|
||||
get_legacy_sigopcount_block,
|
||||
MAX_BLOCK_SIGOPS,
|
||||
)
|
||||
from test_framework.key import CECKey
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.messages import (
|
||||
CBlock,
|
||||
COIN,
|
||||
@@ -86,9 +86,9 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
self.bootstrap_p2p() # Add one p2p connection to the node
|
||||
|
||||
self.block_heights = {}
|
||||
self.coinbase_key = CECKey()
|
||||
self.coinbase_key.set_secretbytes(b"horsebattery")
|
||||
self.coinbase_pubkey = self.coinbase_key.get_pubkey()
|
||||
self.coinbase_key = ECKey()
|
||||
self.coinbase_key.generate()
|
||||
self.coinbase_pubkey = self.coinbase_key.get_pubkey().get_bytes()
|
||||
self.tip = None
|
||||
self.blocks = {}
|
||||
self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16)
|
||||
@@ -146,20 +146,6 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
badtx = template.get_tx()
|
||||
if TxTemplate != invalid_txs.InputMissing:
|
||||
self.sign_tx(badtx, attempt_spend_tx)
|
||||
else:
|
||||
# Segwit is active in regtest at this point, so to deserialize a
|
||||
# transaction without any inputs correctly, we set the outputs
|
||||
# to an empty list. This is a hack, as the serialization of an
|
||||
# empty list of outputs is deserialized as flags==0 and thus
|
||||
# deserialization of the outputs is skipped.
|
||||
# A policy check requires "loose" txs to be of a minimum size,
|
||||
# so vtx is not set to be empty in the TxTemplate class and we
|
||||
# only apply the workaround where txs are not "loose", i.e. in
|
||||
# blocks.
|
||||
#
|
||||
# The workaround has the purpose that both sides calculate
|
||||
# the same tx hash in the merkle tree
|
||||
badtx.vout = []
|
||||
badtx.rehash()
|
||||
badblock = self.update_block(blockname, [badtx])
|
||||
self.sync_blocks(
|
||||
@@ -528,7 +514,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
tx.vin.append(CTxIn(COutPoint(b39.vtx[i].sha256, 0), b''))
|
||||
# Note: must pass the redeem_script (not p2sh_script) to the signature hash function
|
||||
(sighash, err) = SignatureHash(redeem_script, tx, 1, SIGHASH_ALL)
|
||||
sig = self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))
|
||||
sig = self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))
|
||||
scriptSig = CScript([sig, redeem_script])
|
||||
|
||||
tx.vin[1].scriptSig = scriptSig
|
||||
@@ -1284,7 +1270,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
tx.vin[0].scriptSig = CScript()
|
||||
return
|
||||
(sighash, err) = SignatureHash(spend_tx.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL)
|
||||
tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))])
|
||||
tx.vin[0].scriptSig = CScript([self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))])
|
||||
|
||||
def create_and_sign_transaction(self, spend_tx, value, script=CScript([OP_TRUE])):
|
||||
tx = self.create_tx(spend_tx, 0, value, script)
|
||||
|
||||
@@ -10,12 +10,10 @@ This test takes 30 mins or more (up to 2 hours)
|
||||
"""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error, connect_nodes, mine_large_block, sync_blocks, wait_until
|
||||
from test_framework.util import assert_equal, assert_raises_rpc_error, connect_nodes, mine_large_block, sync_blocks, wait_until
|
||||
|
||||
import os
|
||||
|
||||
MIN_BLOCKS_TO_KEEP = 288
|
||||
|
||||
# Rescans start at the earliest block up to 2 hours before a key timestamp, so
|
||||
# the manual prune RPC avoids pruning blocks in the same window to be
|
||||
# compatible with pruning based on key creation time.
|
||||
@@ -250,20 +248,9 @@ class PruneTest(BitcoinTestFramework):
|
||||
else:
|
||||
return index
|
||||
|
||||
def prune(index, expected_ret=None):
|
||||
def prune(index):
|
||||
ret = node.pruneblockchain(height=height(index))
|
||||
# Check the return value. When use_timestamp is True, just check
|
||||
# that the return value is less than or equal to the expected
|
||||
# value, because when more than one block is generated per second,
|
||||
# a timestamp will not be granular enough to uniquely identify an
|
||||
# individual block.
|
||||
if expected_ret is None:
|
||||
expected_ret = index
|
||||
if use_timestamp:
|
||||
assert_greater_than(ret, 0)
|
||||
assert_greater_than(expected_ret + 1, ret)
|
||||
else:
|
||||
assert_equal(ret, expected_ret)
|
||||
assert_equal(ret, node.getblockchaininfo()['pruneheight'])
|
||||
|
||||
def has_block(index):
|
||||
return os.path.isfile(os.path.join(self.nodes[node_number].datadir, "regtest", "blocks", "blk{:05}.dat".format(index)))
|
||||
@@ -308,7 +295,7 @@ class PruneTest(BitcoinTestFramework):
|
||||
raise AssertionError("blk00001.dat is still there, should be pruned by now")
|
||||
|
||||
# height=1000 should not prune anything more, because tip-288 is in blk00002.dat.
|
||||
prune(1000, 1001 - MIN_BLOCKS_TO_KEEP)
|
||||
prune(1000)
|
||||
if not has_block(2):
|
||||
raise AssertionError("blk00002.dat is still there, should be pruned by now")
|
||||
|
||||
|
||||
58
test/functional/p2p_blocksonly.py
Executable file
58
test/functional/p2p_blocksonly.py
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 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 p2p blocksonly"""
|
||||
|
||||
from test_framework.messages import msg_tx, CTransaction, FromHex
|
||||
from test_framework.mininode import P2PInterface
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
|
||||
class P2PBlocksOnly(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = False
|
||||
self.num_nodes = 1
|
||||
self.extra_args = [["-blocksonly"]]
|
||||
|
||||
def run_test(self):
|
||||
self.nodes[0].add_p2p_connection(P2PInterface())
|
||||
|
||||
self.log.info('Check that txs from p2p are rejected')
|
||||
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
|
||||
rawtx = self.nodes[0].createrawtransaction(
|
||||
inputs=[{
|
||||
'txid': prevtx['txid'],
|
||||
'vout': 0
|
||||
}],
|
||||
outputs=[{
|
||||
self.nodes[0].get_deterministic_priv_key().address: 50 - 0.00125
|
||||
}],
|
||||
)
|
||||
sigtx = self.nodes[0].signrawtransactionwithkey(
|
||||
hexstring=rawtx,
|
||||
privkeys=[self.nodes[0].get_deterministic_priv_key().key],
|
||||
prevtxs=[{
|
||||
'txid': prevtx['txid'],
|
||||
'vout': 0,
|
||||
'scriptPubKey': prevtx['vout'][0]['scriptPubKey']['hex'],
|
||||
}],
|
||||
)['hex']
|
||||
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
|
||||
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
|
||||
self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
|
||||
self.nodes[0].p2p.sync_with_ping()
|
||||
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
|
||||
|
||||
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
|
||||
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
|
||||
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
|
||||
with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid)]):
|
||||
self.nodes[0].sendrawtransaction(sigtx)
|
||||
self.nodes[0].p2p.wait_for_tx(txid)
|
||||
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
P2PBlocksOnly().main()
|
||||
@@ -10,7 +10,7 @@ import struct
|
||||
import time
|
||||
|
||||
from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, get_witness_script, WITNESS_COMMITMENT_HEADER
|
||||
from test_framework.key import CECKey, CPubKey
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.messages import (
|
||||
BIP125_SEQUENCE_NUMBER,
|
||||
CBlock,
|
||||
@@ -37,6 +37,7 @@ from test_framework.messages import (
|
||||
ser_vector,
|
||||
sha256,
|
||||
uint256_from_str,
|
||||
FromHex,
|
||||
)
|
||||
from test_framework.mininode import (
|
||||
P2PInterface,
|
||||
@@ -81,6 +82,7 @@ from test_framework.util import (
|
||||
hex_str_to_bytes,
|
||||
sync_blocks,
|
||||
sync_mempools,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
|
||||
# The versionbit bit used to signal activation of SegWit
|
||||
@@ -104,7 +106,7 @@ def get_p2pkh_script(pubkeyhash):
|
||||
def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key):
|
||||
"""Add signature for a P2PK witness program."""
|
||||
tx_hash = SegwitVersion1SignatureHash(script, tx_to, in_idx, hashtype, value)
|
||||
signature = key.sign(tx_hash) + chr(hashtype).encode('latin-1')
|
||||
signature = key.sign_ecdsa(tx_hash) + chr(hashtype).encode('latin-1')
|
||||
tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [signature, script]
|
||||
tx_to.rehash()
|
||||
|
||||
@@ -273,6 +275,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
self.test_non_standard_witness()
|
||||
self.test_upgrade_after_activation()
|
||||
self.test_witness_sigops()
|
||||
self.test_superfluous_witness()
|
||||
|
||||
# Individual tests
|
||||
|
||||
@@ -1483,10 +1486,9 @@ class SegWitTest(BitcoinTestFramework):
|
||||
|
||||
# Segwit transactions using uncompressed pubkeys are not accepted
|
||||
# under default policy, but should still pass consensus.
|
||||
key = CECKey()
|
||||
key.set_secretbytes(b"9")
|
||||
key.set_compressed(False)
|
||||
pubkey = CPubKey(key.get_pubkey())
|
||||
key = ECKey()
|
||||
key.generate(False)
|
||||
pubkey = key.get_pubkey().get_bytes()
|
||||
assert_equal(len(pubkey), 65) # This should be an uncompressed pubkey
|
||||
|
||||
utxo = self.utxo.pop(0)
|
||||
@@ -1516,7 +1518,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_wsh))
|
||||
script = get_p2pkh_script(pubkeyhash)
|
||||
sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
|
||||
signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
tx2.wit.vtxinwit.append(CTxInWitness())
|
||||
tx2.wit.vtxinwit[0].scriptWitness.stack = [signature, pubkey]
|
||||
tx2.rehash()
|
||||
@@ -1570,7 +1572,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
tx5.vin.append(CTxIn(COutPoint(tx4.sha256, 0), b""))
|
||||
tx5.vout.append(CTxOut(tx4.vout[0].nValue - 1000, CScript([OP_TRUE])))
|
||||
(sig_hash, err) = SignatureHash(script_pubkey, tx5, 0, SIGHASH_ALL)
|
||||
signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
tx5.vin[0].scriptSig = CScript([signature, pubkey])
|
||||
tx5.rehash()
|
||||
# Should pass policy and consensus.
|
||||
@@ -1583,9 +1585,9 @@ class SegWitTest(BitcoinTestFramework):
|
||||
@subtest
|
||||
def test_signature_version_1(self):
|
||||
|
||||
key = CECKey()
|
||||
key.set_secretbytes(b"9")
|
||||
pubkey = CPubKey(key.get_pubkey())
|
||||
key = ECKey()
|
||||
key.generate()
|
||||
pubkey = key.get_pubkey().get_bytes()
|
||||
|
||||
witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
|
||||
witness_hash = sha256(witness_program)
|
||||
@@ -1720,7 +1722,7 @@ class SegWitTest(BitcoinTestFramework):
|
||||
|
||||
script = get_p2pkh_script(pubkeyhash)
|
||||
sig_hash = SegwitVersion1SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
|
||||
signature = key.sign(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
|
||||
|
||||
# Check that we can't have a scriptSig
|
||||
tx2.vin[0].scriptSig = CScript([signature, pubkey])
|
||||
@@ -2039,5 +2041,51 @@ class SegWitTest(BitcoinTestFramework):
|
||||
|
||||
# TODO: test p2sh sigop counting
|
||||
|
||||
def test_superfluous_witness(self):
|
||||
# Serialization of tx that puts witness flag to 3 always
|
||||
def serialize_with_bogus_witness(tx):
|
||||
flags = 3
|
||||
r = b""
|
||||
r += struct.pack("<i", tx.nVersion)
|
||||
if flags:
|
||||
dummy = []
|
||||
r += ser_vector(dummy)
|
||||
r += struct.pack("<B", flags)
|
||||
r += ser_vector(tx.vin)
|
||||
r += ser_vector(tx.vout)
|
||||
if flags & 1:
|
||||
if (len(tx.wit.vtxinwit) != len(tx.vin)):
|
||||
# vtxinwit must have the same length as vin
|
||||
tx.wit.vtxinwit = tx.wit.vtxinwit[:len(tx.vin)]
|
||||
for i in range(len(tx.wit.vtxinwit), len(tx.vin)):
|
||||
tx.wit.vtxinwit.append(CTxInWitness())
|
||||
r += tx.wit.serialize()
|
||||
r += struct.pack("<I", tx.nLockTime)
|
||||
return r
|
||||
|
||||
class msg_bogus_tx(msg_tx):
|
||||
def serialize(self):
|
||||
return serialize_with_bogus_witness(self.tx)
|
||||
|
||||
self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(address_type='bech32'), 5)
|
||||
self.nodes[0].generate(1)
|
||||
unspent = next(u for u in self.nodes[0].listunspent() if u['spendable'] and u['address'].startswith('bcrt'))
|
||||
|
||||
raw = self.nodes[0].createrawtransaction([{"txid": unspent['txid'], "vout": unspent['vout']}], {self.nodes[0].getnewaddress(): 1})
|
||||
tx = FromHex(CTransaction(), raw)
|
||||
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
|
||||
with self.nodes[0].assert_debug_log(['Superfluous witness record']):
|
||||
self.nodes[0].p2p.send_message(msg_bogus_tx(tx))
|
||||
self.nodes[0].p2p.sync_with_ping()
|
||||
raw = self.nodes[0].signrawtransactionwithwallet(raw)
|
||||
assert raw['complete']
|
||||
raw = raw['hex']
|
||||
tx = FromHex(CTransaction(), raw)
|
||||
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, serialize_with_bogus_witness(tx).hex())
|
||||
with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
|
||||
self.nodes[0].p2p.send_message(msg_bogus_tx(tx))
|
||||
self.nodes[0].p2p.sync_with_ping()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
SegWitTest().main()
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2018 The Bitcoin Core developers
|
||||
# Copyright (c) 2015-2019 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 transaction signing using the signrawtransaction* RPCs."""
|
||||
"""Test multisig RPCs"""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_raises_rpc_error,
|
||||
assert_equal,
|
||||
)
|
||||
from test_framework.key import ECPubKey
|
||||
|
||||
import binascii
|
||||
import decimal
|
||||
import itertools
|
||||
|
||||
class RpcCreateMultiSigTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
@@ -17,29 +25,64 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
|
||||
|
||||
def get_keys(self):
|
||||
node0, node1, node2 = self.nodes
|
||||
self.add = [node1.getnewaddress() for _ in range(self.nkeys)]
|
||||
self.pub = [node1.getaddressinfo(a)["pubkey"] for a in self.add]
|
||||
self.priv = [node1.dumpprivkey(a) for a in self.add]
|
||||
add = [node1.getnewaddress() for _ in range(self.nkeys)]
|
||||
self.pub = [node1.getaddressinfo(a)["pubkey"] for a in add]
|
||||
self.priv = [node1.dumpprivkey(a) for a in add]
|
||||
self.final = node2.getnewaddress()
|
||||
|
||||
def run_test(self):
|
||||
node0,node1,node2 = self.nodes
|
||||
node0, node1, node2 = self.nodes
|
||||
|
||||
# 50 BTC each, rest will be 25 BTC each
|
||||
self.check_addmultisigaddress_errors()
|
||||
|
||||
self.log.info('Generating blocks ...')
|
||||
node0.generate(149)
|
||||
self.sync_all()
|
||||
|
||||
self.moved = 0
|
||||
for self.nkeys in [3,5]:
|
||||
for self.nsigs in [2,3]:
|
||||
for self.nkeys in [3, 5]:
|
||||
for self.nsigs in [2, 3]:
|
||||
for self.output_type in ["bech32", "p2sh-segwit", "legacy"]:
|
||||
self.get_keys()
|
||||
self.do_multisig()
|
||||
|
||||
self.checkbalances()
|
||||
|
||||
# Test mixed compressed and uncompressed pubkeys
|
||||
self.log.info('Mixed compressed and uncompressed multisigs are not allowed')
|
||||
pk0 = node0.getaddressinfo(node0.getnewaddress())['pubkey']
|
||||
pk1 = node1.getaddressinfo(node1.getnewaddress())['pubkey']
|
||||
pk2 = node2.getaddressinfo(node2.getnewaddress())['pubkey']
|
||||
|
||||
# decompress pk2
|
||||
pk_obj = ECPubKey()
|
||||
pk_obj.set(binascii.unhexlify(pk2))
|
||||
pk_obj.compressed = False
|
||||
pk2 = binascii.hexlify(pk_obj.get_bytes()).decode()
|
||||
|
||||
# Check all permutations of keys because order matters apparently
|
||||
for keys in itertools.permutations([pk0, pk1, pk2]):
|
||||
# Results should be the same as this legacy one
|
||||
legacy_addr = node0.createmultisig(2, keys, 'legacy')['address']
|
||||
assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'legacy')['address'])
|
||||
|
||||
# Generate addresses with the segwit types. These should all make legacy addresses
|
||||
assert_equal(legacy_addr, node0.createmultisig(2, keys, 'bech32')['address'])
|
||||
assert_equal(legacy_addr, node0.createmultisig(2, keys, 'p2sh-segwit')['address'])
|
||||
assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'bech32')['address'])
|
||||
assert_equal(legacy_addr, node0.addmultisigaddress(2, keys, '', 'p2sh-segwit')['address'])
|
||||
|
||||
def check_addmultisigaddress_errors(self):
|
||||
self.log.info('Check that addmultisigaddress fails when the private keys are missing')
|
||||
addresses = [self.nodes[1].getnewaddress(address_type='legacy') for _ in range(2)]
|
||||
assert_raises_rpc_error(-5, 'no full public key for address', lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses))
|
||||
for a in addresses:
|
||||
# Importing all addresses should not change the result
|
||||
self.nodes[0].importaddress(a)
|
||||
assert_raises_rpc_error(-5, 'no full public key for address', lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses))
|
||||
|
||||
def checkbalances(self):
|
||||
node0,node1,node2 = self.nodes
|
||||
node0, node1, node2 = self.nodes
|
||||
node0.generate(100)
|
||||
self.sync_all()
|
||||
|
||||
@@ -49,13 +92,13 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
|
||||
|
||||
height = node0.getblockchaininfo()["blocks"]
|
||||
assert 150 < height < 350
|
||||
total = 149*50 + (height-149-100)*25
|
||||
total = 149 * 50 + (height - 149 - 100) * 25
|
||||
assert bal1 == 0
|
||||
assert bal2 == self.moved
|
||||
assert bal0+bal1+bal2 == total
|
||||
assert bal0 + bal1 + bal2 == total
|
||||
|
||||
def do_multisig(self):
|
||||
node0,node1,node2 = self.nodes
|
||||
node0, node1, node2 = self.nodes
|
||||
|
||||
msig = node2.createmultisig(self.nsigs, self.pub, self.output_type)
|
||||
madd = msig["address"]
|
||||
@@ -74,7 +117,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
|
||||
txid = node0.sendtoaddress(madd, 40)
|
||||
|
||||
tx = node0.getrawtransaction(txid, True)
|
||||
vout = [v["n"] for v in tx["vout"] if madd in v["scriptPubKey"].get("addresses",[])]
|
||||
vout = [v["n"] for v in tx["vout"] if madd in v["scriptPubKey"].get("addresses", [])]
|
||||
assert len(vout) == 1
|
||||
vout = vout[0]
|
||||
scriptPubKey = tx["vout"][vout]["scriptPubKey"]["hex"]
|
||||
@@ -86,7 +129,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
|
||||
outval = value - decimal.Decimal("0.00001000")
|
||||
rawtx = node2.createrawtransaction([{"txid": txid, "vout": vout}], [{self.final: outval}])
|
||||
|
||||
rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs-1], prevtxs)
|
||||
rawtx2 = node2.signrawtransactionwithkey(rawtx, self.priv[0:self.nsigs - 1], prevtxs)
|
||||
rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
|
||||
|
||||
self.moved += outval
|
||||
@@ -97,5 +140,6 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
|
||||
txinfo = node0.getrawtransaction(tx, True, blk)
|
||||
self.log.info("n/m=%d/%d %s size=%d vsize=%d weight=%d" % (self.nsigs, self.nkeys, self.output_type, txinfo["size"], txinfo["vsize"], txinfo["weight"]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
RpcCreateMultiSigTest().main()
|
||||
|
||||
@@ -144,10 +144,12 @@ class PSBTTest(BitcoinTestFramework):
|
||||
new_psbt = self.nodes[0].converttopsbt(rawtx['hex'])
|
||||
self.nodes[0].decodepsbt(new_psbt)
|
||||
|
||||
# Make sure that a psbt with signatures cannot be converted
|
||||
# Make sure that a non-psbt with signatures cannot be converted
|
||||
# Error could be either "TX decode failed" (segwit inputs causes parsing to fail) or "Inputs must not have scriptSigs and scriptWitnesses"
|
||||
# We must set iswitness=True because the serialized transaction has inputs and is therefore a witness transaction
|
||||
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])
|
||||
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'])
|
||||
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'], False)
|
||||
assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, hexstring=signedtx['hex'], iswitness=True)
|
||||
assert_raises_rpc_error(-22, "", self.nodes[0].converttopsbt, hexstring=signedtx['hex'], permitsigdata=False, iswitness=True)
|
||||
# Unless we allow it to convert and strip signatures
|
||||
self.nodes[0].converttopsbt(signedtx['hex'], True)
|
||||
|
||||
|
||||
@@ -1,226 +1,386 @@
|
||||
# Copyright (c) 2011 Sam Rushing
|
||||
"""ECC secp256k1 OpenSSL wrapper.
|
||||
# Copyright (c) 2019 Pieter Wuille
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test-only secp256k1 elliptic curve implementation
|
||||
|
||||
WARNING: This module does not mlock() secrets; your private keys may end up on
|
||||
disk in swap! Use with caution!
|
||||
WARNING: This code is slow, uses bad randomness, does not properly protect
|
||||
keys, and is trivially vulnerable to side channel attacks. Do not use for
|
||||
anything but tests."""
|
||||
import random
|
||||
|
||||
This file is modified from python-bitcoinlib.
|
||||
"""
|
||||
def modinv(a, n):
|
||||
"""Compute the modular inverse of a modulo n
|
||||
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import hashlib
|
||||
See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers.
|
||||
"""
|
||||
t1, t2 = 0, 1
|
||||
r1, r2 = n, a
|
||||
while r2 != 0:
|
||||
q = r1 // r2
|
||||
t1, t2 = t2, t1 - q * t2
|
||||
r1, r2 = r2, r1 - q * r2
|
||||
if r1 > 1:
|
||||
return None
|
||||
if t1 < 0:
|
||||
t1 += n
|
||||
return t1
|
||||
|
||||
ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library ('ssl') or 'libeay32')
|
||||
def jacobi_symbol(n, k):
|
||||
"""Compute the Jacobi symbol of n modulo k
|
||||
|
||||
ssl.BN_new.restype = ctypes.c_void_p
|
||||
ssl.BN_new.argtypes = []
|
||||
See http://en.wikipedia.org/wiki/Jacobi_symbol
|
||||
|
||||
ssl.BN_bin2bn.restype = ctypes.c_void_p
|
||||
ssl.BN_bin2bn.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p]
|
||||
For our application k is always prime, so this is the same as the Legendre symbol."""
|
||||
assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k"
|
||||
n %= k
|
||||
t = 0
|
||||
while n != 0:
|
||||
while n & 1 == 0:
|
||||
n >>= 1
|
||||
r = k & 7
|
||||
t ^= (r == 3 or r == 5)
|
||||
n, k = k, n
|
||||
t ^= (n & k & 3 == 3)
|
||||
n = n % k
|
||||
if k == 1:
|
||||
return -1 if t else 1
|
||||
return 0
|
||||
|
||||
ssl.BN_CTX_free.restype = None
|
||||
ssl.BN_CTX_free.argtypes = [ctypes.c_void_p]
|
||||
def modsqrt(a, p):
|
||||
"""Compute the square root of a modulo p when p % 4 = 3.
|
||||
|
||||
ssl.BN_CTX_new.restype = ctypes.c_void_p
|
||||
ssl.BN_CTX_new.argtypes = []
|
||||
The Tonelli-Shanks algorithm can be used. See https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm
|
||||
|
||||
ssl.ECDH_compute_key.restype = ctypes.c_int
|
||||
ssl.ECDH_compute_key.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p]
|
||||
Limiting this function to only work for p % 4 = 3 means we don't need to
|
||||
iterate through the loop. The highest n such that p - 1 = 2^n Q with Q odd
|
||||
is n = 1. Therefore Q = (p-1)/2 and sqrt = a^((Q+1)/2) = a^((p+1)/4)
|
||||
|
||||
ssl.ECDSA_sign.restype = ctypes.c_int
|
||||
ssl.ECDSA_sign.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
secp256k1's is defined over field of size 2**256 - 2**32 - 977, which is 3 mod 4.
|
||||
"""
|
||||
if p % 4 != 3:
|
||||
raise NotImplementedError("modsqrt only implemented for p % 4 = 3")
|
||||
sqrt = pow(a, (p + 1)//4, p)
|
||||
if pow(sqrt, 2, p) == a % p:
|
||||
return sqrt
|
||||
return None
|
||||
|
||||
ssl.ECDSA_verify.restype = ctypes.c_int
|
||||
ssl.ECDSA_verify.argtypes = [ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p]
|
||||
class EllipticCurve:
|
||||
def __init__(self, p, a, b):
|
||||
"""Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p)."""
|
||||
self.p = p
|
||||
self.a = a % p
|
||||
self.b = b % p
|
||||
|
||||
ssl.EC_KEY_free.restype = None
|
||||
ssl.EC_KEY_free.argtypes = [ctypes.c_void_p]
|
||||
def affine(self, p1):
|
||||
"""Convert a Jacobian point tuple p1 to affine form, or None if at infinity.
|
||||
|
||||
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
|
||||
ssl.EC_KEY_new_by_curve_name.argtypes = [ctypes.c_int]
|
||||
An affine point is represented as the Jacobian (x, y, 1)"""
|
||||
x1, y1, z1 = p1
|
||||
if z1 == 0:
|
||||
return None
|
||||
inv = modinv(z1, self.p)
|
||||
inv_2 = (inv**2) % self.p
|
||||
inv_3 = (inv_2 * inv) % self.p
|
||||
return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1)
|
||||
|
||||
ssl.EC_KEY_get0_group.restype = ctypes.c_void_p
|
||||
ssl.EC_KEY_get0_group.argtypes = [ctypes.c_void_p]
|
||||
def negate(self, p1):
|
||||
"""Negate a Jacobian point tuple p1."""
|
||||
x1, y1, z1 = p1
|
||||
return (x1, (self.p - y1) % self.p, z1)
|
||||
|
||||
ssl.EC_KEY_get0_public_key.restype = ctypes.c_void_p
|
||||
ssl.EC_KEY_get0_public_key.argtypes = [ctypes.c_void_p]
|
||||
def on_curve(self, p1):
|
||||
"""Determine whether a Jacobian tuple p is on the curve (and not infinity)"""
|
||||
x1, y1, z1 = p1
|
||||
z2 = pow(z1, 2, self.p)
|
||||
z4 = pow(z2, 2, self.p)
|
||||
return z1 != 0 and (pow(x1, 3, self.p) + self.a * x1 * z4 + self.b * z2 * z4 - pow(y1, 2, self.p)) % self.p == 0
|
||||
|
||||
ssl.EC_KEY_set_private_key.restype = ctypes.c_int
|
||||
ssl.EC_KEY_set_private_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
||||
def is_x_coord(self, x):
|
||||
"""Test whether x is a valid X coordinate on the curve."""
|
||||
x_3 = pow(x, 3, self.p)
|
||||
return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1
|
||||
|
||||
ssl.EC_KEY_set_conv_form.restype = None
|
||||
ssl.EC_KEY_set_conv_form.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
||||
def lift_x(self, x):
|
||||
"""Given an X coordinate on the curve, return a corresponding affine point."""
|
||||
x_3 = pow(x, 3, self.p)
|
||||
v = x_3 + self.a * x + self.b
|
||||
y = modsqrt(v, self.p)
|
||||
if y is None:
|
||||
return None
|
||||
return (x, y, 1)
|
||||
|
||||
ssl.EC_KEY_set_public_key.restype = ctypes.c_int
|
||||
ssl.EC_KEY_set_public_key.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
||||
def double(self, p1):
|
||||
"""Double a Jacobian tuple p1
|
||||
|
||||
ssl.i2o_ECPublicKey.restype = ctypes.c_void_p
|
||||
ssl.i2o_ECPublicKey.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
||||
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Doubling"""
|
||||
x1, y1, z1 = p1
|
||||
if z1 == 0:
|
||||
return (0, 1, 0)
|
||||
y1_2 = (y1**2) % self.p
|
||||
y1_4 = (y1_2**2) % self.p
|
||||
x1_2 = (x1**2) % self.p
|
||||
s = (4*x1*y1_2) % self.p
|
||||
m = 3*x1_2
|
||||
if self.a:
|
||||
m += self.a * pow(z1, 4, self.p)
|
||||
m = m % self.p
|
||||
x2 = (m**2 - 2*s) % self.p
|
||||
y2 = (m*(s - x2) - 8*y1_4) % self.p
|
||||
z2 = (2*y1*z1) % self.p
|
||||
return (x2, y2, z2)
|
||||
|
||||
ssl.EC_POINT_new.restype = ctypes.c_void_p
|
||||
ssl.EC_POINT_new.argtypes = [ctypes.c_void_p]
|
||||
def add_mixed(self, p1, p2):
|
||||
"""Add a Jacobian tuple p1 and an affine tuple p2
|
||||
|
||||
ssl.EC_POINT_free.restype = None
|
||||
ssl.EC_POINT_free.argtypes = [ctypes.c_void_p]
|
||||
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition (with affine point)"""
|
||||
x1, y1, z1 = p1
|
||||
x2, y2, z2 = p2
|
||||
assert(z2 == 1)
|
||||
# Adding to the point at infinity is a no-op
|
||||
if z1 == 0:
|
||||
return p2
|
||||
z1_2 = (z1**2) % self.p
|
||||
z1_3 = (z1_2 * z1) % self.p
|
||||
u2 = (x2 * z1_2) % self.p
|
||||
s2 = (y2 * z1_3) % self.p
|
||||
if x1 == u2:
|
||||
if (y1 != s2):
|
||||
# p1 and p2 are inverses. Return the point at infinity.
|
||||
return (0, 1, 0)
|
||||
# p1 == p2. The formulas below fail when the two points are equal.
|
||||
return self.double(p1)
|
||||
h = u2 - x1
|
||||
r = s2 - y1
|
||||
h_2 = (h**2) % self.p
|
||||
h_3 = (h_2 * h) % self.p
|
||||
u1_h_2 = (x1 * h_2) % self.p
|
||||
x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
|
||||
y3 = (r*(u1_h_2 - x3) - y1*h_3) % self.p
|
||||
z3 = (h*z1) % self.p
|
||||
return (x3, y3, z3)
|
||||
|
||||
ssl.EC_POINT_mul.restype = ctypes.c_int
|
||||
ssl.EC_POINT_mul.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
def add(self, p1, p2):
|
||||
"""Add two Jacobian tuples p1 and p2
|
||||
|
||||
# this specifies the curve used with ECDSA.
|
||||
NID_secp256k1 = 714 # from openssl/obj_mac.h
|
||||
See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition"""
|
||||
x1, y1, z1 = p1
|
||||
x2, y2, z2 = p2
|
||||
# Adding the point at infinity is a no-op
|
||||
if z1 == 0:
|
||||
return p2
|
||||
if z2 == 0:
|
||||
return p1
|
||||
# Adding an Affine to a Jacobian is more efficient since we save field multiplications and squarings when z = 1
|
||||
if z1 == 1:
|
||||
return self.add_mixed(p2, p1)
|
||||
if z2 == 1:
|
||||
return self.add_mixed(p1, p2)
|
||||
z1_2 = (z1**2) % self.p
|
||||
z1_3 = (z1_2 * z1) % self.p
|
||||
z2_2 = (z2**2) % self.p
|
||||
z2_3 = (z2_2 * z2) % self.p
|
||||
u1 = (x1 * z2_2) % self.p
|
||||
u2 = (x2 * z1_2) % self.p
|
||||
s1 = (y1 * z2_3) % self.p
|
||||
s2 = (y2 * z1_3) % self.p
|
||||
if u1 == u2:
|
||||
if (s1 != s2):
|
||||
# p1 and p2 are inverses. Return the point at infinity.
|
||||
return (0, 1, 0)
|
||||
# p1 == p2. The formulas below fail when the two points are equal.
|
||||
return self.double(p1)
|
||||
h = u2 - u1
|
||||
r = s2 - s1
|
||||
h_2 = (h**2) % self.p
|
||||
h_3 = (h_2 * h) % self.p
|
||||
u1_h_2 = (u1 * h_2) % self.p
|
||||
x3 = (r**2 - h_3 - 2*u1_h_2) % self.p
|
||||
y3 = (r*(u1_h_2 - x3) - s1*h_3) % self.p
|
||||
z3 = (h*z1*z2) % self.p
|
||||
return (x3, y3, z3)
|
||||
|
||||
def mul(self, ps):
|
||||
"""Compute a (multi) point multiplication
|
||||
|
||||
ps is a list of (Jacobian tuple, scalar) pairs.
|
||||
"""
|
||||
r = (0, 1, 0)
|
||||
for i in range(255, -1, -1):
|
||||
r = self.double(r)
|
||||
for (p, n) in ps:
|
||||
if ((n >> i) & 1):
|
||||
r = self.add(r, p)
|
||||
return r
|
||||
|
||||
SECP256K1 = EllipticCurve(2**256 - 2**32 - 977, 0, 7)
|
||||
SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1)
|
||||
SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
||||
SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2
|
||||
|
||||
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
|
||||
def _check_result(val, func, args):
|
||||
if val == 0:
|
||||
raise ValueError
|
||||
else:
|
||||
return ctypes.c_void_p (val)
|
||||
|
||||
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
|
||||
ssl.EC_KEY_new_by_curve_name.errcheck = _check_result
|
||||
|
||||
class CECKey():
|
||||
"""Wrapper around OpenSSL's EC_KEY"""
|
||||
|
||||
POINT_CONVERSION_COMPRESSED = 2
|
||||
POINT_CONVERSION_UNCOMPRESSED = 4
|
||||
class ECPubKey():
|
||||
"""A secp256k1 public key"""
|
||||
|
||||
def __init__(self):
|
||||
self.k = ssl.EC_KEY_new_by_curve_name(NID_secp256k1)
|
||||
"""Construct an uninitialized public key"""
|
||||
self.valid = False
|
||||
|
||||
def __del__(self):
|
||||
if ssl:
|
||||
ssl.EC_KEY_free(self.k)
|
||||
self.k = None
|
||||
|
||||
def set_secretbytes(self, secret):
|
||||
priv_key = ssl.BN_bin2bn(secret, 32, ssl.BN_new())
|
||||
group = ssl.EC_KEY_get0_group(self.k)
|
||||
pub_key = ssl.EC_POINT_new(group)
|
||||
ctx = ssl.BN_CTX_new()
|
||||
if not ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx):
|
||||
raise ValueError("Could not derive public key from the supplied secret.")
|
||||
ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx)
|
||||
ssl.EC_KEY_set_private_key(self.k, priv_key)
|
||||
ssl.EC_KEY_set_public_key(self.k, pub_key)
|
||||
ssl.EC_POINT_free(pub_key)
|
||||
ssl.BN_CTX_free(ctx)
|
||||
return self.k
|
||||
|
||||
def set_privkey(self, key):
|
||||
self.mb = ctypes.create_string_buffer(key)
|
||||
return ssl.d2i_ECPrivateKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key))
|
||||
|
||||
def set_pubkey(self, key):
|
||||
self.mb = ctypes.create_string_buffer(key)
|
||||
return ssl.o2i_ECPublicKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key))
|
||||
|
||||
def get_privkey(self):
|
||||
size = ssl.i2d_ECPrivateKey(self.k, 0)
|
||||
mb_pri = ctypes.create_string_buffer(size)
|
||||
ssl.i2d_ECPrivateKey(self.k, ctypes.byref(ctypes.pointer(mb_pri)))
|
||||
return mb_pri.raw
|
||||
|
||||
def get_pubkey(self):
|
||||
size = ssl.i2o_ECPublicKey(self.k, 0)
|
||||
mb = ctypes.create_string_buffer(size)
|
||||
ssl.i2o_ECPublicKey(self.k, ctypes.byref(ctypes.pointer(mb)))
|
||||
return mb.raw
|
||||
|
||||
def get_raw_ecdh_key(self, other_pubkey):
|
||||
ecdh_keybuffer = ctypes.create_string_buffer(32)
|
||||
r = ssl.ECDH_compute_key(ctypes.pointer(ecdh_keybuffer), 32,
|
||||
ssl.EC_KEY_get0_public_key(other_pubkey.k),
|
||||
self.k, 0)
|
||||
if r != 32:
|
||||
raise Exception('CKey.get_ecdh_key(): ECDH_compute_key() failed')
|
||||
return ecdh_keybuffer.raw
|
||||
|
||||
def get_ecdh_key(self, other_pubkey, kdf=lambda k: hashlib.sha256(k).digest()):
|
||||
# FIXME: be warned it's not clear what the kdf should be as a default
|
||||
r = self.get_raw_ecdh_key(other_pubkey)
|
||||
return kdf(r)
|
||||
|
||||
def sign(self, hash, low_s = True):
|
||||
# FIXME: need unit tests for below cases
|
||||
if not isinstance(hash, bytes):
|
||||
raise TypeError('Hash must be bytes instance; got %r' % hash.__class__)
|
||||
if len(hash) != 32:
|
||||
raise ValueError('Hash must be exactly 32 bytes long')
|
||||
|
||||
sig_size0 = ctypes.c_uint32()
|
||||
sig_size0.value = ssl.ECDSA_size(self.k)
|
||||
mb_sig = ctypes.create_string_buffer(sig_size0.value)
|
||||
result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k)
|
||||
assert 1 == result
|
||||
assert mb_sig.raw[0] == 0x30
|
||||
assert mb_sig.raw[1] == sig_size0.value - 2
|
||||
total_size = mb_sig.raw[1]
|
||||
assert mb_sig.raw[2] == 2
|
||||
r_size = mb_sig.raw[3]
|
||||
assert mb_sig.raw[4 + r_size] == 2
|
||||
s_size = mb_sig.raw[5 + r_size]
|
||||
s_value = int.from_bytes(mb_sig.raw[6+r_size:6+r_size+s_size], byteorder='big')
|
||||
if (not low_s) or s_value <= SECP256K1_ORDER_HALF:
|
||||
return mb_sig.raw[:sig_size0.value]
|
||||
def set(self, data):
|
||||
"""Construct a public key from a serialization in compressed or uncompressed format"""
|
||||
if (len(data) == 65 and data[0] == 0x04):
|
||||
p = (int.from_bytes(data[1:33], 'big'), int.from_bytes(data[33:65], 'big'), 1)
|
||||
self.valid = SECP256K1.on_curve(p)
|
||||
if self.valid:
|
||||
self.p = p
|
||||
self.compressed = False
|
||||
elif (len(data) == 33 and (data[0] == 0x02 or data[0] == 0x03)):
|
||||
x = int.from_bytes(data[1:33], 'big')
|
||||
if SECP256K1.is_x_coord(x):
|
||||
p = SECP256K1.lift_x(x)
|
||||
# if the oddness of the y co-ord isn't correct, find the other
|
||||
# valid y
|
||||
if (p[1] & 1) != (data[0] & 1):
|
||||
p = SECP256K1.negate(p)
|
||||
self.p = p
|
||||
self.valid = True
|
||||
self.compressed = True
|
||||
else:
|
||||
self.valid = False
|
||||
else:
|
||||
low_s_value = SECP256K1_ORDER - s_value
|
||||
low_s_bytes = (low_s_value).to_bytes(33, byteorder='big')
|
||||
while len(low_s_bytes) > 1 and low_s_bytes[0] == 0 and low_s_bytes[1] < 0x80:
|
||||
low_s_bytes = low_s_bytes[1:]
|
||||
new_s_size = len(low_s_bytes)
|
||||
new_total_size_byte = (total_size + new_s_size - s_size).to_bytes(1,byteorder='big')
|
||||
new_s_size_byte = (new_s_size).to_bytes(1,byteorder='big')
|
||||
return b'\x30' + new_total_size_byte + mb_sig.raw[2:5+r_size] + new_s_size_byte + low_s_bytes
|
||||
|
||||
def verify(self, hash, sig):
|
||||
"""Verify a DER signature"""
|
||||
return ssl.ECDSA_verify(0, hash, len(hash), sig, len(sig), self.k) == 1
|
||||
|
||||
def set_compressed(self, compressed):
|
||||
if compressed:
|
||||
form = self.POINT_CONVERSION_COMPRESSED
|
||||
else:
|
||||
form = self.POINT_CONVERSION_UNCOMPRESSED
|
||||
ssl.EC_KEY_set_conv_form(self.k, form)
|
||||
|
||||
|
||||
class CPubKey(bytes):
|
||||
"""An encapsulated public key
|
||||
|
||||
Attributes:
|
||||
|
||||
is_valid - Corresponds to CPubKey.IsValid()
|
||||
is_fullyvalid - Corresponds to CPubKey.IsFullyValid()
|
||||
is_compressed - Corresponds to CPubKey.IsCompressed()
|
||||
"""
|
||||
|
||||
def __new__(cls, buf, _cec_key=None):
|
||||
self = super(CPubKey, cls).__new__(cls, buf)
|
||||
if _cec_key is None:
|
||||
_cec_key = CECKey()
|
||||
self._cec_key = _cec_key
|
||||
self.is_fullyvalid = _cec_key.set_pubkey(self) != 0
|
||||
return self
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return len(self) > 0
|
||||
self.valid = False
|
||||
|
||||
@property
|
||||
def is_compressed(self):
|
||||
return len(self) == 33
|
||||
return self.compressed
|
||||
|
||||
def verify(self, hash, sig):
|
||||
return self._cec_key.verify(hash, sig)
|
||||
@property
|
||||
def is_valid(self):
|
||||
return self.valid
|
||||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
def get_bytes(self):
|
||||
assert(self.valid)
|
||||
p = SECP256K1.affine(self.p)
|
||||
if p is None:
|
||||
return None
|
||||
if self.compressed:
|
||||
return bytes([0x02 + (p[1] & 1)]) + p[0].to_bytes(32, 'big')
|
||||
else:
|
||||
return bytes([0x04]) + p[0].to_bytes(32, 'big') + p[1].to_bytes(32, 'big')
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__())
|
||||
def verify_ecdsa(self, sig, msg, low_s=True):
|
||||
"""Verify a strictly DER-encoded ECDSA signature against this pubkey.
|
||||
|
||||
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
||||
ECDSA verifier algorithm"""
|
||||
assert(self.valid)
|
||||
|
||||
# Extract r and s from the DER formatted signature. Return false for
|
||||
# any DER encoding errors.
|
||||
if (sig[1] + 2 != len(sig)):
|
||||
return False
|
||||
if (len(sig) < 4):
|
||||
return False
|
||||
if (sig[0] != 0x30):
|
||||
return False
|
||||
if (sig[2] != 0x02):
|
||||
return False
|
||||
rlen = sig[3]
|
||||
if (len(sig) < 6 + rlen):
|
||||
return False
|
||||
if rlen < 1 or rlen > 33:
|
||||
return False
|
||||
if sig[4] >= 0x80:
|
||||
return False
|
||||
if (rlen > 1 and (sig[4] == 0) and not (sig[5] & 0x80)):
|
||||
return False
|
||||
r = int.from_bytes(sig[4:4+rlen], 'big')
|
||||
if (sig[4+rlen] != 0x02):
|
||||
return False
|
||||
slen = sig[5+rlen]
|
||||
if slen < 1 or slen > 33:
|
||||
return False
|
||||
if (len(sig) != 6 + rlen + slen):
|
||||
return False
|
||||
if sig[6+rlen] >= 0x80:
|
||||
return False
|
||||
if (slen > 1 and (sig[6+rlen] == 0) and not (sig[7+rlen] & 0x80)):
|
||||
return False
|
||||
s = int.from_bytes(sig[6+rlen:6+rlen+slen], 'big')
|
||||
|
||||
# Verify that r and s are within the group order
|
||||
if r < 1 or s < 1 or r >= SECP256K1_ORDER or s >= SECP256K1_ORDER:
|
||||
return False
|
||||
if low_s and s >= SECP256K1_ORDER_HALF:
|
||||
return False
|
||||
z = int.from_bytes(msg, 'big')
|
||||
|
||||
# Run verifier algorithm on r, s
|
||||
w = modinv(s, SECP256K1_ORDER)
|
||||
u1 = z*w % SECP256K1_ORDER
|
||||
u2 = r*w % SECP256K1_ORDER
|
||||
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, u1), (self.p, u2)]))
|
||||
if R is None or R[0] != r:
|
||||
return False
|
||||
return True
|
||||
|
||||
class ECKey():
|
||||
"""A secp256k1 private key"""
|
||||
|
||||
def __init__(self):
|
||||
self.valid = False
|
||||
|
||||
def set(self, secret, compressed):
|
||||
"""Construct a private key object with given 32-byte secret and compressed flag."""
|
||||
assert(len(secret) == 32)
|
||||
secret = int.from_bytes(secret, 'big')
|
||||
self.valid = (secret > 0 and secret < SECP256K1_ORDER)
|
||||
if self.valid:
|
||||
self.secret = secret
|
||||
self.compressed = compressed
|
||||
|
||||
def generate(self, compressed=True):
|
||||
"""Generate a random private key (compressed or uncompressed)."""
|
||||
self.set(random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big'), compressed)
|
||||
|
||||
def get_bytes(self):
|
||||
"""Retrieve the 32-byte representation of this key."""
|
||||
assert(self.valid)
|
||||
return self.secret.to_bytes(32, 'big')
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return self.valid
|
||||
|
||||
@property
|
||||
def is_compressed(self):
|
||||
return self.compressed
|
||||
|
||||
def get_pubkey(self):
|
||||
"""Compute an ECPubKey object for this secret key."""
|
||||
assert(self.valid)
|
||||
ret = ECPubKey()
|
||||
p = SECP256K1.mul([(SECP256K1_G, self.secret)])
|
||||
ret.p = p
|
||||
ret.valid = True
|
||||
ret.compressed = self.compressed
|
||||
return ret
|
||||
|
||||
def sign_ecdsa(self, msg, low_s=True):
|
||||
"""Construct a DER-encoded ECDSA signature with this key.
|
||||
|
||||
See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the
|
||||
ECDSA signer algorithm."""
|
||||
assert(self.valid)
|
||||
z = int.from_bytes(msg, 'big')
|
||||
# Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation)
|
||||
k = random.randrange(1, SECP256K1_ORDER)
|
||||
R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)]))
|
||||
r = R[0] % SECP256K1_ORDER
|
||||
s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER
|
||||
if low_s and s > SECP256K1_ORDER_HALF:
|
||||
s = SECP256K1_ORDER - s
|
||||
# Represent in DER format. The byte representations of r and s have
|
||||
# length rounded up (255 bits becomes 32 bytes and 256 bits becomes 33
|
||||
# bytes).
|
||||
rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')
|
||||
sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')
|
||||
return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb
|
||||
|
||||
@@ -364,6 +364,14 @@ class P2PInterface(P2PConnection):
|
||||
|
||||
# Message receiving helper methods
|
||||
|
||||
def wait_for_tx(self, txid, timeout=60):
|
||||
def test_function():
|
||||
if not self.last_message.get('tx'):
|
||||
return False
|
||||
return self.last_message['tx'].tx.rehash() == txid
|
||||
|
||||
wait_until(test_function, timeout=timeout, lock=mininode_lock)
|
||||
|
||||
def wait_for_block(self, blockhash, timeout=60):
|
||||
test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash
|
||||
wait_until(test_function, timeout=timeout, lock=mininode_lock)
|
||||
|
||||
@@ -219,7 +219,7 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N
|
||||
time.sleep(0.05)
|
||||
|
||||
# Print the cause of the timeout
|
||||
predicate_source = inspect.getsourcelines(predicate)
|
||||
predicate_source = "''''\n" + inspect.getsource(predicate) + "'''"
|
||||
logger.error("wait_until() failed. Predicate: {}".format(predicate_source))
|
||||
if attempt >= attempts:
|
||||
raise AssertionError("Predicate {} not true after {} attempts".format(predicate_source, attempts))
|
||||
|
||||
@@ -134,6 +134,7 @@ BASE_SCRIPTS = [
|
||||
'rpc_net.py',
|
||||
'wallet_keypool.py',
|
||||
'p2p_mempool.py',
|
||||
'p2p_blocksonly.py',
|
||||
'mining_prioritisetransaction.py',
|
||||
'p2p_invalid_locator.py',
|
||||
'p2p_invalid_block.py',
|
||||
|
||||
@@ -13,6 +13,7 @@ export LC_ALL=C
|
||||
FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS=(
|
||||
"FatalError,0"
|
||||
"fprintf,1"
|
||||
"tfm::format,1" # Assuming tfm::::format(std::ostream&, ...
|
||||
"LogConnectFailure,1"
|
||||
"LogPrint,1"
|
||||
"LogPrintf,0"
|
||||
|
||||
@@ -8,7 +8,6 @@ KNOWN_VIOLATIONS=(
|
||||
"src/dbwrapper.cpp:.*vsnprintf"
|
||||
"src/httprpc.cpp.*trim"
|
||||
"src/init.cpp:.*atoi"
|
||||
"src/init.cpp:.*fprintf"
|
||||
"src/qt/rpcconsole.cpp:.*atoi"
|
||||
"src/rest.cpp:.*strtol"
|
||||
"src/test/dbwrapper_tests.cpp:.*snprintf"
|
||||
@@ -85,7 +84,7 @@ LOCALE_DEPENDENT_FUNCTIONS=(
|
||||
mbtowc # LC_CTYPE
|
||||
mktime
|
||||
normalize # boost::locale::normalize
|
||||
# printf # LC_NUMERIC
|
||||
printf # LC_NUMERIC
|
||||
putwc
|
||||
putwchar
|
||||
scanf # LC_NUMERIC
|
||||
@@ -189,8 +188,7 @@ GIT_GREP_OUTPUT=$(git grep -E "[^a-zA-Z0-9_\`'\"<>](${REGEXP_LOCALE_DEPENDENT_FU
|
||||
EXIT_CODE=0
|
||||
for LOCALE_DEPENDENT_FUNCTION in "${LOCALE_DEPENDENT_FUNCTIONS[@]}"; do
|
||||
MATCHES=$(grep -E "[^a-zA-Z0-9_\`'\"<>]${LOCALE_DEPENDENT_FUNCTION}(_r|_s)?[^a-zA-Z0-9_\`'\"<>]" <<< "${GIT_GREP_OUTPUT}" | \
|
||||
grep -vE "\.(c|cpp|h):\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}" | \
|
||||
grep -vE 'fprintf\(.*(stdout|stderr)')
|
||||
grep -vE "\.(c|cpp|h):\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}")
|
||||
if [[ ${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES} != "" ]]; then
|
||||
MATCHES=$(grep -vE "${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES}" <<< "${MATCHES}")
|
||||
fi
|
||||
|
||||
@@ -15,5 +15,5 @@ fi
|
||||
|
||||
vulture \
|
||||
--min-confidence 60 \
|
||||
--ignore-names "argtypes,connection_lost,connection_made,converter,data_received,daemon,errcheck,get_ecdh_key,get_privkey,is_compressed,is_fullyvalid,msg_generic,on_*,optionxform,restype,set_privkey,profile_with_perf" \
|
||||
--ignore-names "argtypes,connection_lost,connection_made,converter,data_received,daemon,errcheck,is_compressed,is_valid,verify_ecdsa,msg_generic,on_*,optionxform,restype,profile_with_perf" \
|
||||
$(git ls-files -- "*.py" ":(exclude)contrib/" ":(exclude)test/functional/data/invalid_txs.py")
|
||||
|
||||
Reference in New Issue
Block a user