Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/base58.cpp b/src/base58.cpp
index 42134196f..5144c9df3 100644
--- a/src/base58.cpp
+++ b/src/base58.cpp
@@ -1,304 +1,305 @@
// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <base58.h>
#include <hash.h>
#include <script/script.h>
#include <uint256.h>
#include <utilstrencodings.h>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
/** All alphanumeric characters except for "0", "I", "O", and "l" */
static const char *pszBase58 =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
bool DecodeBase58(const char *psz, std::vector<uint8_t> &vch) {
// Skip leading spaces.
while (*psz && IsSpace(*psz)) {
psz++;
}
// Skip and count leading '1's.
int zeroes = 0;
int length = 0;
while (*psz == '1') {
zeroes++;
psz++;
}
// Allocate enough space in big-endian base256 representation.
// log(58) / log(256), rounded up.
int size = strlen(psz) * 733 / 1000 + 1;
std::vector<uint8_t> b256(size);
// Process the characters.
while (*psz && !IsSpace(*psz)) {
// Decode base58 character
const char *ch = strchr(pszBase58, *psz);
if (ch == nullptr) {
return false;
}
// Apply "b256 = b256 * 58 + ch".
int carry = ch - pszBase58;
int i = 0;
for (std::vector<uint8_t>::reverse_iterator it = b256.rbegin();
(carry != 0 || i < length) && (it != b256.rend()); ++it, ++i) {
carry += 58 * (*it);
*it = carry % 256;
carry /= 256;
}
assert(carry == 0);
length = i;
psz++;
}
// Skip trailing spaces.
while (IsSpace(*psz)) {
psz++;
}
if (*psz != 0) {
return false;
}
// Skip leading zeroes in b256.
std::vector<uint8_t>::iterator it = b256.begin() + (size - length);
while (it != b256.end() && *it == 0)
it++;
// Copy result into output vector.
vch.reserve(zeroes + (b256.end() - it));
vch.assign(zeroes, 0x00);
while (it != b256.end()) {
vch.push_back(*(it++));
}
return true;
}
std::string EncodeBase58(const uint8_t *pbegin, const uint8_t *pend) {
// Skip & count leading zeroes.
int zeroes = 0;
int length = 0;
while (pbegin != pend && *pbegin == 0) {
pbegin++;
zeroes++;
}
// Allocate enough space in big-endian base58 representation.
// log(256) / log(58), rounded up.
int size = (pend - pbegin) * 138 / 100 + 1;
std::vector<uint8_t> b58(size);
// Process the bytes.
while (pbegin != pend) {
int carry = *pbegin;
int i = 0;
// Apply "b58 = b58 * 256 + ch".
for (std::vector<uint8_t>::reverse_iterator it = b58.rbegin();
(carry != 0 || i < length) && (it != b58.rend()); it++, i++) {
carry += 256 * (*it);
*it = carry % 58;
carry /= 58;
}
assert(carry == 0);
length = i;
pbegin++;
}
// Skip leading zeroes in base58 result.
std::vector<uint8_t>::iterator it = b58.begin() + (size - length);
while (it != b58.end() && *it == 0) {
it++;
}
// Translate the result into a string.
std::string str;
str.reserve(zeroes + (b58.end() - it));
str.assign(zeroes, '1');
while (it != b58.end()) {
str += pszBase58[*(it++)];
}
return str;
}
std::string EncodeBase58(const std::vector<uint8_t> &vch) {
return EncodeBase58(vch.data(), vch.data() + vch.size());
}
bool DecodeBase58(const std::string &str, std::vector<uint8_t> &vchRet) {
return DecodeBase58(str.c_str(), vchRet);
}
std::string EncodeBase58Check(const std::vector<uint8_t> &vchIn) {
// add 4-byte hash check to the end
std::vector<uint8_t> vch(vchIn);
uint256 hash = Hash(vch.begin(), vch.end());
vch.insert(vch.end(), (uint8_t *)&hash, (uint8_t *)&hash + 4);
return EncodeBase58(vch);
}
bool DecodeBase58Check(const char *psz, std::vector<uint8_t> &vchRet) {
if (!DecodeBase58(psz, vchRet) || (vchRet.size() < 4)) {
vchRet.clear();
return false;
}
// re-calculate the checksum, ensure it matches the included 4-byte checksum
uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4);
if (memcmp(&hash, &vchRet[vchRet.size() - 4], 4) != 0) {
vchRet.clear();
return false;
}
vchRet.resize(vchRet.size() - 4);
return true;
}
bool DecodeBase58Check(const std::string &str, std::vector<uint8_t> &vchRet) {
return DecodeBase58Check(str.c_str(), vchRet);
}
CBase58Data::CBase58Data() {
vchVersion.clear();
vchData.clear();
}
void CBase58Data::SetData(const std::vector<uint8_t> &vchVersionIn,
const void *pdata, size_t nSize) {
vchVersion = vchVersionIn;
vchData.resize(nSize);
if (!vchData.empty()) {
memcpy(vchData.data(), pdata, nSize);
}
}
void CBase58Data::SetData(const std::vector<uint8_t> &vchVersionIn,
const uint8_t *pbegin, const uint8_t *pend) {
SetData(vchVersionIn, (void *)pbegin, pend - pbegin);
}
bool CBase58Data::SetString(const char *psz, unsigned int nVersionBytes) {
std::vector<uint8_t> vchTemp;
bool rc58 = DecodeBase58Check(psz, vchTemp);
if ((!rc58) || (vchTemp.size() < nVersionBytes)) {
vchData.clear();
vchVersion.clear();
return false;
}
vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes);
vchData.resize(vchTemp.size() - nVersionBytes);
if (!vchData.empty()) {
memcpy(vchData.data(), vchTemp.data() + nVersionBytes, vchData.size());
}
memory_cleanse(vchTemp.data(), vchTemp.size());
return true;
}
bool CBase58Data::SetString(const std::string &str) {
return SetString(str.c_str());
}
std::string CBase58Data::ToString() const {
std::vector<uint8_t> vch = vchVersion;
vch.insert(vch.end(), vchData.begin(), vchData.end());
return EncodeBase58Check(vch);
}
int CBase58Data::CompareTo(const CBase58Data &b58) const {
if (vchVersion < b58.vchVersion) return -1;
if (vchVersion > b58.vchVersion) return 1;
if (vchData < b58.vchData) return -1;
if (vchData > b58.vchData) return 1;
return 0;
}
namespace {
class DestinationEncoder : public boost::static_visitor<std::string> {
private:
const CChainParams &m_params;
public:
explicit DestinationEncoder(const CChainParams &params)
: m_params(params) {}
std::string operator()(const CKeyID &id) const {
std::vector<uint8_t> data =
m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data);
}
std::string operator()(const CScriptID &id) const {
std::vector<uint8_t> data =
m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data);
}
std::string operator()(const CNoDestination &no) const { return ""; }
};
CTxDestination DecodeLegacyDestination(const std::string &str,
const CChainParams &params) {
std::vector<uint8_t> data;
uint160 hash;
if (!DecodeBase58Check(str, data)) {
return CNoDestination();
}
// Base58Check decoding
const std::vector<uint8_t> &pubkey_prefix =
params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
if (data.size() == 20 + pubkey_prefix.size() &&
std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
memcpy(hash.begin(), &data[pubkey_prefix.size()], 20);
return CKeyID(hash);
}
const std::vector<uint8_t> &script_prefix =
params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
if (data.size() == 20 + script_prefix.size() &&
std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
memcpy(hash.begin(), &data[script_prefix.size()], 20);
return CScriptID(hash);
}
return CNoDestination();
}
} // namespace
-void CBitcoinSecret::SetKey(const CKey &vchSecret) {
- assert(vchSecret.IsValid());
- SetData(Params().Base58Prefix(CChainParams::SECRET_KEY), vchSecret.begin(),
- vchSecret.size());
- if (vchSecret.IsCompressed()) vchData.push_back(1);
+CKey DecodeSecret(const std::string &str) {
+ CKey key;
+ std::vector<unsigned char> data;
+ if (DecodeBase58Check(str, data)) {
+ const std::vector<unsigned char> &privkey_prefix =
+ Params().Base58Prefix(CChainParams::SECRET_KEY);
+ if ((data.size() == 32 + privkey_prefix.size() ||
+ (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) &&
+ std::equal(privkey_prefix.begin(), privkey_prefix.end(),
+ data.begin())) {
+ bool compressed = data.size() == 33 + privkey_prefix.size();
+ key.Set(data.begin() + privkey_prefix.size(),
+ data.begin() + privkey_prefix.size() + 32, compressed);
+ }
+ }
+ memory_cleanse(data.data(), data.size());
+ return key;
}
-CKey CBitcoinSecret::GetKey() {
- CKey ret;
- assert(vchData.size() >= 32);
- ret.Set(vchData.begin(), vchData.begin() + 32,
- vchData.size() > 32 && vchData[32] == 1);
+std::string EncodeSecret(const CKey &key) {
+ assert(key.IsValid());
+ std::vector<unsigned char> data =
+ Params().Base58Prefix(CChainParams::SECRET_KEY);
+ data.insert(data.end(), key.begin(), key.end());
+ if (key.IsCompressed()) {
+ data.push_back(1);
+ }
+ std::string ret = EncodeBase58Check(data);
+ memory_cleanse(data.data(), data.size());
return ret;
}
-bool CBitcoinSecret::IsValid() const {
- bool fExpectedFormat =
- vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1);
- bool fCorrectVersion =
- vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY);
- return fExpectedFormat && fCorrectVersion;
-}
-
-bool CBitcoinSecret::SetString(const char *pszSecret) {
- return CBase58Data::SetString(pszSecret) && IsValid();
-}
-
-bool CBitcoinSecret::SetString(const std::string &strSecret) {
- return SetString(strSecret.c_str());
-}
-
std::string EncodeLegacyAddr(const CTxDestination &dest,
const CChainParams &params) {
return boost::apply_visitor(DestinationEncoder(params), dest);
}
CTxDestination DecodeLegacyAddr(const std::string &str,
const CChainParams &params) {
return DecodeLegacyDestination(str, params);
}
diff --git a/src/base58.h b/src/base58.h
index 7854ba442..772cb9d5d 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -1,162 +1,150 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/**
* Why base-58 instead of standard base-64 encoding?
* - Don't want 0OIl characters that look the same in some fonts and
* could be used to create visually identical looking data.
* - A string with non-alphanumeric characters is not as easily accepted as
* input.
* - E-mail usually won't line-break if there's no punctuation to break at.
* - Double-clicking selects the whole string as one word if it's all
* alphanumeric.
*/
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
#include <chainparams.h>
#include <key.h>
#include <pubkey.h>
#include <script/standard.h>
#include <support/allocators/zeroafterfree.h>
#include <string>
#include <vector>
/**
* Encode a byte sequence as a base58-encoded string.
* pbegin and pend cannot be nullptr, unless both are.
*/
std::string EncodeBase58(const uint8_t *pbegin, const uint8_t *pend);
/**
* Encode a byte vector as a base58-encoded string
*/
std::string EncodeBase58(const std::vector<uint8_t> &vch);
/**
* Decode a base58-encoded string (psz) into a byte vector (vchRet).
* return true if decoding is successful.
* psz cannot be nullptr.
*/
bool DecodeBase58(const char *psz, std::vector<uint8_t> &vchRet);
/**
* Decode a base58-encoded string (str) into a byte vector (vchRet).
* return true if decoding is successful.
*/
bool DecodeBase58(const std::string &str, std::vector<uint8_t> &vchRet);
/**
* Encode a byte vector into a base58-encoded string, including checksum
*/
std::string EncodeBase58Check(const std::vector<uint8_t> &vchIn);
/**
* Decode a base58-encoded string (psz) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
inline bool DecodeBase58Check(const char *psz, std::vector<uint8_t> &vchRet);
/**
* Decode a base58-encoded string (str) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful
*/
inline bool DecodeBase58Check(const std::string &str,
std::vector<uint8_t> &vchRet);
/**
* Base class for all base58-encoded data
*/
class CBase58Data {
protected:
//! the version byte(s)
std::vector<uint8_t> vchVersion;
//! the actually encoded data
typedef std::vector<uint8_t, zero_after_free_allocator<uint8_t>>
vector_uchar;
vector_uchar vchData;
CBase58Data();
void SetData(const std::vector<uint8_t> &vchVersionIn, const void *pdata,
size_t nSize);
void SetData(const std::vector<uint8_t> &vchVersionIn,
const uint8_t *pbegin, const uint8_t *pend);
public:
bool SetString(const char *psz, unsigned int nVersionBytes = 1);
bool SetString(const std::string &str);
std::string ToString() const;
int CompareTo(const CBase58Data &b58) const;
bool operator==(const CBase58Data &b58) const {
return CompareTo(b58) == 0;
}
bool operator<=(const CBase58Data &b58) const {
return CompareTo(b58) <= 0;
}
bool operator>=(const CBase58Data &b58) const {
return CompareTo(b58) >= 0;
}
bool operator<(const CBase58Data &b58) const { return CompareTo(b58) < 0; }
bool operator>(const CBase58Data &b58) const { return CompareTo(b58) > 0; }
};
-/**
- * A base58-encoded secret key
- */
-class CBitcoinSecret : public CBase58Data {
-public:
- void SetKey(const CKey &vchSecret);
- CKey GetKey();
- bool IsValid() const;
- bool SetString(const char *pszSecret);
- bool SetString(const std::string &strSecret);
-
- CBitcoinSecret(const CKey &vchSecret) { SetKey(vchSecret); }
- CBitcoinSecret() {}
-};
+CKey DecodeSecret(const std::string &str);
+std::string EncodeSecret(const CKey &key);
template <typename K, int Size, CChainParams::Base58Type Type>
class CBitcoinExtKeyBase : public CBase58Data {
public:
void SetKey(const K &key) {
uint8_t vch[Size];
key.Encode(vch);
SetData(Params().Base58Prefix(Type), vch, vch + Size);
}
K GetKey() {
K ret;
if (vchData.size() == Size) {
// If base58 encoded data does not hold an ext key, return a
// !IsValid() key
ret.Decode(vchData.data());
}
return ret;
}
CBitcoinExtKeyBase(const K &key) { SetKey(key); }
CBitcoinExtKeyBase(const std::string &strBase58c) {
SetString(strBase58c.c_str(), Params().Base58Prefix(Type).size());
}
CBitcoinExtKeyBase() {}
};
typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE,
CChainParams::EXT_SECRET_KEY>
CBitcoinExtKey;
typedef CBitcoinExtKeyBase<CExtPubKey, BIP32_EXTKEY_SIZE,
CChainParams::EXT_PUBLIC_KEY>
CBitcoinExtPubKey;
std::string EncodeLegacyAddr(const CTxDestination &dest, const CChainParams &);
CTxDestination DecodeLegacyAddr(const std::string &str,
const CChainParams &params);
#endif // BITCOIN_BASE58_H
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index ad3be0a9a..a7054c029 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -1,877 +1,874 @@
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <base58.h>
#include <chainparams.h>
#include <clientversion.h>
#include <coins.h>
#include <consensus/consensus.h>
#include <core_io.h>
#include <dstencode.h>
#include <keystore.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <script/sign.h>
#include <util.h>
#include <utilmoneystr.h>
#include <utilstrencodings.h>
#include <boost/algorithm/string.hpp>
#include <univalue.h>
#include <cstdio>
static bool fCreateBlank;
static std::map<std::string, UniValue> registers;
static const int CONTINUE_EXECUTION = -1;
static void SetupBitcoinTxArgs() {
gArgs.AddArg("-?", _("This help message"), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-create", _("Create new, empty TX."), false,
OptionsCategory::OPTIONS);
gArgs.AddArg("-json", _("Select JSON output"), false,
OptionsCategory::OPTIONS);
gArgs.AddArg("-txid",
_("Output only the hex-encoded transaction id of the "
"resultant transaction."),
false, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions();
gArgs.AddArg("delin=N", _("Delete input N from TX"), false,
OptionsCategory::COMMANDS);
gArgs.AddArg("delout=N", _("Delete output N from TX"), false,
OptionsCategory::COMMANDS);
gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"), false,
OptionsCategory::COMMANDS);
gArgs.AddArg("locktime=N", _("Set TX lock time to N"), false,
OptionsCategory::COMMANDS);
gArgs.AddArg("nversion=N", _("Set TX version to N"), false,
OptionsCategory::COMMANDS);
gArgs.AddArg("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"),
false, OptionsCategory::COMMANDS);
gArgs.AddArg("outpubkey=VALUE:PUBKEY[:FLAGS]",
_("Add pay-to-pubkey output to TX") + ". " +
_("Optionally add the \"S\" flag to wrap the output in a "
"pay-to-script-hash."),
false, OptionsCategory::COMMANDS);
gArgs.AddArg("outdata=[VALUE:]DATA", _("Add data-based output to TX"),
false, OptionsCategory::COMMANDS);
gArgs.AddArg("outscript=VALUE:SCRIPT[:FLAGS]",
_("Add raw script output to TX") + ". " +
_("Optionally add the \"S\" flag to wrap the output in a "
"pay-to-script-hash."),
false, OptionsCategory::COMMANDS);
gArgs.AddArg(
"outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]",
_("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = "
"PUBKEYS") +
". " +
_("Optionally add the \"S\" flag to wrap the output in a "
"pay-to-script-hash."),
false, OptionsCategory::COMMANDS);
gArgs.AddArg("sign=SIGHASH-FLAGS",
_("Add zero or more signatures to transaction") + ". " +
_("This command requires JSON registers:") +
_("prevtxs=JSON object") + ", " +
_("privatekeys=JSON object") + ". " +
_("See signrawtransaction docs for format of sighash "
"flags, JSON objects."),
false, OptionsCategory::COMMANDS);
gArgs.AddArg("load=NAME:FILENAME",
_("Load JSON file FILENAME into register NAME"), false,
OptionsCategory::REGISTER_COMMANDS);
gArgs.AddArg("set=NAME:JSON-STRING",
_("Set register NAME to given JSON-STRING"), false,
OptionsCategory::REGISTER_COMMANDS);
}
//
// This function returns either one of EXIT_ codes when it's expected to stop
// the process or CONTINUE_EXECUTION when it's expected to continue further.
//
static int AppInitRawTx(int argc, char *argv[]) {
//
// Parameters
//
SetupBitcoinTxArgs();
gArgs.ParseParameters(argc, argv);
// 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());
return EXIT_FAILURE;
}
fCreateBlank = gArgs.GetBoolArg("-create", false);
if (argc < 2 || HelpRequested(gArgs)) {
// First part of help message is specific to this utility
std::string strUsage =
PACKAGE_NAME " bitcoin-tx utility version " + FormatFullVersion() +
"\n\n" +
"Usage: bitcoin-tx [options] <hex-tx> [commands] Update "
"hex-encoded bitcoin transaction\n" +
"or: bitcoin-tx [options] -create [commands] Create "
"hex-encoded bitcoin transaction\n" +
"\n";
strUsage += gArgs.GetHelpMessage();
fprintf(stdout, "%s", strUsage.c_str());
if (argc < 2) {
fprintf(stderr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
return CONTINUE_EXECUTION;
}
static void RegisterSetJson(const std::string &key,
const std::string &rawJson) {
UniValue val;
if (!val.read(rawJson)) {
std::string strErr = "Cannot parse JSON for key " + key;
throw std::runtime_error(strErr);
}
registers[key] = val;
}
static void RegisterSet(const std::string &strInput) {
// separate NAME:VALUE in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) || (pos == 0) ||
(pos == (strInput.size() - 1))) {
throw std::runtime_error("Register input requires NAME:VALUE");
}
std::string key = strInput.substr(0, pos);
std::string valStr = strInput.substr(pos + 1, std::string::npos);
RegisterSetJson(key, valStr);
}
static void RegisterLoad(const std::string &strInput) {
// separate NAME:FILENAME in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) || (pos == 0) ||
(pos == (strInput.size() - 1))) {
throw std::runtime_error("Register load requires NAME:FILENAME");
}
std::string key = strInput.substr(0, pos);
std::string filename = strInput.substr(pos + 1, std::string::npos);
FILE *f = fopen(filename.c_str(), "r");
if (!f) {
std::string strErr = "Cannot open file " + filename;
throw std::runtime_error(strErr);
}
// load file chunks into one big buffer
std::string valStr;
while ((!feof(f)) && (!ferror(f))) {
char buf[4096];
int bread = fread(buf, 1, sizeof(buf), f);
if (bread <= 0) {
break;
}
valStr.insert(valStr.size(), buf, bread);
}
int error = ferror(f);
fclose(f);
if (error) {
std::string strErr = "Error reading file " + filename;
throw std::runtime_error(strErr);
}
// evaluate as JSON buffer register
RegisterSetJson(key, valStr);
}
static Amount ExtractAndValidateValue(const std::string &strValue) {
Amount value;
if (!ParseMoney(strValue, value)) {
throw std::runtime_error("invalid TX output value");
}
return value;
}
static void MutateTxVersion(CMutableTransaction &tx,
const std::string &cmdVal) {
int64_t newVersion = atoi64(cmdVal);
if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION) {
throw std::runtime_error("Invalid TX version requested");
}
tx.nVersion = int(newVersion);
}
static void MutateTxLocktime(CMutableTransaction &tx,
const std::string &cmdVal) {
int64_t newLocktime = atoi64(cmdVal);
if (newLocktime < 0LL || newLocktime > 0xffffffffLL) {
throw std::runtime_error("Invalid TX locktime requested");
}
tx.nLockTime = (unsigned int)newLocktime;
}
static void MutateTxAddInput(CMutableTransaction &tx,
const std::string &strInput) {
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
// separate TXID:VOUT in string
if (vStrInputParts.size() < 2) {
throw std::runtime_error("TX input missing separator");
}
// extract and validate TXID
std::string strTxid = vStrInputParts[0];
if ((strTxid.size() != 64) || !IsHex(strTxid)) {
throw std::runtime_error("invalid TX input txid");
}
TxId txid(uint256S(strTxid));
static const unsigned int minTxOutSz = 9;
static const unsigned int maxVout = MAX_TX_SIZE / minTxOutSz;
// extract and validate vout
std::string strVout = vStrInputParts[1];
int vout = atoi(strVout);
if ((vout < 0) || (vout > (int)maxVout)) {
throw std::runtime_error("invalid TX input vout");
}
// extract the optional sequence number
uint32_t nSequenceIn = std::numeric_limits<unsigned int>::max();
if (vStrInputParts.size() > 2) {
nSequenceIn = std::stoul(vStrInputParts[2]);
}
// append to transaction input list
CTxIn txin(txid, vout, CScript(), nSequenceIn);
tx.vin.push_back(txin);
}
static void MutateTxAddOutAddr(CMutableTransaction &tx,
const std::string &strInput,
const CChainParams &chainParams) {
// Separate into VALUE:ADDRESS
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
if (vStrInputParts.size() != 2) {
throw std::runtime_error("TX output missing or too many separators");
}
// Extract and validate VALUE
Amount value = ExtractAndValidateValue(vStrInputParts[0]);
// extract and validate ADDRESS
std::string strAddr = vStrInputParts[1];
CTxDestination destination = DecodeDestination(strAddr, chainParams);
if (!IsValidDestination(destination)) {
throw std::runtime_error("invalid TX output address");
}
CScript scriptPubKey = GetScriptForDestination(destination);
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutPubKey(CMutableTransaction &tx,
const std::string &strInput) {
// Separate into VALUE:PUBKEY[:FLAGS]
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3) {
throw std::runtime_error("TX output missing or too many separators");
}
// Extract and validate VALUE
Amount value = ExtractAndValidateValue(vStrInputParts[0]);
// Extract and validate PUBKEY
CPubKey pubkey(ParseHex(vStrInputParts[1]));
if (!pubkey.IsFullyValid()) {
throw std::runtime_error("invalid TX output pubkey");
}
CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
// Extract and validate FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == 3) {
std::string flags = vStrInputParts[2];
bScriptHash = (flags.find("S") != std::string::npos);
}
if (bScriptHash) {
// Get the ID for the script, and then construct a P2SH destination for
// it.
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutMultiSig(CMutableTransaction &tx,
const std::string &strInput) {
// Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
// Check that there are enough parameters
if (vStrInputParts.size() < 3) {
throw std::runtime_error("Not enough multisig parameters");
}
// Extract and validate VALUE
Amount value = ExtractAndValidateValue(vStrInputParts[0]);
// Extract REQUIRED
uint32_t required = stoul(vStrInputParts[1]);
// Extract NUMKEYS
uint32_t numkeys = stoul(vStrInputParts[2]);
// Validate there are the correct number of pubkeys
if (vStrInputParts.size() < numkeys + 3) {
throw std::runtime_error("incorrect number of multisig pubkeys");
}
if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 ||
numkeys < required) {
throw std::runtime_error("multisig parameter mismatch. Required " +
std::to_string(required) + " of " +
std::to_string(numkeys) + "signatures.");
}
// extract and validate PUBKEYs
std::vector<CPubKey> pubkeys;
for (int pos = 1; pos <= int(numkeys); pos++) {
CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
if (!pubkey.IsFullyValid()) {
throw std::runtime_error("invalid TX output pubkey");
}
pubkeys.push_back(pubkey);
}
// Extract FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == numkeys + 4) {
std::string flags = vStrInputParts.back();
bScriptHash = (flags.find("S") != std::string::npos);
} else if (vStrInputParts.size() > numkeys + 4) {
// Validate that there were no more parameters passed
throw std::runtime_error("Too many parameters");
}
CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
if (bScriptHash) {
// Get the ID for the script, and then construct a P2SH destination for
// it.
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutData(CMutableTransaction &tx,
const std::string &strInput) {
Amount value = Amount::zero();
// separate [VALUE:]DATA in string
size_t pos = strInput.find(':');
if (pos == 0) {
throw std::runtime_error("TX output value not specified");
}
if (pos != std::string::npos) {
// Extract and validate VALUE
value = ExtractAndValidateValue(strInput.substr(0, pos));
}
// extract and validate DATA
std::string strData = strInput.substr(pos + 1, std::string::npos);
if (!IsHex(strData)) {
throw std::runtime_error("invalid TX output data");
}
std::vector<uint8_t> data = ParseHex(strData);
CTxOut txout(value, CScript() << OP_RETURN << data);
tx.vout.push_back(txout);
}
static void MutateTxAddOutScript(CMutableTransaction &tx,
const std::string &strInput) {
// separate VALUE:SCRIPT[:FLAGS]
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
if (vStrInputParts.size() < 2)
throw std::runtime_error("TX output missing separator");
// Extract and validate VALUE
Amount value = ExtractAndValidateValue(vStrInputParts[0]);
// extract and validate script
std::string strScript = vStrInputParts[1];
CScript scriptPubKey = ParseScript(strScript);
// Extract FLAGS
bool bScriptHash = false;
if (vStrInputParts.size() == 3) {
std::string flags = vStrInputParts.back();
bScriptHash = (flags.find("S") != std::string::npos);
}
if (bScriptHash) {
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
}
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxDelInput(CMutableTransaction &tx,
const std::string &strInIdx) {
// parse requested deletion index
int inIdx = atoi(strInIdx);
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
std::string strErr = "Invalid TX input index '" + strInIdx + "'";
throw std::runtime_error(strErr.c_str());
}
// delete input from transaction
tx.vin.erase(tx.vin.begin() + inIdx);
}
static void MutateTxDelOutput(CMutableTransaction &tx,
const std::string &strOutIdx) {
// parse requested deletion index
int outIdx = atoi(strOutIdx);
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
throw std::runtime_error(strErr.c_str());
}
// delete output from transaction
tx.vout.erase(tx.vout.begin() + outIdx);
}
static const unsigned int N_SIGHASH_OPTS = 12;
static const struct {
const char *flagStr;
int flags;
} sigHashOptions[N_SIGHASH_OPTS] = {
{"ALL", SIGHASH_ALL},
{"NONE", SIGHASH_NONE},
{"SINGLE", SIGHASH_SINGLE},
{"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY},
{"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY},
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY},
{"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID},
{"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID},
{"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID},
{"ALL|FORKID|ANYONECANPAY",
SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
{"NONE|FORKID|ANYONECANPAY",
SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
{"SINGLE|FORKID|ANYONECANPAY",
SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
};
static bool findSigHashFlags(SigHashType &sigHashType,
const std::string &flagStr) {
sigHashType = SigHashType();
for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
if (flagStr == sigHashOptions[i].flagStr) {
sigHashType = SigHashType(sigHashOptions[i].flags);
return true;
}
}
return false;
}
static Amount AmountFromValue(const UniValue &value) {
if (!value.isNum() && !value.isStr()) {
throw std::runtime_error("Amount is not a number or string");
}
int64_t n;
if (!ParseFixedPoint(value.getValStr(), 8, &n)) {
throw std::runtime_error("Invalid amount");
}
Amount amount = n * SATOSHI;
if (!MoneyRange(amount)) {
throw std::runtime_error("Amount out of range");
}
return amount;
}
static void MutateTxSign(CMutableTransaction &tx, const std::string &flagStr) {
SigHashType sigHashType = SigHashType().withForkId();
if ((flagStr.size() > 0) && !findSigHashFlags(sigHashType, flagStr)) {
throw std::runtime_error("unknown sighash flag/sign option");
}
// mergedTx will end up with all the signatures; it
// starts as a clone of the raw tx:
CMutableTransaction mergedTx{tx};
const CMutableTransaction txv{tx};
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
if (!registers.count("privatekeys")) {
throw std::runtime_error("privatekeys register variable must be set.");
}
CBasicKeyStore tempKeystore;
UniValue keysObj = registers["privatekeys"];
for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
if (!keysObj[kidx].isStr()) {
throw std::runtime_error("privatekey not a std::string");
}
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
- if (!fGood) {
+ CKey key = DecodeSecret(keysObj[kidx].getValStr());
+ if (!key.IsValid()) {
throw std::runtime_error("privatekey not valid");
}
-
- CKey key = vchSecret.GetKey();
tempKeystore.AddKey(key);
}
// Add previous txouts given in the RPC call:
if (!registers.count("prevtxs")) {
throw std::runtime_error("prevtxs register variable must be set.");
}
UniValue prevtxsObj = registers["prevtxs"];
for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
UniValue prevOut = prevtxsObj[previdx];
if (!prevOut.isObject()) {
throw std::runtime_error("expected prevtxs internal object");
}
std::map<std::string, UniValue::VType> types = {
{"txid", UniValue::VSTR},
{"vout", UniValue::VNUM},
{"scriptPubKey", UniValue::VSTR}};
if (!prevOut.checkObject(types)) {
throw std::runtime_error("prevtxs internal object typecheck fail");
}
TxId txid(ParseHashUV(prevOut["txid"], "txid"));
int nOut = atoi(prevOut["vout"].getValStr());
if (nOut < 0) {
throw std::runtime_error("vout must be positive");
}
COutPoint out(txid, nOut);
std::vector<uint8_t> pkData(
ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
const Coin &coin = view.AccessCoin(out);
if (!coin.IsSpent() &&
coin.GetTxOut().scriptPubKey != scriptPubKey) {
std::string err("Previous output scriptPubKey mismatch:\n");
err = err + ScriptToAsmStr(coin.GetTxOut().scriptPubKey) +
"\nvs:\n" + ScriptToAsmStr(scriptPubKey);
throw std::runtime_error(err);
}
CTxOut txout;
txout.scriptPubKey = scriptPubKey;
txout.nValue = Amount::zero();
if (prevOut.exists("amount")) {
txout.nValue = AmountFromValue(prevOut["amount"]);
}
view.AddCoin(out, Coin(txout, 1, false), true);
}
// If redeemScript given and private keys given, add redeemScript to the
// tempKeystore so it can be signed:
if (scriptPubKey.IsPayToScriptHash() &&
prevOut.exists("redeemScript")) {
UniValue v = prevOut["redeemScript"];
std::vector<uint8_t> rsData(ParseHexUV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
const CKeyStore &keystore = tempKeystore;
// Sign what we can:
for (size_t i = 0; i < mergedTx.vin.size(); i++) {
const CTxIn &txin = mergedTx.vin[i];
const Coin &coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {
continue;
}
const CScript &prevPubKey = coin.GetTxOut().scriptPubKey;
const Amount amount = coin.GetTxOut().nValue;
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) ||
(i < mergedTx.vout.size())) {
ProduceSignature(MutableTransactionSignatureCreator(
&keystore, &mergedTx, i, amount, sigHashType),
prevPubKey, sigdata);
}
// ... and merge in other signatures:
sigdata = CombineSignatures(
prevPubKey,
MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata,
DataFromTransaction(txv, i));
UpdateTransaction(mergedTx, i, sigdata);
}
tx = mergedTx;
}
class Secp256k1Init {
ECCVerifyHandle globalVerifyHandle;
public:
Secp256k1Init() { ECC_Start(); }
~Secp256k1Init() { ECC_Stop(); }
};
static void MutateTx(CMutableTransaction &tx, const std::string &command,
const std::string &commandVal,
const CChainParams &chainParams) {
std::unique_ptr<Secp256k1Init> ecc;
if (command == "nversion") {
MutateTxVersion(tx, commandVal);
} else if (command == "locktime") {
MutateTxLocktime(tx, commandVal);
} else if (command == "delin") {
MutateTxDelInput(tx, commandVal);
} else if (command == "in") {
MutateTxAddInput(tx, commandVal);
} else if (command == "delout") {
MutateTxDelOutput(tx, commandVal);
} else if (command == "outaddr") {
MutateTxAddOutAddr(tx, commandVal, chainParams);
} else if (command == "outpubkey") {
ecc.reset(new Secp256k1Init());
MutateTxAddOutPubKey(tx, commandVal);
} else if (command == "outmultisig") {
ecc.reset(new Secp256k1Init());
MutateTxAddOutMultiSig(tx, commandVal);
} else if (command == "outscript") {
MutateTxAddOutScript(tx, commandVal);
} else if (command == "outdata") {
MutateTxAddOutData(tx, commandVal);
} else if (command == "sign") {
ecc.reset(new Secp256k1Init());
MutateTxSign(tx, commandVal);
} else if (command == "load") {
RegisterLoad(commandVal);
} else if (command == "set") {
RegisterSet(commandVal);
} else {
throw std::runtime_error("unknown command");
}
}
static void OutputTxJSON(const CTransaction &tx) {
UniValue entry(UniValue::VOBJ);
TxToUniv(tx, uint256(), entry);
std::string jsonOutput = entry.write(4);
fprintf(stdout, "%s\n", jsonOutput.c_str());
}
static void OutputTxHash(const CTransaction &tx) {
// the hex-encoded transaction id.
std::string strHexHash = tx.GetId().GetHex();
fprintf(stdout, "%s\n", strHexHash.c_str());
}
static void OutputTxHex(const CTransaction &tx) {
std::string strHex = EncodeHexTx(tx);
fprintf(stdout, "%s\n", strHex.c_str());
}
static void OutputTx(const CTransaction &tx) {
if (gArgs.GetBoolArg("-json", false)) {
OutputTxJSON(tx);
} else if (gArgs.GetBoolArg("-txid", false)) {
OutputTxHash(tx);
} else {
OutputTxHex(tx);
}
}
static std::string readStdin() {
char buf[4096];
std::string ret;
while (!feof(stdin)) {
size_t bread = fread(buf, 1, sizeof(buf), stdin);
ret.append(buf, bread);
if (bread < sizeof(buf)) {
break;
}
}
if (ferror(stdin)) {
throw std::runtime_error("error reading stdin");
}
boost::algorithm::trim_right(ret);
return ret;
}
static int CommandLineRawTx(int argc, char *argv[],
const CChainParams &chainParams) {
std::string strPrint;
int nRet = 0;
try {
// Skip switches; Permit common stdin convention "-"
while (argc > 1 && IsSwitchChar(argv[1][0]) && (argv[1][1] != 0)) {
argc--;
argv++;
}
CMutableTransaction tx;
int startArg;
if (!fCreateBlank) {
// require at least one param
if (argc < 2) {
throw std::runtime_error("too few parameters");
}
// param: hex-encoded bitcoin transaction
std::string strHexTx(argv[1]);
// "-" implies standard input
if (strHexTx == "-") {
strHexTx = readStdin();
}
if (!DecodeHexTx(tx, strHexTx)) {
throw std::runtime_error("invalid transaction encoding");
}
startArg = 2;
} else {
startArg = 1;
}
for (int i = startArg; i < argc; i++) {
std::string arg = argv[i];
std::string key, value;
size_t eqpos = arg.find('=');
if (eqpos == std::string::npos) {
key = arg;
} else {
key = arg.substr(0, eqpos);
value = arg.substr(eqpos + 1);
}
MutateTx(tx, key, value, chainParams);
}
OutputTx(CTransaction(tx));
}
catch (const boost::thread_interrupted &) {
throw;
} catch (const std::exception &e) {
strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
} catch (...) {
PrintExceptionContinue(nullptr, "CommandLineRawTx()");
throw;
}
if (strPrint != "") {
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
}
return nRet;
}
int main(int argc, char *argv[]) {
SetupEnvironment();
try {
int ret = AppInitRawTx(argc, argv);
if (ret != CONTINUE_EXECUTION) return ret;
} catch (const std::exception &e) {
PrintExceptionContinue(&e, "AppInitRawTx()");
return EXIT_FAILURE;
} catch (...) {
PrintExceptionContinue(nullptr, "AppInitRawTx()");
return EXIT_FAILURE;
}
int ret = EXIT_FAILURE;
try {
ret = CommandLineRawTx(argc, argv, Params());
} catch (const std::exception &e) {
PrintExceptionContinue(&e, "CommandLineRawTx()");
} catch (...) {
PrintExceptionContinue(nullptr, "CommandLineRawTx()");
}
return ret;
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 9df4dc352..e21ed5f8d 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -1,491 +1,485 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <base58.h>
#include <chain.h>
#include <clientversion.h>
#include <config.h>
#include <core_io.h>
#include <dstencode.h>
#include <init.h>
#include <net.h>
#include <netbase.h>
#include <rpc/blockchain.h>
#include <rpc/misc.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <timedata.h>
#include <util.h>
#include <utilstrencodings.h>
#include <validation.h>
#ifdef ENABLE_WALLET
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <wallet/walletdb.h>
#endif
#include <warnings.h>
#include <univalue.h>
#include <cstdint>
#ifdef HAVE_MALLOC_INFO
#include <malloc.h>
#endif
static UniValue validateaddress(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"validateaddress \"address\"\n"
"\nReturn information about the given bitcoin address.\n"
"DEPRECATION WARNING: Parts of this command have been deprecated "
"and moved to getaddressinfo. Clients must\n"
"transition to using getaddressinfo to access this information "
"before upgrading to v0.20. The following deprecated\n"
"fields have moved to getaddressinfo and will only be shown here "
"with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n"
"script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, "
"iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin "
"address to validate\n"
"\nResult:\n"
"{\n"
" \"isvalid\" : true|false, (boolean) If the address is "
"valid or not. If not, this is the only property returned.\n"
" \"address\" : \"address\", (string) The bitcoin address "
"validated\n"
" \"scriptPubKey\" : \"hex\", (string) The hex encoded "
"scriptPubKey generated by the address\n"
" \"isscript\" : true|false, (boolean) If the key is a "
"script\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("validateaddress",
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") +
HelpExampleRpc("validateaddress",
"\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\""));
}
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
bool isValid = IsValidDestination(dest);
UniValue ret(UniValue::VOBJ);
ret.pushKV("isvalid", isValid);
if (isValid) {
#ifdef ENABLE_WALLET
if (HasWallets() && IsDeprecatedRPCEnabled(gArgs, "validateaddress")) {
ret.pushKVs(getaddressinfo(config, request));
}
#endif
if (ret["address"].isNull()) {
std::string currentAddress = EncodeDestination(dest, config);
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
ret.pushKV("scriptPubKey",
HexStr(scriptPubKey.begin(), scriptPubKey.end()));
UniValue detail = DescribeAddress(dest);
ret.pushKVs(detail);
}
}
return ret;
}
// Needed even with !ENABLE_WALLET, to pass (ignored) pointers around
class CWallet;
static UniValue createmultisig(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 2) {
std::string msg =
"createmultisig nrequired [\"key\",...]\n"
"\nCreates a multi-signature address with n signature of m keys "
"required.\n"
"It returns a json object with the address and redeemScript.\n"
"DEPRECATION WARNING: Using addresses with createmultisig is "
"deprecated. Clients must\n"
"transition to using addmultisigaddress to create multisig "
"addresses with addresses known\n"
"to the wallet before upgrading to v0.20. To use the deprecated "
"functionality, start bitcoind with -deprecatedrpc=createmultisig\n"
"\nArguments:\n"
"1. nrequired (numeric, required) The number of required "
"signatures out of the n keys or addresses.\n"
"2. \"keys\" (string, required) A json array of hex-encoded "
"public keys\n"
" [\n"
" \"key\" (string) The hex-encoded public key\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"{\n"
" \"address\":\"multisigaddress\", (string) The value of the new "
"multisig address.\n"
" \"redeemScript\":\"script\" (string) The string value of "
"the hex-encoded redemption script.\n"
"}\n"
"\nExamples:\n"
"\nCreate a multisig address from 2 public keys\n" +
HelpExampleCli("createmultisig",
"2 "
"\"["
"\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3"
"42cf11ae157a7ace5fd\\\","
"\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1"
"7e107ef3f6aa5a61626\\\"]\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("createmultisig",
"2, "
"\"["
"\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3"
"42cf11ae157a7ace5fd\\\","
"\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1"
"7e107ef3f6aa5a61626\\\"]\"");
throw std::runtime_error(msg);
}
int required = request.params[0].get_int();
// Get the public keys
const UniValue &keys = request.params[1].get_array();
std::vector<CPubKey> pubkeys;
for (size_t i = 0; i < keys.size(); ++i) {
if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 ||
keys[i].get_str().length() == 130)) {
pubkeys.push_back(HexToPubKey(keys[i].get_str()));
} else {
#ifdef ENABLE_WALLET
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (IsDeprecatedRPCEnabled(gArgs, "createmultisig") &&
EnsureWalletIsAvailable(pwallet, false)) {
pubkeys.push_back(AddrToPubKey(config.GetChainParams(), pwallet,
keys[i].get_str()));
} else
#endif
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
strprintf("Invalid public key: %s\nNote that from v0.19.6, "
"createmultisig no longer accepts addresses."
" Clients must transition to using "
"addmultisigaddress to create multisig addresses "
"with addresses known to the wallet before "
"upgrading to v0.20."
" To use the deprecated functionality, start "
"bitcoind with -deprecatedrpc=createmultisig",
keys[i].get_str()));
}
}
// Construct using pay-to-script-hash:
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
CScriptID innerID(inner);
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(innerID, config));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
return result;
}
static UniValue verifymessage(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 3) {
throw std::runtime_error(
"verifymessage \"address\" \"signature\" \"message\"\n"
"\nVerify a signed message\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address to "
"use for the signature.\n"
"2. \"signature\" (string, required) The signature provided "
"by the signer in base 64 encoding (see signmessage).\n"
"3. \"message\" (string, required) The message that was "
"signed.\n"
"\nResult:\n"
"true|false (boolean) If the signature is verified or not.\n"
"\nExamples:\n"
"\nUnlock the wallet for 30 seconds\n" +
HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
"\nCreate the signature\n" +
HelpExampleCli(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n" +
HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\" \"signature\" \"my "
"message\"") +
"\nAs json rpc\n" +
HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\", \"signature\", \"my "
"message\""));
}
LOCK(cs_main);
std::string strAddress = request.params[0].get_str();
std::string strSign = request.params[1].get_str();
std::string strMessage = request.params[2].get_str();
CTxDestination destination =
DecodeDestination(strAddress, config.GetChainParams());
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
const CKeyID *keyID = boost::get<CKeyID>(&destination);
if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
bool fInvalid = false;
std::vector<uint8_t> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
if (fInvalid) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Malformed base64 encoding");
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
CPubKey pubkey;
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) {
return false;
}
return (pubkey.GetID() == *keyID);
}
static UniValue signmessagewithprivkey(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"signmessagewithprivkey \"privkey\" \"message\"\n"
"\nSign a message with the private key of an address\n"
"\nArguments:\n"
"1. \"privkey\" (string, required) The private key to sign "
"the message with.\n"
"2. \"message\" (string, required) The message to create a "
"signature of.\n"
"\nResult:\n"
"\"signature\" (string) The signature of the message "
"encoded in base 64\n"
"\nExamples:\n"
"\nCreate the signature\n" +
HelpExampleCli("signmessagewithprivkey",
"\"privkey\" \"my message\"") +
"\nVerify the signature\n" +
HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\" \"signature\" \"my "
"message\"") +
"\nAs json rpc\n" +
HelpExampleRpc("signmessagewithprivkey",
"\"privkey\", \"my message\""));
}
std::string strPrivkey = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(strPrivkey);
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
- }
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(strPrivkey);
if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Private key outside allowed range");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
std::vector<uint8_t> vchSig;
if (!key.SignCompact(ss.GetHash(), vchSig)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
}
return EncodeBase64(&vchSig[0], vchSig.size());
}
static UniValue setmocktime(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"setmocktime timestamp\n"
"\nSet the local time to given timestamp (-regtest only)\n"
"\nArguments:\n"
"1. timestamp (integer, required) Unix seconds-since-epoch "
"timestamp\n"
" Pass 0 to go back to using the system time.");
}
if (!config.GetChainParams().MineBlocksOnDemand()) {
throw std::runtime_error(
"setmocktime for regression testing (-regtest mode) only");
}
// For now, don't change mocktime if we're in the middle of validation, as
// this could have an effect on mempool time-based eviction, as well as
// IsInitialBlockDownload().
// TODO: figure out the right way to synchronize around mocktime, and
// ensure all call sites of GetTime() are accessing this safely.
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VNUM});
SetMockTime(request.params[0].get_int64());
return NullUniValue;
}
static UniValue RPCLockedMemoryInfo() {
LockedPool::Stats stats = LockedPoolManager::Instance().stats();
UniValue obj(UniValue::VOBJ);
obj.pushKV("used", uint64_t(stats.used));
obj.pushKV("free", uint64_t(stats.free));
obj.pushKV("total", uint64_t(stats.total));
obj.pushKV("locked", uint64_t(stats.locked));
obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
return obj;
}
#ifdef HAVE_MALLOC_INFO
static std::string RPCMallocInfo() {
char *ptr = nullptr;
size_t size = 0;
FILE *f = open_memstream(&ptr, &size);
if (f) {
malloc_info(0, f);
fclose(f);
if (ptr) {
std::string rv(ptr, size);
free(ptr);
return rv;
}
}
return "";
}
#endif
static UniValue getmemoryinfo(const Config &config,
const JSONRPCRequest &request) {
/* Please, avoid using the word "pool" here in the RPC interface or help,
* as users will undoubtedly confuse it with the other "memory pool"
*/
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getmemoryinfo (\"mode\")\n"
"Returns an object containing information about memory usage.\n"
"Arguments:\n"
"1. \"mode\" determines what kind of information is returned. This "
"argument is optional, the default mode is \"stats\".\n"
" - \"stats\" returns general statistics about memory usage in "
"the daemon.\n"
" - \"mallocinfo\" returns an XML string describing low-level "
"heap state (only available if compiled with glibc 2.10+).\n"
"\nResult (mode \"stats\"):\n"
"{\n"
" \"locked\": { (json object) Information about "
"locked memory manager\n"
" \"used\": xxxxx, (numeric) Number of bytes used\n"
" \"free\": xxxxx, (numeric) Number of bytes available "
"in current arenas\n"
" \"total\": xxxxxxx, (numeric) Total number of bytes "
"managed\n"
" \"locked\": xxxxxx, (numeric) Amount of bytes that "
"succeeded locking. If this number is smaller than total, locking "
"pages failed at some point and key data could be swapped to "
"disk.\n"
" \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n"
" \"chunks_free\": xxxxx, (numeric) Number unused chunks\n"
" }\n"
"}\n"
"\nResult (mode \"mallocinfo\"):\n"
"\"<malloc version=\"1\">...\"\n"
"\nExamples:\n" +
HelpExampleCli("getmemoryinfo", "") +
HelpExampleRpc("getmemoryinfo", ""));
}
std::string mode = (request.params.size() < 1 || request.params[0].isNull())
? "stats"
: request.params[0].get_str();
if (mode == "stats") {
UniValue obj(UniValue::VOBJ);
obj.pushKV("locked", RPCLockedMemoryInfo());
return obj;
} else if (mode == "mallocinfo") {
#ifdef HAVE_MALLOC_INFO
return RPCMallocInfo();
#else
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"mallocinfo is only available when compiled with glibc 2.10+");
#endif
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode);
}
}
static UniValue echo(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp) {
throw std::runtime_error(
"echo|echojson \"message\" ...\n"
"\nSimply echo back the input arguments. This command is for "
"testing.\n"
"\nThe difference between echo and echojson is that echojson has "
"argument conversion enabled in the client-side table in"
"bitcoin-cli and the GUI. There is no server-side difference.");
}
return request.params;
}
static UniValue getinfo_deprecated(const Config &config,
const JSONRPCRequest &request) {
throw JSONRPCError(RPC_METHOD_NOT_FOUND,
"getinfo\n"
"\nThis call was removed in version 0.19.8. Use the "
"appropriate fields from:\n"
"- getblockchaininfo: blocks, difficulty, chain\n"
"- getnetworkinfo: version, protocolversion, "
"timeoffset, connections, proxy, relayfee, warnings\n"
"- getwalletinfo: balance, keypoololdest, keypoolsize, "
"paytxfee, unlocked_until, walletversion\n"
"\nbitcoin-cli has the option -getinfo to collect and "
"format these in the old format.");
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
// category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
{ "control", "getmemoryinfo", getmemoryinfo, {"mode"} },
{ "util", "validateaddress", validateaddress, {"address"} }, /* uses wallet if enabled */
{ "util", "createmultisig", createmultisig, {"nrequired","keys"} },
{ "util", "verifymessage", verifymessage, {"address","signature","message"} },
{ "util", "signmessagewithprivkey", signmessagewithprivkey, {"privkey","message"} },
/* Not shown in help */
{ "hidden", "setmocktime", setmocktime, {"timestamp"}},
{ "hidden", "echo", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
{ "hidden", "echojson", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
{ "hidden", "getinfo", getinfo_deprecated, {}},
};
// clang-format on
void RegisterMiscRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index ff888abc4..97290f27d 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1,1500 +1,1495 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <base58.h>
#include <chain.h>
#include <coins.h>
#include <config.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <dstencode.h>
#include <index/txindex.h>
#include <init.h>
#include <keystore.h>
#include <merkleblock.h>
#include <net.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <rpc/rawtransaction.h>
#include <rpc/server.h>
#include <script/script.h>
#include <script/script_error.h>
#include <script/sign.h>
#include <script/standard.h>
#include <txmempool.h>
#include <uint256.h>
#include <utilstrencodings.h>
#include <validation.h>
#include <validationinterface.h>
#ifdef ENABLE_WALLET
#include <wallet/rpcwallet.h>
#endif
#include <cstdint>
#include <future>
#include <univalue.h>
static void TxToJSON(const CTransaction &tx, const uint256 hashBlock,
UniValue &entry) {
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//
// Blockchain contextual information (confirmations and blocktime) is not
// available to code in bitcoin-common, so we query them here and push the
// data into the returned UniValue.
TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags());
if (!hashBlock.IsNull()) {
LOCK(cs_main);
entry.pushKV("blockhash", hashBlock.GetHex());
CBlockIndex *pindex = LookupBlockIndex(hashBlock);
if (pindex) {
if (chainActive.Contains(pindex)) {
entry.pushKV("confirmations",
1 + chainActive.Height() - pindex->nHeight);
entry.pushKV("time", pindex->GetBlockTime());
entry.pushKV("blocktime", pindex->GetBlockTime());
} else {
entry.pushKV("confirmations", 0);
}
}
}
}
static UniValue getrawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 3) {
throw std::runtime_error(
"getrawtransaction \"txid\" ( verbose \"blockhash\" )\n"
"\nNOTE: By default this function only works for mempool "
"transactions. If the -txindex option is\n"
"enabled, it also works for blockchain transactions. If the block "
"which contains the transaction\n"
"is known, its hash can be provided even for nodes without "
"-txindex. Note that if a blockhash is\n"
"provided, only that block will be searched and if the transaction "
"is in the mempool or other\n"
"blocks, or if this node does not have the given block available, "
"the transaction will not be found.\n"
"DEPRECATED: for now, it also works for transactions with unspent "
"outputs.\n"
"\nReturn the raw transaction data.\n"
"\nIf verbose is 'true', returns an Object with information about "
"'txid'.\n"
"If verbose is 'false' or omitted, returns a string that is "
"serialized, hex-encoded data for 'txid'.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. verbose (bool, optional, default=false) If false, return a "
"string, otherwise return a json object\n"
"3. \"blockhash\" (string, optional) The block in which to look "
"for the transaction\n"
"\nResult (if verbose is not set or set to false):\n"
"\"data\" (string) The serialized, hex-encoded data for "
"'txid'\n"
"\nResult (if verbose is set to true):\n"
"{\n"
" \"in_active_chain\": b, (bool) Whether specified block is in "
"the active chain or not (only present with explicit \"blockhash\" "
"argument)\n"
" \"hex\" : \"data\", (string) The serialized, hex-encoded "
"data for 'txid'\n"
" \"txid\" : \"id\", (string) The transaction id (same as "
"provided)\n"
" \"hash\" : \"id\", (string) The transaction hash "
"(differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The serialized transaction "
"size\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
" {\n"
" \"txid\": \"id\", (string) The transaction id\n"
" \"vout\": n, (numeric) \n"
" \"scriptSig\": { (json object) The script\n"
" \"asm\": \"asm\", (string) asm\n"
" \"hex\": \"hex\" (string) hex\n"
" },\n"
" \"sequence\": n (numeric) The script sequence number\n"
" }\n"
" ,...\n"
" ],\n"
" \"vout\" : [ (array of json objects)\n"
" {\n"
" \"value\" : x.xxx, (numeric) The value in " +
CURRENCY_UNIT +
"\n"
" \"n\" : n, (numeric) index\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"asm\", (string) the asm\n"
" \"hex\" : \"hex\", (string) the hex\n"
" \"reqSigs\" : n, (numeric) The required sigs\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg "
"'pubkeyhash'\n"
" \"addresses\" : [ (json array of string)\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
" }\n"
" }\n"
" ,...\n"
" ],\n"
" \"blockhash\" : \"hash\", (string) the block hash\n"
" \"confirmations\" : n, (numeric) The confirmations\n"
" \"time\" : ttt, (numeric) The transaction time in "
"seconds since epoch (Jan 1 1970 GMT)\n"
" \"blocktime\" : ttt (numeric) The block time in seconds "
"since epoch (Jan 1 1970 GMT)\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getrawtransaction", "\"mytxid\"") +
HelpExampleCli("getrawtransaction", "\"mytxid\" true") +
HelpExampleRpc("getrawtransaction", "\"mytxid\", true") +
HelpExampleCli("getrawtransaction",
"\"mytxid\" false \"myblockhash\"") +
HelpExampleCli("getrawtransaction",
"\"mytxid\" true \"myblockhash\""));
}
bool in_active_chain = true;
TxId txid = TxId(ParseHashV(request.params[0], "parameter 1"));
CBlockIndex *blockindex = nullptr;
if (txid == config.GetChainParams().GenesisBlock().hashMerkleRoot) {
// Special exception for the genesis block coinbase transaction
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"The genesis block coinbase is not considered an "
"ordinary transaction and cannot be retrieved");
}
// Accept either a bool (true) or a num (>=1) to indicate verbose output.
bool fVerbose = false;
if (!request.params[1].isNull()) {
fVerbose = request.params[1].isNum()
? (request.params[1].get_int() != 0)
: request.params[1].get_bool();
}
if (!request.params[2].isNull()) {
LOCK(cs_main);
uint256 blockhash = ParseHashV(request.params[2], "parameter 3");
blockindex = LookupBlockIndex(blockhash);
if (!blockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Block hash not found");
}
in_active_chain = chainActive.Contains(blockindex);
}
bool f_txindex_ready = false;
if (g_txindex && !blockindex) {
f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain();
}
CTransactionRef tx;
uint256 hash_block;
if (!GetTransaction(config, txid, tx, hash_block, true, blockindex)) {
std::string errmsg;
if (blockindex) {
if (!blockindex->nStatus.hasData()) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
}
errmsg = "No such transaction found in the provided block";
} else if (!g_txindex) {
errmsg = "No such mempool transaction. Use -txindex to enable "
"blockchain transaction queries";
} else if (!f_txindex_ready) {
errmsg = "No such mempool transaction. Blockchain transactions are "
"still in the process of being indexed";
} else {
errmsg = "No such mempool or blockchain transaction";
}
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
errmsg +
". Use gettransaction for wallet transactions.");
}
if (!fVerbose) {
return EncodeHexTx(*tx, RPCSerializationFlags());
}
UniValue result(UniValue::VOBJ);
if (blockindex) {
result.pushKV("in_active_chain", in_active_chain);
}
TxToJSON(*tx, hash_block, result);
return result;
}
static UniValue gettxoutproof(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp ||
(request.params.size() != 1 && request.params.size() != 2)) {
throw std::runtime_error(
"gettxoutproof [\"txid\",...] ( blockhash )\n"
"\nReturns a hex-encoded proof that \"txid\" was included in a "
"block.\n"
"\nNOTE: By default this function only works sometimes. This is "
"when there is an\n"
"unspent output in the utxo for this transaction. To make it "
"always work,\n"
"you need to maintain a transaction index, using the -txindex "
"command line option or\n"
"specify the block in which the transaction is included manually "
"(by blockhash).\n"
"\nArguments:\n"
"1. \"txids\" (string) A json array of txids to filter\n"
" [\n"
" \"txid\" (string) A transaction hash\n"
" ,...\n"
" ]\n"
"2. \"blockhash\" (string, optional) If specified, looks for "
"txid in the block with this hash\n"
"\nResult:\n"
"\"data\" (string) A string that is a serialized, "
"hex-encoded data for the proof.\n");
}
std::set<TxId> setTxIds;
TxId oneTxId;
UniValue txids = request.params[0].get_array();
for (unsigned int idx = 0; idx < txids.size(); idx++) {
const UniValue &utxid = txids[idx];
if (utxid.get_str().length() != 64 || !IsHex(utxid.get_str())) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
std::string("Invalid txid ") + utxid.get_str());
}
TxId txid(uint256S(utxid.get_str()));
if (setTxIds.count(txid)) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string("Invalid parameter, duplicated txid: ") +
utxid.get_str());
}
setTxIds.insert(txid);
oneTxId = txid;
}
CBlockIndex *pblockindex = nullptr;
uint256 hashBlock;
if (!request.params[1].isNull()) {
LOCK(cs_main);
hashBlock = uint256S(request.params[1].get_str());
pblockindex = LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
} else {
LOCK(cs_main);
// Loop through txids and try to find which block they're in. Exit loop
// once a block is found.
for (const auto &txid : setTxIds) {
const Coin &coin = AccessByTxid(*pcoinsTip, txid);
if (!coin.IsSpent()) {
pblockindex = chainActive[coin.GetHeight()];
break;
}
}
}
// Allow txindex to catch up if we need to query it and before we acquire
// cs_main.
if (g_txindex && !pblockindex) {
g_txindex->BlockUntilSyncedToCurrentChain();
}
LOCK(cs_main);
if (pblockindex == nullptr) {
CTransactionRef tx;
if (!GetTransaction(config, oneTxId, tx, hashBlock, false) ||
hashBlock.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Transaction not yet in block");
}
pblockindex = LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
}
}
CBlock block;
if (!ReadBlockFromDisk(block, pblockindex, config)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
}
unsigned int ntxFound = 0;
for (const auto &tx : block.vtx) {
if (setTxIds.count(tx->GetId())) {
ntxFound++;
}
}
if (ntxFound != setTxIds.size()) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"Not all transactions found in specified or retrieved block");
}
CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION);
CMerkleBlock mb(block, setTxIds);
ssMB << mb;
std::string strHex = HexStr(ssMB.begin(), ssMB.end());
return strHex;
}
static UniValue verifytxoutproof(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"verifytxoutproof \"proof\"\n"
"\nVerifies that a proof points to a transaction in a block, "
"returning the transaction it commits to\n"
"and throwing an RPC error if the block is not in our best chain\n"
"\nArguments:\n"
"1. \"proof\" (string, required) The hex-encoded proof "
"generated by gettxoutproof\n"
"\nResult:\n"
"[\"txid\"] (array, strings) The txid(s) which the proof "
"commits to, or empty array if the proof can not be validated.\n");
}
CDataStream ssMB(ParseHexV(request.params[0], "proof"), SER_NETWORK,
PROTOCOL_VERSION);
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
UniValue res(UniValue::VARR);
std::vector<uint256> vMatch;
std::vector<size_t> vIndex;
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) !=
merkleBlock.header.hashMerkleRoot) {
return res;
}
LOCK(cs_main);
const CBlockIndex *pindex = LookupBlockIndex(merkleBlock.header.GetHash());
if (!pindex || !chainActive.Contains(pindex) || pindex->nTx == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Block not found in chain");
}
// Check if proof is valid, only add results if so
if (pindex->nTx == merkleBlock.txn.GetNumTransactions()) {
for (const uint256 &hash : vMatch) {
res.push_back(hash.GetHex());
}
}
return res;
}
static UniValue createrawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 3) {
throw std::runtime_error(
// clang-format off
"createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime )\n"
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
"Outputs can be addresses or data.\n"
"Returns hex-encoded raw transaction.\n"
"Note that the transaction's inputs are not signed, and\n"
"it is not stored in the wallet or transmitted to the network.\n"
"\nArguments:\n"
"1. \"inputs\" (array, required) A json array of "
"json objects\n"
" [\n"
" {\n"
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n, (numeric, required) The output number\n"
" \"sequence\":n (numeric, optional) The sequence number\n"
" } \n"
" ,...\n"
" ]\n"
"2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
" [\n"
" {\n"
" \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
" },\n"
" {\n"
" \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
" }\n"
" ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.\n"
" ]\n"
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
"\nExamples:\n"
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
+ HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
// clang-format on
);
}
RPCTypeCheck(request.params,
{UniValue::VARR,
UniValueType(), // ARR or OBJ, checked later
UniValue::VNUM, UniValue::VBOOL},
true);
if (request.params[0].isNull() || request.params[1].isNull()) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Invalid parameter, arguments 1 and 2 must be non-null");
}
UniValue inputs = request.params[0].get_array();
const bool outputs_is_obj = request.params[1].isObject();
UniValue outputs = outputs_is_obj ? request.params[1].get_obj()
: request.params[1].get_array();
CMutableTransaction rawTx;
if (request.params.size() > 2 && !request.params[2].isNull()) {
int64_t nLockTime = request.params[2].get_int64();
if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, locktime out of range");
}
rawTx.nLockTime = nLockTime;
}
for (size_t idx = 0; idx < inputs.size(); idx++) {
const UniValue &input = inputs[idx];
const UniValue &o = input.get_obj();
uint256 txid = ParseHashO(o, "txid");
const UniValue &vout_v = find_value(o, "vout");
if (vout_v.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, missing vout key");
}
if (!vout_v.isNum()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, vout must be a number");
}
int nOutput = vout_v.get_int();
if (nOutput < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, vout must be positive");
}
uint32_t nSequence =
(rawTx.nLockTime ? std::numeric_limits<uint32_t>::max() - 1
: std::numeric_limits<uint32_t>::max());
// Set the sequence number if passed in the parameters object.
const UniValue &sequenceObj = find_value(o, "sequence");
if (sequenceObj.isNum()) {
int64_t seqNr64 = sequenceObj.get_int64();
if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max()) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Invalid parameter, sequence number is out of range");
}
nSequence = uint32_t(seqNr64);
}
CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
rawTx.vin.push_back(in);
}
std::set<CTxDestination> destinations;
if (!outputs_is_obj) {
// Translate array of key-value pairs into dict
UniValue outputs_dict = UniValue(UniValue::VOBJ);
for (size_t i = 0; i < outputs.size(); ++i) {
const UniValue &output = outputs[i];
if (!output.isObject()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, key-value pair not an "
"object as expected");
}
if (output.size() != 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, key-value pair must "
"contain exactly one key");
}
outputs_dict.pushKVs(output);
}
outputs = std::move(outputs_dict);
}
for (const std::string &name_ : outputs.getKeys()) {
if (name_ == "data") {
std::vector<uint8_t> data =
ParseHexV(outputs[name_].getValStr(), "Data");
CTxOut out(Amount::zero(), CScript() << OP_RETURN << data);
rawTx.vout.push_back(out);
} else {
CTxDestination destination =
DecodeDestination(name_, config.GetChainParams());
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
std::string("Invalid Bitcoin address: ") +
name_);
}
if (!destinations.insert(destination).second) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string("Invalid parameter, duplicated address: ") +
name_);
}
CScript scriptPubKey = GetScriptForDestination(destination);
Amount nAmount = AmountFromValue(outputs[name_]);
CTxOut out(nAmount, scriptPubKey);
rawTx.vout.push_back(out);
}
}
return EncodeHexTx(CTransaction(rawTx));
}
static UniValue decoderawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"decoderawtransaction \"hexstring\"\n"
"\nReturn a JSON object representing the serialized, hex-encoded "
"transaction.\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex "
"string\n"
"\nResult:\n"
"{\n"
" \"txid\" : \"id\", (string) The transaction id\n"
" \"hash\" : \"id\", (string) The transaction hash "
"(differs from txid for witness transactions)\n"
" \"size\" : n, (numeric) The transaction size\n"
" \"version\" : n, (numeric) The version\n"
" \"locktime\" : ttt, (numeric) The lock time\n"
" \"vin\" : [ (array of json objects)\n"
" {\n"
" \"txid\": \"id\", (string) The transaction id\n"
" \"vout\": n, (numeric) The output number\n"
" \"scriptSig\": { (json object) The script\n"
" \"asm\": \"asm\", (string) asm\n"
" \"hex\": \"hex\" (string) hex\n"
" },\n"
" \"sequence\": n (numeric) The script sequence number\n"
" }\n"
" ,...\n"
" ],\n"
" \"vout\" : [ (array of json objects)\n"
" {\n"
" \"value\" : x.xxx, (numeric) The value in " +
CURRENCY_UNIT +
"\n"
" \"n\" : n, (numeric) index\n"
" \"scriptPubKey\" : { (json object)\n"
" \"asm\" : \"asm\", (string) the asm\n"
" \"hex\" : \"hex\", (string) the hex\n"
" \"reqSigs\" : n, (numeric) The required sigs\n"
" \"type\" : \"pubkeyhash\", (string) The type, eg "
"'pubkeyhash'\n"
" \"addresses\" : [ (json array of string)\n"
" \"12tvKAXCxZjSmdNbao16dKXC8tRWfcF5oc\" (string) "
"bitcoin address\n"
" ,...\n"
" ]\n"
" }\n"
" }\n"
" ,...\n"
" ],\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("decoderawtransaction", "\"hexstring\"") +
HelpExampleRpc("decoderawtransaction", "\"hexstring\""));
}
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VSTR});
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
UniValue result(UniValue::VOBJ);
TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false);
return result;
}
static UniValue decodescript(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"decodescript \"hexstring\"\n"
"\nDecode a hex-encoded script.\n"
"\nArguments:\n"
"1. \"hexstring\" (string) the hex encoded script\n"
"\nResult:\n"
"{\n"
" \"asm\":\"asm\", (string) Script public key\n"
" \"hex\":\"hex\", (string) hex encoded public key\n"
" \"type\":\"type\", (string) The output type\n"
" \"reqSigs\": n, (numeric) The required signatures\n"
" \"addresses\": [ (json array of string)\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ],\n"
" \"p2sh\",\"address\" (string) address of P2SH script wrapping "
"this redeem script (not returned if the script is already a "
"P2SH).\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("decodescript", "\"hexstring\"") +
HelpExampleRpc("decodescript", "\"hexstring\""));
}
RPCTypeCheck(request.params, {UniValue::VSTR});
UniValue r(UniValue::VOBJ);
CScript script;
if (request.params[0].get_str().size() > 0) {
std::vector<uint8_t> scriptData(
ParseHexV(request.params[0], "argument"));
script = CScript(scriptData.begin(), scriptData.end());
} else {
// Empty scripts are valid.
}
ScriptPubKeyToUniv(script, r, false);
UniValue type;
type = find_value(r, "type");
if (type.isStr() && type.get_str() != "scripthash") {
// P2SH cannot be wrapped in a P2SH. If this script is already a P2SH,
// don't return the address for a P2SH of the P2SH.
r.pushKV("p2sh", EncodeDestination(CScriptID(script), config));
}
return r;
}
/**
* Pushes a JSON object for script verification or signing errors to vErrorsRet.
*/
static void TxInErrorToJSON(const CTxIn &txin, UniValue &vErrorsRet,
const std::string &strMessage) {
UniValue entry(UniValue::VOBJ);
entry.pushKV("txid", txin.prevout.GetTxId().ToString());
entry.pushKV("vout", uint64_t(txin.prevout.GetN()));
entry.pushKV("scriptSig",
HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
entry.pushKV("sequence", uint64_t(txin.nSequence));
entry.pushKV("error", strMessage);
vErrorsRet.push_back(entry);
}
static UniValue combinerawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"combinerawtransaction [\"hexstring\",...]\n"
"\nCombine multiple partially signed transactions into one "
"transaction.\n"
"The combined transaction may be another partially signed "
"transaction or a \n"
"fully signed transaction."
"\nArguments:\n"
"1. \"txs\" (string) A json array of hex strings of "
"partially signed transactions\n"
" [\n"
" \"hexstring\" (string) A transaction hash\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"\"hex\" (string) The hex-encoded raw transaction with "
"signature(s)\n"
"\nExamples:\n" +
HelpExampleCli("combinerawtransaction",
"[\"myhex1\", \"myhex2\", \"myhex3\"]"));
}
UniValue txs = request.params[0].get_array();
std::vector<CMutableTransaction> txVariants(txs.size());
for (unsigned int idx = 0; idx < txs.size(); idx++) {
if (!DecodeHexTx(txVariants[idx], txs[idx].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
strprintf("TX decode failed for tx %d", idx));
}
}
if (txVariants.empty()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transactions");
}
// mergedTx will end up with all the signatures; it
// starts as a clone of the rawtx:
CMutableTransaction mergedTx(txVariants[0]);
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
LOCK(cs_main);
LOCK(g_mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip;
CCoinsViewMemPool viewMempool(&viewChain, g_mempool);
// temporarily switch cache backend to db+mempool view
view.SetBackend(viewMempool);
for (const CTxIn &txin : mergedTx.vin) {
// Load entries from viewChain into view; can fail.
view.AccessCoin(txin.prevout);
}
// switch back to avoid locking mempool for too long
view.SetBackend(viewDummy);
}
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mergedTx);
// Sign what we can:
for (size_t i = 0; i < mergedTx.vin.size(); i++) {
CTxIn &txin = mergedTx.vin[i];
const Coin &coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {
throw JSONRPCError(RPC_VERIFY_ERROR,
"Input not found or already spent");
}
const CScript &prevPubKey = coin.GetTxOut().scriptPubKey;
const Amount &amount = coin.GetTxOut().nValue;
SignatureData sigdata;
// ... and merge in other signatures:
for (const CMutableTransaction &txv : txVariants) {
if (txv.vin.size() > i) {
sigdata = CombineSignatures(
prevPubKey,
TransactionSignatureChecker(&txConst, i, amount), sigdata,
DataFromTransaction(txv, i));
}
}
UpdateTransaction(mergedTx, i, sigdata);
}
return EncodeHexTx(CTransaction(mergedTx));
}
UniValue SignTransaction(CMutableTransaction &mtx,
const UniValue &prevTxsUnival,
CBasicKeyStore *keystore, bool is_temp_keystore,
const UniValue &hashType) {
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
{
LOCK2(cs_main, g_mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip;
CCoinsViewMemPool viewMempool(&viewChain, g_mempool);
// Temporarily switch cache backend to db+mempool view.
view.SetBackend(viewMempool);
for (const CTxIn &txin : mtx.vin) {
// Load entries from viewChain into view; can fail.
view.AccessCoin(txin.prevout);
}
// Switch back to avoid locking mempool for too long.
view.SetBackend(viewDummy);
}
// Add previous txouts given in the RPC call:
if (!prevTxsUnival.isNull()) {
UniValue prevTxs = prevTxsUnival.get_array();
for (size_t idx = 0; idx < prevTxs.size(); ++idx) {
const UniValue &p = prevTxs[idx];
if (!p.isObject()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"expected object with "
"{\"txid'\",\"vout\",\"scriptPubKey\"}");
}
UniValue prevOut = p.get_obj();
RPCTypeCheckObj(prevOut,
{
{"txid", UniValueType(UniValue::VSTR)},
{"vout", UniValueType(UniValue::VNUM)},
{"scriptPubKey", UniValueType(UniValue::VSTR)},
// "amount" is also required but check is done
// below due to UniValue::VNUM erroneously
// not accepting quoted numerics
// (which are valid JSON)
});
uint256 txid = ParseHashO(prevOut, "txid");
int nOut = find_value(prevOut, "vout").get_int();
if (nOut < 0) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"vout must be positive");
}
COutPoint out(txid, nOut);
std::vector<uint8_t> pkData(ParseHexO(prevOut, "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
const Coin &coin = view.AccessCoin(out);
if (!coin.IsSpent() &&
coin.GetTxOut().scriptPubKey != scriptPubKey) {
std::string err("Previous output scriptPubKey mismatch:\n");
err = err + ScriptToAsmStr(coin.GetTxOut().scriptPubKey) +
"\nvs:\n" + ScriptToAsmStr(scriptPubKey);
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
}
CTxOut txout;
txout.scriptPubKey = scriptPubKey;
txout.nValue = Amount::zero();
if (prevOut.exists("amount")) {
txout.nValue =
AmountFromValue(find_value(prevOut, "amount"));
} else {
// amount param is required in replay-protected txs.
// Note that we must check for its presence here rather
// than use RPCTypeCheckObj() above, since UniValue::VNUM
// parser incorrectly parses numerics with quotes, eg
// "3.12" as a string when JSON allows it to also parse
// as numeric. And we have to accept numerics with quotes
// because our own dogfood (our rpc results) always
// produces decimal numbers that are quoted
// eg getbalance returns "3.14152" rather than 3.14152
throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount");
}
view.AddCoin(out, Coin(txout, 1, false), true);
}
// If redeemScript given and not using the local wallet (private
// keys given), add redeemScript to the keystore so it can be
// signed:
if (is_temp_keystore && scriptPubKey.IsPayToScriptHash()) {
RPCTypeCheckObj(
prevOut, {
{"txid", UniValueType(UniValue::VSTR)},
{"vout", UniValueType(UniValue::VNUM)},
{"scriptPubKey", UniValueType(UniValue::VSTR)},
{"redeemScript", UniValueType(UniValue::VSTR)},
});
UniValue v = find_value(prevOut, "redeemScript");
if (!v.isNull()) {
std::vector<uint8_t> rsData(ParseHexV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
keystore->AddCScript(redeemScript);
}
}
}
}
SigHashType sigHashType = SigHashType().withForkId();
if (!hashType.isNull()) {
static std::map<std::string, int> mapSigHashValues = {
{"ALL", SIGHASH_ALL},
{"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY},
{"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID},
{"ALL|FORKID|ANYONECANPAY",
SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
{"NONE", SIGHASH_NONE},
{"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY},
{"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID},
{"NONE|FORKID|ANYONECANPAY",
SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
{"SINGLE", SIGHASH_SINGLE},
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY},
{"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID},
{"SINGLE|FORKID|ANYONECANPAY",
SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY},
};
std::string strHashType = hashType.get_str();
if (!mapSigHashValues.count(strHashType)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
}
sigHashType = SigHashType(mapSigHashValues[strHashType]);
if (!sigHashType.hasForkId()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Signature must use SIGHASH_FORKID");
}
}
// Script verification errors.
UniValue vErrors(UniValue::VARR);
// Use CTransaction for the constant parts of the transaction to avoid
// rehashing.
const CTransaction txConst(mtx);
// Sign what we can:
for (size_t i = 0; i < mtx.vin.size(); i++) {
CTxIn &txin = mtx.vin[i];
const Coin &coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
continue;
}
const CScript &prevPubKey = coin.GetTxOut().scriptPubKey;
const Amount amount = coin.GetTxOut().nValue;
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) ||
(i < mtx.vout.size())) {
ProduceSignature(MutableTransactionSignatureCreator(
keystore, &mtx, i, amount, sigHashType),
prevPubKey, sigdata);
}
sigdata = CombineSignatures(
prevPubKey, TransactionSignatureChecker(&txConst, i, amount),
sigdata, DataFromTransaction(mtx, i));
UpdateTransaction(mtx, i, sigdata);
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(
txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
TransactionSignatureChecker(&txConst, i, amount), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible
// attempt to partially sign).
TxInErrorToJSON(txin, vErrors,
"Unable to sign input, invalid "
"stack size (possibly missing "
"key)");
} else {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
}
}
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
result.pushKV("complete", fComplete);
if (!vErrors.empty()) {
result.pushKV("errors", vErrors);
}
return result;
}
static UniValue signrawtransactionwithkey(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 4) {
throw std::runtime_error(
"signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( "
"[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\","
"\"redeemScript\":\"hex\"},...] sighashtype )\n"
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second argument is an array of base58-encoded private\n"
"keys that will be the only keys used to sign the transaction.\n"
"The third optional argument (may be null) is an array of previous "
"transaction outputs that\n"
"this transaction depends on but may not yet be in the block "
"chain.\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The "
"transaction hex string\n"
"2. \"privkeys\" (string, required) A json "
"array of base58-encoded private keys for signing\n"
" [ (json array of strings)\n"
" \"privatekey\" (string) private key in "
"base58-encoding\n"
" ,...\n"
" ]\n"
"3. \"prevtxs\" (string, optional) An json "
"array of previous dependent transaction outputs\n"
" [ (json array of json objects, "
"or 'null' if none provided)\n"
" {\n"
" \"txid\":\"id\", (string, required) The "
"transaction id\n"
" \"vout\":n, (numeric, required) The "
"output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script "
"key\n"
" \"redeemScript\": \"hex\", (string, required for "
"P2SH) redeem script\n"
" \"amount\": value (numeric, required) The "
"amount spent\n"
" }\n"
" ,...\n"
" ]\n"
"4. \"sighashtype\" (string, optional, "
"default=ALL) The signature hash type. Must be one of\n"
" \"ALL|FORKID\"\n"
" \"NONE|FORKID\"\n"
" \"SINGLE|FORKID\"\n"
" \"ALL|FORKID|ANYONECANPAY\"\n"
" \"NONE|FORKID|ANYONECANPAY\"\n"
" \"SINGLE|FORKID|ANYONECANPAY\"\n"
"\nResult:\n"
"{\n"
" \"hex\" : \"value\", (string) The hex-encoded "
"raw transaction with signature(s)\n"
" \"complete\" : true|false, (boolean) If the "
"transaction has a complete set of signatures\n"
" \"errors\" : [ (json array of objects) "
"Script verification errors (if there are any)\n"
" {\n"
" \"txid\" : \"hash\", (string) The hash of the "
"referenced, previous transaction\n"
" \"vout\" : n, (numeric) The index of the "
"output to spent and used as input\n"
" \"scriptSig\" : \"hex\", (string) The hex-encoded "
"signature script\n"
" \"sequence\" : n, (numeric) Script sequence "
"number\n"
" \"error\" : \"text\" (string) Verification or "
"signing error related to the input\n"
" }\n"
" ,...\n"
" ]\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("signrawtransactionwithkey", "\"myhex\"") +
HelpExampleRpc("signrawtransactionwithkey", "\"myhex\""));
}
RPCTypeCheck(
request.params,
{UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
CBasicKeyStore keystore;
const UniValue &keys = request.params[1].get_array();
for (size_t idx = 0; idx < keys.size(); ++idx) {
UniValue k = keys[idx];
- CBitcoinSecret vchSecret;
- if (!vchSecret.SetString(k.get_str())) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Invalid private key");
- }
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(k.get_str());
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Private key outside allowed range");
+ "Invalid private key");
}
keystore.AddKey(key);
}
return SignTransaction(mtx, request.params[2], &keystore, true,
request.params[3]);
}
static UniValue signrawtransaction(const Config &config,
const JSONRPCRequest &request) {
#ifdef ENABLE_WALLET
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
#endif
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 4) {
throw std::runtime_error(
"signrawtransaction \"hexstring\" ( "
"[{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\","
"\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype "
")\n"
"\nDEPRECATED.Sign inputs for raw transaction (serialized, "
"hex-encoded).\n"
"The second optional argument (may be null) is an array of "
"previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block "
"chain.\n"
"The third optional argument (may be null) is an array of "
"base58-encoded private\n"
"keys that, if given, will be the only keys used to sign the "
"transaction.\n"
#ifdef ENABLE_WALLET
+ HelpRequiringPassphrase(pwallet) +
"\n"
#endif
"\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex "
"string\n"
"2. \"prevtxs\" (string, optional) An json array of previous "
"dependent transaction outputs\n"
" [ (json array of json objects, or 'null' if "
"none provided)\n"
" {\n"
" \"txid\":\"id\", (string, required) The "
"transaction id\n"
" \"vout\":n, (numeric, required) The "
"output number\n"
" \"scriptPubKey\": \"hex\", (string, required) script "
"key\n"
" \"redeemScript\": \"hex\", (string, required for P2SH) "
"redeem script\n"
" \"amount\": value (numeric, required) The "
"amount spent\n"
" }\n"
" ,...\n"
" ]\n"
"3. \"privkeys\" (string, optional) A json array of "
"base58-encoded private keys for signing\n"
" [ (json array of strings, or 'null' if none "
"provided)\n"
" \"privatekey\" (string) private key in base58-encoding\n"
" ,...\n"
" ]\n"
"4. \"sighashtype\" (string, optional, default=ALL) The "
"signature hash type. Must be one of\n"
" \"ALL|FORKID\"\n"
" \"NONE|FORKID\"\n"
" \"SINGLE|FORKID\"\n"
" \"ALL|FORKID|ANYONECANPAY\"\n"
" \"NONE|FORKID|ANYONECANPAY\"\n"
" \"SINGLE|FORKID|ANYONECANPAY\"\n"
"\nResult:\n"
"{\n"
" \"hex\" : \"value\", (string) The hex-encoded raw "
"transaction with signature(s)\n"
" \"complete\" : true|false, (boolean) If the transaction has a "
"complete set of signatures\n"
" \"errors\" : [ (json array of objects) Script "
"verification errors (if there are any)\n"
" {\n"
" \"txid\" : \"hash\", (string) The hash of the "
"referenced, previous transaction\n"
" \"vout\" : n, (numeric) The index of the "
"output to spent and used as input\n"
" \"scriptSig\" : \"hex\", (string) The hex-encoded "
"signature script\n"
" \"sequence\" : n, (numeric) Script sequence "
"number\n"
" \"error\" : \"text\" (string) Verification or "
"signing error related to the input\n"
" }\n"
" ,...\n"
" ]\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("signrawtransaction", "\"myhex\"") +
HelpExampleRpc("signrawtransaction", "\"myhex\""));
}
if (!IsDeprecatedRPCEnabled(gArgs, "signrawtransaction")) {
throw JSONRPCError(
RPC_METHOD_DEPRECATED,
"signrawtransaction is deprecated and will be fully removed in "
"v0.20. "
"To use signrawtransaction in v0.19, restart bitcoind with "
"-deprecatedrpc=signrawtransaction.\n"
"Projects should transition to using signrawtransactionwithkey and "
"signrawtransactionwithwallet before upgrading to v0.20");
}
RPCTypeCheck(
request.params,
{UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
// Make a JSONRPCRequest to pass on to the right signrawtransaction* command
JSONRPCRequest new_request;
new_request.id = request.id;
new_request.URI = std::move(request.URI);
new_request.params.setArray();
// For signing with private keys
if (!request.params[2].isNull()) {
new_request.params.push_back(request.params[0]);
// Note: the prevtxs and privkeys are reversed for
// signrawtransactionwithkey
new_request.params.push_back(request.params[2]);
new_request.params.push_back(request.params[1]);
new_request.params.push_back(request.params[3]);
return signrawtransactionwithkey(config, new_request);
}
// Otherwise sign with the wallet which does not take a privkeys parameter
#ifdef ENABLE_WALLET
else {
new_request.params.push_back(request.params[0]);
new_request.params.push_back(request.params[1]);
new_request.params.push_back(request.params[3]);
return signrawtransactionwithwallet(config, new_request);
}
#endif
// If we have made it this far, then wallet is disabled and no private keys
// were given, so fail here.
throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available.");
}
static UniValue sendrawtransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"sendrawtransaction \"hexstring\" ( allowhighfees )\n"
"\nSubmits raw transaction (serialized, hex-encoded) to local node "
"and network.\n"
"\nAlso see createrawtransaction and signrawtransaction calls.\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw "
"transaction)\n"
"2. allowhighfees (boolean, optional, default=false) Allow high "
"fees\n"
"\nResult:\n"
"\"hex\" (string) The transaction hash in hex\n"
"\nExamples:\n"
"\nCreate a transaction\n" +
HelpExampleCli(
"createrawtransaction",
"\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" "
"\"{\\\"myaddress\\\":0.01}\"") +
"Sign the transaction, and get back the hex\n" +
HelpExampleCli("signrawtransaction", "\"myhex\"") +
"\nSend the transaction (signed hex)\n" +
HelpExampleCli("sendrawtransaction", "\"signedhex\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("sendrawtransaction", "\"signedhex\""));
}
std::promise<void> promise;
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
// parse hex string from parameter
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const uint256 &txid = tx->GetId();
Amount nMaxRawTxFee = maxTxFee;
if (request.params.size() > 1 && request.params[1].get_bool()) {
nMaxRawTxFee = Amount::zero();
}
{ // cs_main scope
LOCK(cs_main);
CCoinsViewCache &view = *pcoinsTip;
bool fHaveChain = false;
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
const Coin &existingCoin = view.AccessCoin(COutPoint(txid, o));
fHaveChain = !existingCoin.IsSpent();
}
bool fHaveMempool = g_mempool.exists(txid);
if (!fHaveMempool && !fHaveChain) {
// Push to local node and sync with wallets.
CValidationState state;
bool fMissingInputs;
bool fLimitFree = false;
if (!AcceptToMemoryPool(config, g_mempool, state, std::move(tx),
fLimitFree, &fMissingInputs, false,
nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED,
FormatStateMessage(state));
}
if (fMissingInputs) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
}
throw JSONRPCError(RPC_TRANSACTION_ERROR,
FormatStateMessage(state));
} else {
// If wallet is enabled, ensure that the wallet has been made
// aware of the new transaction prior to returning. This
// prevents a race where a user might call sendrawtransaction
// with a transaction to/from their wallet, immediately call
// some wallet RPC, and get a stale result because callbacks
// have not yet been processed.
CallFunctionInValidationInterfaceQueue(
[&promise] { promise.set_value(); });
}
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN,
"transaction already in block chain");
} else {
// Make sure we don't block forever if re-sending a transaction
// already in mempool.
promise.set_value();
}
} // cs_main
promise.get_future().wait();
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
CInv inv(MSG_TX, txid);
g_connman->ForEachNode([&inv](CNode *pnode) { pnode->PushInventory(inv); });
return txid.GetHex();
}
static UniValue testmempoolaccept(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
// clang-format off
"testmempoolaccept [\"rawtxs\"] ( allowhighfees )\n"
"\nReturns if raw transaction (serialized, hex-encoded) would be accepted by mempool.\n"
"\nThis checks if the transaction violates the consensus or policy rules.\n"
"\nSee sendrawtransaction call.\n"
"\nArguments:\n"
"1. [\"rawtxs\"] (array, required) An array of hex strings of raw transactions.\n"
" Length must be one for now.\n"
"2. allowhighfees (boolean, optional, default=false) Allow high fees\n"
"\nResult:\n"
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
" Length is exactly one for now.\n"
" {\n"
" \"txid\" (string) The transaction hash in hex\n"
" \"allowed\" (boolean) If the mempool allows this tx to be inserted\n"
" \"reject-reason\" (string) Rejection string (only present when 'allowed' is false)\n"
" }\n"
"]\n"
"\nExamples:\n"
"\nCreate a transaction\n"
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") +
"Sign the transaction, and get back the hex\n"
+ HelpExampleCli("signrawtransaction", "\"myhex\"") +
"\nTest acceptance of the transaction (signed hex)\n"
+ HelpExampleCli("testmempoolaccept", "\"signedhex\"") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]")
// clang-format on
);
}
RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL});
if (request.params[0].get_array().size() != 1) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Array must contain exactly one raw transaction for now");
}
CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_array()[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const uint256 &txid = tx->GetId();
bool fLimitFree = false;
Amount max_raw_tx_fee = maxTxFee;
if (!request.params[1].isNull() && request.params[1].get_bool()) {
max_raw_tx_fee = Amount::zero();
}
UniValue result(UniValue::VARR);
UniValue result_0(UniValue::VOBJ);
result_0.pushKV("txid", txid.GetHex());
CValidationState state;
bool missing_inputs;
bool test_accept_res;
{
LOCK(cs_main);
test_accept_res = AcceptToMemoryPool(
config, g_mempool, state, std::move(tx), fLimitFree,
&missing_inputs, /* bypass_limits */ false, max_raw_tx_fee,
/* test_accept */ true);
}
result_0.pushKV("allowed", test_accept_res);
if (!test_accept_res) {
if (state.IsInvalid()) {
result_0.pushKV("reject-reason",
strprintf("%i: %s", state.GetRejectCode(),
state.GetRejectReason()));
} else if (missing_inputs) {
result_0.pushKV("reject-reason", "missing-inputs");
} else {
result_0.pushKV("reject-reason", state.GetRejectReason());
}
}
result.push_back(std::move(result_0));
return result;
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
// category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
{ "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose","blockhash"} },
{ "rawtransactions", "createrawtransaction", createrawtransaction, {"inputs","outputs","locktime"} },
{ "rawtransactions", "decoderawtransaction", decoderawtransaction, {"hexstring"} },
{ "rawtransactions", "decodescript", decodescript, {"hexstring"} },
{ "rawtransactions", "sendrawtransaction", sendrawtransaction, {"hexstring","allowhighfees"} },
{ "rawtransactions", "combinerawtransaction", combinerawtransaction, {"txs"} },
{ "rawtransactions", "signrawtransaction", signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
{ "rawtransactions", "signrawtransactionwithkey", signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
{ "rawtransactions", "testmempoolaccept", testmempoolaccept, {"rawtxs","allowhighfees"} },
{ "blockchain", "gettxoutproof", gettxoutproof, {"txids", "blockhash"} },
{ "blockchain", "verifytxoutproof", verifytxoutproof, {"proof"} },
};
// clang-format on
void RegisterRawTransactionRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index b6a64a119..9d8597520 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -1,215 +1,211 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <base58.h>
#include <key.h>
#include <script/script.h>
#include <uint256.h>
#include <util.h>
#include <utilstrencodings.h>
#include <test/data/base58_encode_decode.json.h>
#include <test/data/base58_keys_invalid.json.h>
#include <test/data/base58_keys_valid.json.h>
#include <test/jsonutil.h>
#include <test/test_bitcoin.h>
#include <univalue.h>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(base58_tests, BasicTestingSetup)
// Goal: test low-level base58 encoding functionality
BOOST_AUTO_TEST_CASE(base58_EncodeBase58) {
UniValue tests =
read_json(std::string(json_tests::base58_encode_decode,
json_tests::base58_encode_decode +
sizeof(json_tests::base58_encode_decode)));
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
// Allow for extra stuff (useful for comments)
if (test.size() < 2) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::vector<uint8_t> sourcedata = ParseHex(test[0].get_str());
std::string base58string = test[1].get_str();
BOOST_CHECK_MESSAGE(
EncodeBase58(sourcedata.data(),
sourcedata.data() + sourcedata.size()) == base58string,
strTest);
}
}
// Goal: test low-level base58 decoding functionality
BOOST_AUTO_TEST_CASE(base58_DecodeBase58) {
UniValue tests =
read_json(std::string(json_tests::base58_encode_decode,
json_tests::base58_encode_decode +
sizeof(json_tests::base58_encode_decode)));
std::vector<uint8_t> result;
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
// Allow for extra stuff (useful for comments)
if (test.size() < 2) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::vector<uint8_t> expected = ParseHex(test[0].get_str());
std::string base58string = test[1].get_str();
BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest);
BOOST_CHECK_MESSAGE(
result.size() == expected.size() &&
std::equal(result.begin(), result.end(), expected.begin()),
strTest);
}
BOOST_CHECK(!DecodeBase58("invalid", result));
// check that DecodeBase58 skips whitespace, but still fails with unexpected
// non-whitespace at the end.
BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result));
BOOST_CHECK(DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result));
std::vector<uint8_t> expected = ParseHex("971a55");
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
expected.begin(), expected.end());
}
// Goal: check that parsed keys match test payload
BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) {
UniValue tests = read_json(std::string(
json_tests::base58_keys_valid,
json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
- CBitcoinSecret secret;
+ CKey privkey;
CTxDestination destination;
SelectParams(CBaseChainParams::MAIN);
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
// Allow for extra stuff (useful for comments)
if (test.size() < 3) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::string exp_base58string = test[0].get_str();
std::vector<uint8_t> exp_payload = ParseHex(test[1].get_str());
const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
SelectParams(find_value(metadata, "chain").get_str());
if (isPrivkey) {
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
// Must be valid private key
- BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string),
- "!SetString:" + strTest);
- BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest);
- CKey privkey = secret.GetKey();
+ privkey = DecodeSecret(exp_base58string);
+ BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest);
BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed,
"compressed mismatch:" + strTest);
BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() &&
std::equal(privkey.begin(), privkey.end(),
exp_payload.begin()),
"key mismatch:" + strTest);
// Private key must be invalid public key
destination = DecodeLegacyAddr(exp_base58string, Params());
BOOST_CHECK_MESSAGE(!IsValidDestination(destination),
"IsValid privkey as pubkey:" + strTest);
} else {
// Must be valid public key
destination = DecodeLegacyAddr(exp_base58string, Params());
CScript script = GetScriptForDestination(destination);
BOOST_CHECK_MESSAGE(IsValidDestination(destination),
"!IsValid:" + strTest);
BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload));
// Public key must be invalid private key
- secret.SetString(exp_base58string);
- BOOST_CHECK_MESSAGE(!secret.IsValid(),
+ privkey = DecodeSecret(exp_base58string);
+ BOOST_CHECK_MESSAGE(!privkey.IsValid(),
"IsValid pubkey as privkey:" + strTest);
}
}
}
// Goal: check that generated keys match test vectors
BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) {
UniValue tests = read_json(std::string(
json_tests::base58_keys_valid,
json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
// Allow for extra stuff (useful for comments)
if (test.size() < 3) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::string exp_base58string = test[0].get_str();
std::vector<uint8_t> exp_payload = ParseHex(test[1].get_str());
const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
SelectParams(find_value(metadata, "chain").get_str());
if (isPrivkey) {
bool isCompressed = find_value(metadata, "isCompressed").get_bool();
CKey key;
key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
assert(key.IsValid());
- CBitcoinSecret secret;
- secret.SetKey(key);
- BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string,
+ BOOST_CHECK_MESSAGE(EncodeSecret(key) == exp_base58string,
"result mismatch: " + strTest);
} else {
CTxDestination dest;
CScript exp_script(exp_payload.begin(), exp_payload.end());
ExtractDestination(exp_script, dest);
std::string address = EncodeLegacyAddr(dest, Params());
BOOST_CHECK_EQUAL(address, exp_base58string);
}
}
SelectParams(CBaseChainParams::MAIN);
}
// Goal: check that base58 parsing code is robust against a variety of corrupted
// data
BOOST_AUTO_TEST_CASE(base58_keys_invalid) {
// Negative testcases
UniValue tests =
read_json(std::string(json_tests::base58_keys_invalid,
json_tests::base58_keys_invalid +
sizeof(json_tests::base58_keys_invalid)));
- CBitcoinSecret secret;
+ CKey privkey;
CTxDestination destination;
for (unsigned int idx = 0; idx < tests.size(); idx++) {
UniValue test = tests[idx];
std::string strTest = test.write();
// Allow for extra stuff (useful for comments)
if (test.size() < 1) {
BOOST_ERROR("Bad test: " << strTest);
continue;
}
std::string exp_base58string = test[0].get_str();
// must be invalid as public and as private key
for (auto chain : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET,
CBaseChainParams::REGTEST}) {
SelectParams(chain);
destination = DecodeLegacyAddr(exp_base58string, Params());
BOOST_CHECK_MESSAGE(!IsValidDestination(destination),
"IsValid pubkey:" + strTest);
- secret.SetString(exp_base58string);
- BOOST_CHECK_MESSAGE(!secret.IsValid(),
+ privkey = DecodeSecret(exp_base58string);
+ BOOST_CHECK_MESSAGE(!privkey.IsValid(),
"IsValid privkey:" + strTest);
}
}
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index 3b42f9a08..4ac18e730 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -1,1278 +1,1275 @@
// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bloom.h>
#include <base58.h>
#include <clientversion.h>
#include <consensus/merkle.h>
#include <key.h>
#include <merkleblock.h>
#include <random.h>
#include <serialize.h>
#include <streams.h>
#include <uint256.h>
#include <util.h>
#include <utilstrencodings.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <vector>
BOOST_FIXTURE_TEST_SUITE(bloom_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize) {
CBloomFilter filter(3, 0.01, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
BOOST_CHECK_MESSAGE(
filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")),
"Bloom filter doesn't contain just-inserted object!");
// One bit different in first byte
BOOST_CHECK_MESSAGE(
!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")),
"Bloom filter contains something it shouldn't!");
filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
BOOST_CHECK_MESSAGE(
filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")),
"Bloom filter doesn't contain just-inserted object (2)!");
filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
BOOST_CHECK_MESSAGE(
filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")),
"Bloom filter doesn't contain just-inserted object (3)!");
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << filter;
std::vector<uint8_t> vch = ParseHex("03614e9b050000000000000001");
std::vector<char> expected(vch.size());
for (size_t i = 0; i < vch.size(); i++) {
expected[i] = (char)vch[i];
}
BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(),
expected.begin(), expected.end());
BOOST_CHECK_MESSAGE(
filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")),
"Bloom filter doesn't contain just-inserted object!");
filter.clear();
BOOST_CHECK_MESSAGE(
!filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")),
"Bloom filter should be empty!");
}
BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) {
// Same test as bloom_create_insert_serialize, but we add a nTweak of 100
CBloomFilter filter(3, 0.01, 2147483649UL, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
BOOST_CHECK_MESSAGE(
filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")),
"Bloom filter doesn't contain just-inserted object!");
// One bit different in first byte
BOOST_CHECK_MESSAGE(
!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")),
"Bloom filter contains something it shouldn't!");
filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
BOOST_CHECK_MESSAGE(
filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")),
"Bloom filter doesn't contain just-inserted object (2)!");
filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
BOOST_CHECK_MESSAGE(
filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")),
"Bloom filter doesn't contain just-inserted object (3)!");
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << filter;
std::vector<uint8_t> vch = ParseHex("03ce4299050000000100008001");
std::vector<char> expected(vch.size());
for (size_t i = 0; i < vch.size(); i++) {
expected[i] = (char)vch[i];
}
BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(),
expected.begin(), expected.end());
}
BOOST_AUTO_TEST_CASE(bloom_create_insert_key) {
std::string strSecret =
std::string("5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C");
- CBitcoinSecret vchSecret;
- BOOST_CHECK(vchSecret.SetString(strSecret));
-
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(strSecret);
CPubKey pubkey = key.GetPubKey();
std::vector<uint8_t> vchPubKey(pubkey.begin(), pubkey.end());
CBloomFilter filter(2, 0.001, 0, BLOOM_UPDATE_ALL);
filter.insert(vchPubKey);
uint160 hash = pubkey.GetID();
filter.insert(std::vector<uint8_t>(hash.begin(), hash.end()));
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << filter;
std::vector<uint8_t> vch = ParseHex("038fc16b080000000000000001");
std::vector<char> expected(vch.size());
for (size_t i = 0; i < vch.size(); i++) {
expected[i] = (char)vch[i];
}
BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(),
expected.begin(), expected.end());
}
BOOST_AUTO_TEST_CASE(bloom_match) {
// Random real transaction
// (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b)
CDataStream stream(
ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e"
"88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c"
"3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d"
"8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b"
"0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef8"
"7e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffff"
"ffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e27"
"54dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d"
"9032a7b9d64fa43188ac00000000"),
SER_DISK, CLIENT_VERSION);
CTransaction tx(deserialize, stream);
// and one which spends it
// (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
uint8_t ch[] = {
0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65,
0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8,
0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74,
0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00,
0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77,
0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f,
0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2,
0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e,
0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4,
0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2,
0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a,
0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34,
0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97,
0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b,
0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f,
0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00,
0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef,
0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39,
0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00,
0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93,
0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43,
0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
std::vector<uint8_t> vch(ch, ch + sizeof(ch) - 1);
CDataStream spendStream(vch, SER_DISK, CLIENT_VERSION);
CTransaction spendingTx(deserialize, spendStream);
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(uint256S(
"0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter didn't match tx hash");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// byte-reversed tx hash
filter.insert(ParseHex(
"6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4"));
BOOST_CHECK_MESSAGE(
filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter didn't match manually serialized tx hash");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e"
"1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba7"
"1e5aef6405b8643ac4cb7cb3c462aced7f14711a01"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter didn't match input signature");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("046d11fee51b0e60666d5049a9101a72741df480b96ee26488a"
"4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c54"
"7957b7700ff4dfbdefe76036c339"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter didn't match input pub key");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("04943fdd508053c75000106d3bc6e2754dbcff19"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter didn't match output address");
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(spendingTx),
"Simple Bloom filter didn't add output");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("04943fdd508053c75000106d3bc6e2754dbcff19"));
BOOST_CHECK_MESSAGE(filter.MatchAndInsertOutputs(tx),
"Simple Bloom filter didn't match output address");
BOOST_CHECK_MESSAGE(!filter.MatchAndInsertOutputs(spendingTx),
"Simple Bloom filter matched unrelated output");
BOOST_CHECK_MESSAGE(filter.MatchInputs(spendingTx),
"Simple Bloom filter didn't add output");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("a266436d2965547608b9e15d9032a7b9d64fa431"));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter didn't match output address");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(COutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb"
"6ba58f3bdaab65e73b7e9260b"),
0));
BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter didn't match COutPoint");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
COutPoint prevOutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb6"
"ba58f3bdaab65e73b7e9260b"),
0);
{
std::vector<uint8_t> data(32 + sizeof(uint32_t));
memcpy(&data[0], prevOutPoint.GetTxId().begin(), 32);
uint32_t n = prevOutPoint.GetN();
memcpy(&data[32], &n, sizeof(uint32_t));
filter.insert(data);
}
BOOST_CHECK_MESSAGE(
filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter didn't match manually serialized COutPoint");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(uint256S(
"00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436"));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter matched random tx hash");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(ParseHex("0000006d2965547608b9e15d9032a7b9d64fa431"));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter matched random address");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(COutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb"
"6ba58f3bdaab65e73b7e9260b"),
1));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter matched COutPoint for an output "
"we didn't care about");
filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
filter.insert(COutPoint(uint256S("0x000000d70786e899529d71dbeba91ba216982fb"
"6ba58f3bdaab65e73b7e9260b"),
0));
BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx),
"Simple Bloom filter matched COutPoint for an output "
"we didn't care about");
}
BOOST_AUTO_TEST_CASE(merkle_block_1) {
// Random real block
// (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
// With 9 txes
CBlock block;
CDataStream stream(
ParseHex(
"0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb680000000"
"0000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558"
"da2fdb261b4d4c86041b1ab1bf9309010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff07044c86041b0146ffff"
"ffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf2"
"54bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c"
"4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada9027"
"80da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100da"
"b24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd0221"
"00fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb4"
"01ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369e"
"d2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98e"
"c706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad"
"2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21ad"
"c6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e86"
"25a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d8982"
"35e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff02"
"80969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388"
"ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd3"
"88ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c522"
"92d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3"
"889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2f"
"afd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d3"
"1b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83aba"
"f975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cb"
"cba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044"
"022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae7406056"
"58022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e0100"
"3614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c342"
"3e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc"
"2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13"
"fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021"
"e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91"
"c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b4830"
"4502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9"
"236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddc"
"ce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d78990"
"4f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8"
"ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942"
"fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad5652937"
"1864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd9"
"0111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37"
"f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b"
"25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d0014104"
"43bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf"
"7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffff"
"ffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a1232"
"2d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70a"
"e67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176"
"f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf"
"062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b956"
"00db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd40"
"67b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c6"
"9b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e"
"58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b217901000000"
"8b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3c"
"a2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b49"
"1d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33"
"d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312e"
"f1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144a"
"f553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd"
"48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b"
"659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0"
"aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc3"
"1895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9"
"944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830"
"450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1a"
"f03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d9"
"8a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d78990"
"4f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8"
"ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a03"
"8fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261"
"b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f6351285"
"0811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa4207"
"0082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0"
"c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c0000000000"
"1976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000"
"000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed"
"8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc"
"87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded"
"4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e"
"3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29"
"934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695"
"a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93"
"376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e9"
"1349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49"
"304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654"
"d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66"
"ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e88"
"60c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8"
"fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb604203"
"4aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75e"
"ef7942fc9288edd37c32f5c388ac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the last transaction
filter.insert(uint256S(
"0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
std::pair<size_t, uint256> pair = merkleBlock.vMatchedTxn[0];
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b"
"586ec87451f20"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 8);
std::vector<uint256> vMatched;
std::vector<size_t> vIndex;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
// Also match the 8th transaction
filter.insert(uint256S(
"0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2);
BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f16"
"8809cdfae1053"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 7);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
}
BOOST_AUTO_TEST_CASE(merkle_block_2) {
// Random real block
// (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6)
// With 4 txes
CBlock block;
CDataStream stream(
ParseHex(
"0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800"
"000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f"
"0ae1d4d26e49ffff001d00f0a44104010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff0804ffff001d029105ff"
"ffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a62"
"94078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc571"
"9aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6d"
"a58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100"
"aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702"
"205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df15"
"01ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c"
"268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e"
"3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8b"
"be9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a07"
"65b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a"
"68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d"
"8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad7"
"69f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cf"
"c617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d"
"182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b"
"06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27"
"ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06"
"d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c202801000000"
"4a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca"
"42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391f"
"a3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce"
"08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d484"
"8b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f0000000043"
"41046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef1"
"70e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0c"
"ac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f1"
"19b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376a"
"aa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e"
"27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d96"
"3914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a4930460221"
"00b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9"
"022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb900"
"5c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8"
"dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8d"
"d2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5"
"865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68ae"
"e3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the first transaction
filter.insert(uint256S(
"0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
std::pair<size_t, uint256> pair = merkleBlock.vMatchedTxn[0];
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df"
"5b47aecb93b70"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
std::vector<uint256> vMatched;
std::vector<size_t> vIndex;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
// Match an output from the second transaction (the pubkey for address
// 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5)
// This should match the third transaction because it spends the output
// matched
// It also matches the fourth transaction, which spends to the pubkey again
filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad"
"769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875"
"a390f67c1f6c94cfc617c0ea45af"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 4);
BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]);
BOOST_CHECK(merkleBlock.vMatchedTxn[1].second ==
uint256S("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56e"
"bdcacd3069a5f"));
BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
BOOST_CHECK(merkleBlock.vMatchedTxn[2].second ==
uint256S("0x6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df"
"21bea5f4e27e2"));
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 2);
BOOST_CHECK(merkleBlock.vMatchedTxn[3].second ==
uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d70076"
"63ace63cddb23"));
BOOST_CHECK(merkleBlock.vMatchedTxn[3].first == 3);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
}
BOOST_AUTO_TEST_CASE(merkle_block_2_reversed) {
// Like merkle_block_2 except this block gets its transactions reversed in
// order to check non-topological processing.
// Random real block
// (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6)
// With 4 txes
CBlock block;
CDataStream stream(
ParseHex(
"0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800"
"000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f"
"0ae1d4d26e49ffff001d00f0a44104010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff0804ffff001d029105ff"
"ffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a62"
"94078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc571"
"9aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6d"
"a58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100"
"aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702"
"205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df15"
"01ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c"
"268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e"
"3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8b"
"be9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a07"
"65b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a"
"68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d"
"8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad7"
"69f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cf"
"c617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d"
"182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b"
"06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27"
"ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06"
"d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c202801000000"
"4a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca"
"42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391f"
"a3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce"
"08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d484"
"8b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f0000000043"
"41046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef1"
"70e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0c"
"ac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f1"
"19b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376a"
"aa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e"
"27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d96"
"3914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a4930460221"
"00b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9"
"022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb900"
"5c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8"
"dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8d"
"d2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5"
"865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68ae"
"e3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
// Reverse the transactions and recalculate merkle root. The remainder of
// this test is the same as merkle_block_2 above except the transaction
// indices get reversed too.
std::reverse(block.vtx.begin(), block.vtx.end());
block.hashMerkleRoot = BlockMerkleRoot(block);
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the fourth (was first) transaction
filter.insert(uint256S(
"0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
std::pair<size_t, uint256> pair = merkleBlock.vMatchedTxn[0];
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df"
"5b47aecb93b70"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 3);
std::vector<uint256> vMatched;
std::vector<size_t> vIndex;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
// Match an output from the third (was second) transaction (the pubkey for
// address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5) This should match the second
// (was third) transaction because it spends the output matched
// It also matches the first (was fourth) transaction, which spends to the
// pubkey again
filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad"
"769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875"
"a390f67c1f6c94cfc617c0ea45af"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 4);
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d70076"
"63ace63cddb23"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
BOOST_CHECK(merkleBlock.vMatchedTxn[1].second ==
uint256S("0x6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df"
"21bea5f4e27e2"));
BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
BOOST_CHECK(merkleBlock.vMatchedTxn[2].second ==
uint256S("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56e"
"bdcacd3069a5f"));
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 2);
BOOST_CHECK(pair == merkleBlock.vMatchedTxn[3]);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
}
BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none) {
// Random real block
// (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6)
// With 4 txes
CBlock block;
CDataStream stream(
ParseHex(
"0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800"
"000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f"
"0ae1d4d26e49ffff001d00f0a44104010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff0804ffff001d029105ff"
"ffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a62"
"94078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc571"
"9aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6d"
"a58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100"
"aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702"
"205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df15"
"01ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c"
"268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e"
"3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8b"
"be9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a07"
"65b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a"
"68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d"
"8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad7"
"69f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cf"
"c617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d"
"182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b"
"06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27"
"ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06"
"d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c202801000000"
"4a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca"
"42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391f"
"a3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce"
"08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d484"
"8b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f0000000043"
"41046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef1"
"70e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0c"
"ac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f1"
"19b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376a"
"aa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e"
"27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d96"
"3914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a4930460221"
"00b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9"
"022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb900"
"5c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8"
"dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8d"
"d2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5"
"865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68ae"
"e3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE);
// Match the first transaction
filter.insert(uint256S(
"0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
std::pair<size_t, uint256> pair = merkleBlock.vMatchedTxn[0];
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df"
"5b47aecb93b70"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
std::vector<uint256> vMatched;
std::vector<size_t> vIndex;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
// Match an output from the second transaction (the pubkey for address
// 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5)
// This should not match the third transaction though it spends the output
// matched
// It will match the fourth transaction, which has another pay-to-pubkey
// output to the same address
filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad"
"769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875"
"a390f67c1f6c94cfc617c0ea45af"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 3);
BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]);
BOOST_CHECK(merkleBlock.vMatchedTxn[1].second ==
uint256S("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56e"
"bdcacd3069a5f"));
BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
BOOST_CHECK(merkleBlock.vMatchedTxn[2].second ==
uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d70076"
"63ace63cddb23"));
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 3);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
}
BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize) {
// Random real block
// (000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45)
// With one tx
CBlock block;
CDataStream stream(
ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b00"
"00000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3f"
"f60abe184f196367291b4d4c86041b8fa45d6301010000000100000000000"
"00000000000000000000000000000000000000000000000000000ffffffff"
"08044c86041b020a02ffffffff0100f2052a01000000434104ecd3229b057"
"1c3be876feaac0442a9f13c5a572742927af1dc623353ecf8c202225f6486"
"8137a18cdd85cbbb4c74fbccfd4f49639cf1bdc94a5672bb15ad5d4cac000"
"00000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the only transaction
filter.insert(uint256S(
"0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee"
"3a3d669c00cb5"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
std::vector<uint256> vMatched;
std::vector<size_t> vIndex;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
CDataStream merkleStream(SER_NETWORK, PROTOCOL_VERSION);
merkleStream << merkleBlock;
std::vector<uint8_t> vch =
ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b00"
"00000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3f"
"f60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e"
"33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101");
std::vector<char> expected(vch.size());
for (size_t i = 0; i < vch.size(); i++) {
expected[i] = (char)vch[i];
}
BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(),
merkleStream.begin(), merkleStream.end());
}
BOOST_AUTO_TEST_CASE(merkle_block_4) {
// Random real block
// (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
// With 7 txes
CBlock block;
CDataStream stream(
ParseHex(
"0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc88067010000"
"0000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9"
"728776381b4d4c86041b554b852907010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff07044c86041b0136ffff"
"ffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1"
"f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad"
"1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08"
"989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e"
"834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea1022100"
"9253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901"
"ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643"
"207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58cc"
"b3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9d"
"ee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca"
"0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8"
"acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf4"
"9e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fc"
"ad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff30"
"9e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af830000"
"00004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b"
"51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb542"
"8f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6"
"d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c8"
"10ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d"
"31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff01007144"
"60030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000"
"000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72"
"a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6"
"f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423"
"746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6"
"fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd62"
"38f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d225"
"3d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a47304402207812"
"4c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e93022069"
"1d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a360141"
"0462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab8"
"44c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ff"
"fffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4daf"
"daa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab0"
"23abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7"
"fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561"
"f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d8"
"7270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9"
"e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e0084714"
"7cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b"
"2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd7014104"
"62bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844"
"c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffff"
"ffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c38"
"5d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758d"
"f616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11e"
"eb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a"
"0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f"
"464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e"
"88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636"
"030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c"
"00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac0000"
"0000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d"
"850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1a"
"c1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90"
"f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1"
"e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c"
"4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f000000000019"
"76a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f0000000000"
"1976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000"
"000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3"
"f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e2"
"80007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a"
"690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2"
"b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a3"
"3ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf041"
"76b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b"
"903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb"
"87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf8"
"0125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06"
"820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff01"
"00093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888"
"ac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
// Match the last transaction
filter.insert(uint256S(
"0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
std::pair<size_t, uint256> pair = merkleBlock.vMatchedTxn[0];
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f"
"19b15df0ac154"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 6);
std::vector<uint256> vMatched;
std::vector<size_t> vIndex;
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
// Also match the 4th transaction
filter.insert(uint256S(
"0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
merkleBlock = CMerkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2);
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second ==
uint256S("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de673"
"26471df5bc041"));
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 3);
BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) ==
block.hashMerkleRoot);
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
for (size_t i = 0; i < vMatched.size(); i++) {
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
}
}
BOOST_AUTO_TEST_CASE(merkle_block_4_test_p2pubkey_only) {
// Random real block
// (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
// With 7 txes
CBlock block;
CDataStream stream(
ParseHex(
"0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc88067010000"
"0000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9"
"728776381b4d4c86041b554b852907010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff07044c86041b0136ffff"
"ffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1"
"f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad"
"1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08"
"989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e"
"834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea1022100"
"9253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901"
"ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643"
"207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58cc"
"b3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9d"
"ee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca"
"0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8"
"acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf4"
"9e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fc"
"ad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff30"
"9e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af830000"
"00004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b"
"51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb542"
"8f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6"
"d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c8"
"10ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d"
"31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff01007144"
"60030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000"
"000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72"
"a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6"
"f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423"
"746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6"
"fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd62"
"38f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d225"
"3d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a47304402207812"
"4c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e93022069"
"1d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a360141"
"0462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab8"
"44c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ff"
"fffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4daf"
"daa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab0"
"23abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7"
"fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561"
"f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d8"
"7270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9"
"e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e0084714"
"7cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b"
"2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd7014104"
"62bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844"
"c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffff"
"ffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c38"
"5d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758d"
"f616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11e"
"eb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a"
"0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f"
"464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e"
"88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636"
"030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c"
"00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac0000"
"0000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d"
"850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1a"
"c1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90"
"f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1"
"e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c"
"4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f000000000019"
"76a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f0000000000"
"1976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000"
"000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3"
"f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e2"
"80007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a"
"690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2"
"b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a3"
"3ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf041"
"76b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b"
"903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb"
"87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf8"
"0125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06"
"820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff01"
"00093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888"
"ac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_P2PUBKEY_ONLY);
// Match the generation pubkey
filter.insert(ParseHex("04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f"
"134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce"
"13ad1357231a2252247d97a46a91"));
// ...and the output address of the 4th transaction
filter.insert(ParseHex("b6efd80d99179f4f4ff6f4dd0a007d018c385d21"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
// We should match the generation outpoint
BOOST_CHECK(
filter.contains(COutPoint(uint256S("0x147caa76786596590baa4e98f5d9f48b8"
"6c7765e489f7a6ff3360fe5c674360b"),
0)));
// ... but not the 4th transaction's output (its not pay-2-pubkey)
BOOST_CHECK(
!filter.contains(COutPoint(uint256S("0x02981fa052f0481dbc5868f4fc216603"
"5a10f27a03cfd2de67326471df5bc041"),
0)));
}
BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none) {
// Random real block
// (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
// With 7 txes
CBlock block;
CDataStream stream(
ParseHex(
"0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc88067010000"
"0000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9"
"728776381b4d4c86041b554b852907010000000100000000000000000000000000"
"00000000000000000000000000000000000000ffffffff07044c86041b0136ffff"
"ffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1"
"f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad"
"1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08"
"989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e"
"834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea1022100"
"9253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901"
"ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643"
"207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58cc"
"b3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9d"
"ee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca"
"0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8"
"acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf4"
"9e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fc"
"ad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff30"
"9e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af830000"
"00004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b"
"51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb542"
"8f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6"
"d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c8"
"10ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d"
"31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff01007144"
"60030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000"
"000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72"
"a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6"
"f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423"
"746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6"
"fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd62"
"38f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d225"
"3d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a47304402207812"
"4c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e93022069"
"1d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a360141"
"0462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab8"
"44c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ff"
"fffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4daf"
"daa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab0"
"23abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7"
"fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561"
"f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d8"
"7270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9"
"e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e0084714"
"7cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b"
"2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd7014104"
"62bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844"
"c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffff"
"ffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c38"
"5d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758d"
"f616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11e"
"eb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a"
"0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f"
"464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e"
"88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636"
"030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c"
"00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac0000"
"0000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d"
"850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1a"
"c1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90"
"f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1"
"e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c"
"4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f000000000019"
"76a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f0000000000"
"1976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000"
"000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3"
"f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e2"
"80007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a"
"690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2"
"b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a3"
"3ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf041"
"76b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b"
"903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb"
"87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf8"
"0125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06"
"820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff01"
"00093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888"
"ac00000000"),
SER_NETWORK, PROTOCOL_VERSION);
stream >> block;
CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE);
// Match the generation pubkey
filter.insert(ParseHex("04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f"
"134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce"
"13ad1357231a2252247d97a46a91"));
// ...and the output address of the 4th transaction
filter.insert(ParseHex("b6efd80d99179f4f4ff6f4dd0a007d018c385d21"));
CMerkleBlock merkleBlock(block, filter);
BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
// We shouldn't match any outpoints (UPDATE_NONE)
BOOST_CHECK(
!filter.contains(COutPoint(uint256S("0x147caa76786596590baa4e98f5d9f48b"
"86c7765e489f7a6ff3360fe5c674360b"),
0)));
BOOST_CHECK(
!filter.contains(COutPoint(uint256S("0x02981fa052f0481dbc5868f4fc216603"
"5a10f27a03cfd2de67326471df5bc041"),
0)));
}
static std::vector<uint8_t> RandomData() {
uint256 r = InsecureRand256();
return std::vector<uint8_t>(r.begin(), r.end());
}
BOOST_AUTO_TEST_CASE(rolling_bloom) {
// last-100-entry, 1% false positive:
CRollingBloomFilter rb1(100, 0.01);
// Overfill:
static const int DATASIZE = 399;
std::vector<uint8_t> data[DATASIZE];
for (int i = 0; i < DATASIZE; i++) {
data[i] = RandomData();
rb1.insert(data[i]);
}
// Last 100 guaranteed to be remembered:
for (int i = 299; i < DATASIZE; i++) {
BOOST_CHECK(rb1.contains(data[i]));
}
// false positive rate is 1%, so we should get about 100 hits if
// testing 10,000 random keys. We get worst-case false positive
// behavior when the filter is as full as possible, which is
// when we've inserted one minus an integer multiple of nElement*2.
unsigned int nHits = 0;
for (int i = 0; i < 10000; i++) {
if (rb1.contains(RandomData())) ++nHits;
}
// Run test_bitcoin with --log_level=message to see BOOST_TEST_MESSAGEs:
BOOST_TEST_MESSAGE("RollingBloomFilter got "
<< nHits << " false positives (~100 expected)");
// Insanely unlikely to get a fp count outside this range:
BOOST_CHECK(nHits > 25);
BOOST_CHECK(nHits < 175);
BOOST_CHECK(rb1.contains(data[DATASIZE - 1]));
rb1.reset();
BOOST_CHECK(!rb1.contains(data[DATASIZE - 1]));
// Now roll through data, make sure last 100 entries
// are always remembered:
for (int i = 0; i < DATASIZE; i++) {
if (i >= 100) BOOST_CHECK(rb1.contains(data[i - 100]));
rb1.insert(data[i]);
BOOST_CHECK(rb1.contains(data[i]));
}
// Insert 999 more random entries:
for (int i = 0; i < 999; i++) {
std::vector<uint8_t> d = RandomData();
rb1.insert(d);
BOOST_CHECK(rb1.contains(d));
}
// Sanity check to make sure the filter isn't just filling up:
nHits = 0;
for (int i = 0; i < DATASIZE; i++) {
if (rb1.contains(data[i])) ++nHits;
}
// Expect about 5 false positives, more than 100 means
// something is definitely broken.
BOOST_TEST_MESSAGE("RollingBloomFilter got "
<< nHits << " false positives (~5 expected)");
BOOST_CHECK(nHits < 100);
// last-1000-entry, 0.01% false positive:
CRollingBloomFilter rb2(1000, 0.001);
for (int i = 0; i < DATASIZE; i++) {
rb2.insert(data[i]);
}
// ... room for all of them:
for (int i = 0; i < DATASIZE; i++) {
BOOST_CHECK(rb2.contains(data[i]));
}
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 7bf64d313..8132d0d45 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -1,301 +1,296 @@
// Copyright (c) 2012-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <key.h>
#include <base58.h>
#include <dstencode.h>
#include <script/script.h>
#include <uint256.h>
#include <util.h>
#include <utilstrencodings.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <string>
#include <vector>
static const std::string strSecret1 =
"5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj";
static const std::string strSecret2 =
"5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3";
static const std::string strSecret1C =
"Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw";
static const std::string strSecret2C =
"L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g";
static const std::string addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ";
static const std::string addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ";
static const std::string addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs";
static const std::string addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs";
static const std::string strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF";
// get r value produced by ECDSA signing algorithm
// (assumes ECDSA r is encoded in the canonical manner)
std::vector<uint8_t> get_r_ECDSA(std::vector<uint8_t> sigECDSA) {
std::vector<uint8_t> ret(32, 0);
assert(sigECDSA[2] == 2);
int rlen = sigECDSA[3];
assert(rlen <= 33);
assert(sigECDSA[4 + rlen] == 2);
if (rlen == 33) {
assert(sigECDSA[4] == 0);
std::copy(sigECDSA.begin() + 5, sigECDSA.begin() + 37, ret.begin());
} else {
std::copy(sigECDSA.begin() + 4, sigECDSA.begin() + (4 + rlen),
ret.begin() + (32 - rlen));
}
return ret;
}
BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(internal_test) {
// test get_r_ECDSA (defined above) to make sure it's working properly
BOOST_CHECK(get_r_ECDSA(ParseHex(
"3045022100c6ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40"
"c82e56a6cc37f9b50463111c9f9229b8f3b3")) ==
ParseHex("c6ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f78"));
BOOST_CHECK(get_r_ECDSA(ParseHex(
"3045022046ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40"
"c82e56a6cc37f9b50463111c9f9229b8f3b3")) ==
ParseHex("46ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f78"));
BOOST_CHECK(get_r_ECDSA(ParseHex(
"3045021f4b5f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40"
"c82e56a6cc37f9b50463111c9f9229b8f3b3")) ==
ParseHex("004b5f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f78"));
BOOST_CHECK(get_r_ECDSA(ParseHex(
"3045021e5f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40"
"c82e56a6cc37f9b50463111c9f9229b8f3b3")) ==
ParseHex("00005f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f78"));
}
BOOST_AUTO_TEST_CASE(key_test1) {
- CBitcoinSecret bsecret1, bsecret2, bsecret1C, bsecret2C, baddress1;
- BOOST_CHECK(bsecret1.SetString(strSecret1));
- BOOST_CHECK(bsecret2.SetString(strSecret2));
- BOOST_CHECK(bsecret1C.SetString(strSecret1C));
- BOOST_CHECK(bsecret2C.SetString(strSecret2C));
- BOOST_CHECK(!baddress1.SetString(strAddressBad));
-
- CKey key1 = bsecret1.GetKey();
- BOOST_CHECK(key1.IsCompressed() == false);
- CKey key2 = bsecret2.GetKey();
- BOOST_CHECK(key2.IsCompressed() == false);
- CKey key1C = bsecret1C.GetKey();
- BOOST_CHECK(key1C.IsCompressed() == true);
- CKey key2C = bsecret2C.GetKey();
- BOOST_CHECK(key2C.IsCompressed() == true);
+ CKey key1 = DecodeSecret(strSecret1);
+ BOOST_CHECK(key1.IsValid() && !key1.IsCompressed());
+ CKey key2 = DecodeSecret(strSecret2);
+ BOOST_CHECK(key2.IsValid() && !key2.IsCompressed());
+ CKey key1C = DecodeSecret(strSecret1C);
+ BOOST_CHECK(key1C.IsValid() && key1C.IsCompressed());
+ CKey key2C = DecodeSecret(strSecret2C);
+ BOOST_CHECK(key2C.IsValid() && key2C.IsCompressed());
+ CKey bad_key = DecodeSecret(strAddressBad);
+ BOOST_CHECK(!bad_key.IsValid());
CPubKey pubkey1 = key1.GetPubKey();
CPubKey pubkey2 = key2.GetPubKey();
CPubKey pubkey1C = key1C.GetPubKey();
CPubKey pubkey2C = key2C.GetPubKey();
BOOST_CHECK(key1.VerifyPubKey(pubkey1));
BOOST_CHECK(!key1.VerifyPubKey(pubkey1C));
BOOST_CHECK(!key1.VerifyPubKey(pubkey2));
BOOST_CHECK(!key1.VerifyPubKey(pubkey2C));
BOOST_CHECK(!key1C.VerifyPubKey(pubkey1));
BOOST_CHECK(key1C.VerifyPubKey(pubkey1C));
BOOST_CHECK(!key1C.VerifyPubKey(pubkey2));
BOOST_CHECK(!key1C.VerifyPubKey(pubkey2C));
BOOST_CHECK(!key2.VerifyPubKey(pubkey1));
BOOST_CHECK(!key2.VerifyPubKey(pubkey1C));
BOOST_CHECK(key2.VerifyPubKey(pubkey2));
BOOST_CHECK(!key2.VerifyPubKey(pubkey2C));
BOOST_CHECK(!key2C.VerifyPubKey(pubkey1));
BOOST_CHECK(!key2C.VerifyPubKey(pubkey1C));
BOOST_CHECK(!key2C.VerifyPubKey(pubkey2));
BOOST_CHECK(key2C.VerifyPubKey(pubkey2C));
const CChainParams &chainParams = Params();
BOOST_CHECK(DecodeDestination(addr1, chainParams) ==
CTxDestination(pubkey1.GetID()));
BOOST_CHECK(DecodeDestination(addr2, chainParams) ==
CTxDestination(pubkey2.GetID()));
BOOST_CHECK(DecodeDestination(addr1C, chainParams) ==
CTxDestination(pubkey1C.GetID()));
BOOST_CHECK(DecodeDestination(addr2C, chainParams) ==
CTxDestination(pubkey2C.GetID()));
for (int n = 0; n < 16; n++) {
std::string strMsg = strprintf("Very secret message %i: 11", n);
uint256 hashMsg = Hash(strMsg.begin(), strMsg.end());
// normal ECDSA signatures
std::vector<uint8_t> sign1, sign2, sign1C, sign2C;
BOOST_CHECK(key1.SignECDSA(hashMsg, sign1));
BOOST_CHECK(key2.SignECDSA(hashMsg, sign2));
BOOST_CHECK(key1C.SignECDSA(hashMsg, sign1C));
BOOST_CHECK(key2C.SignECDSA(hashMsg, sign2C));
BOOST_CHECK(pubkey1.VerifyECDSA(hashMsg, sign1));
BOOST_CHECK(!pubkey1.VerifyECDSA(hashMsg, sign2));
BOOST_CHECK(pubkey1.VerifyECDSA(hashMsg, sign1C));
BOOST_CHECK(!pubkey1.VerifyECDSA(hashMsg, sign2C));
BOOST_CHECK(!pubkey2.VerifyECDSA(hashMsg, sign1));
BOOST_CHECK(pubkey2.VerifyECDSA(hashMsg, sign2));
BOOST_CHECK(!pubkey2.VerifyECDSA(hashMsg, sign1C));
BOOST_CHECK(pubkey2.VerifyECDSA(hashMsg, sign2C));
BOOST_CHECK(pubkey1C.VerifyECDSA(hashMsg, sign1));
BOOST_CHECK(!pubkey1C.VerifyECDSA(hashMsg, sign2));
BOOST_CHECK(pubkey1C.VerifyECDSA(hashMsg, sign1C));
BOOST_CHECK(!pubkey1C.VerifyECDSA(hashMsg, sign2C));
BOOST_CHECK(!pubkey2C.VerifyECDSA(hashMsg, sign1));
BOOST_CHECK(pubkey2C.VerifyECDSA(hashMsg, sign2));
BOOST_CHECK(!pubkey2C.VerifyECDSA(hashMsg, sign1C));
BOOST_CHECK(pubkey2C.VerifyECDSA(hashMsg, sign2C));
// compact ECDSA signatures (with key recovery)
std::vector<uint8_t> csign1, csign2, csign1C, csign2C;
BOOST_CHECK(key1.SignCompact(hashMsg, csign1));
BOOST_CHECK(key2.SignCompact(hashMsg, csign2));
BOOST_CHECK(key1C.SignCompact(hashMsg, csign1C));
BOOST_CHECK(key2C.SignCompact(hashMsg, csign2C));
CPubKey rkey1, rkey2, rkey1C, rkey2C;
BOOST_CHECK(rkey1.RecoverCompact(hashMsg, csign1));
BOOST_CHECK(rkey2.RecoverCompact(hashMsg, csign2));
BOOST_CHECK(rkey1C.RecoverCompact(hashMsg, csign1C));
BOOST_CHECK(rkey2C.RecoverCompact(hashMsg, csign2C));
BOOST_CHECK(rkey1 == pubkey1);
BOOST_CHECK(rkey2 == pubkey2);
BOOST_CHECK(rkey1C == pubkey1C);
BOOST_CHECK(rkey2C == pubkey2C);
// Schnorr signatures
std::vector<uint8_t> ssign1, ssign2, ssign1C, ssign2C;
BOOST_CHECK(key1.SignSchnorr(hashMsg, ssign1));
BOOST_CHECK(key2.SignSchnorr(hashMsg, ssign2));
BOOST_CHECK(key1C.SignSchnorr(hashMsg, ssign1C));
BOOST_CHECK(key2C.SignSchnorr(hashMsg, ssign2C));
BOOST_CHECK(pubkey1.VerifySchnorr(hashMsg, ssign1));
BOOST_CHECK(!pubkey1.VerifySchnorr(hashMsg, ssign2));
BOOST_CHECK(pubkey1.VerifySchnorr(hashMsg, ssign1C));
BOOST_CHECK(!pubkey1.VerifySchnorr(hashMsg, ssign2C));
BOOST_CHECK(!pubkey2.VerifySchnorr(hashMsg, ssign1));
BOOST_CHECK(pubkey2.VerifySchnorr(hashMsg, ssign2));
BOOST_CHECK(!pubkey2.VerifySchnorr(hashMsg, ssign1C));
BOOST_CHECK(pubkey2.VerifySchnorr(hashMsg, ssign2C));
BOOST_CHECK(pubkey1C.VerifySchnorr(hashMsg, ssign1));
BOOST_CHECK(!pubkey1C.VerifySchnorr(hashMsg, ssign2));
BOOST_CHECK(pubkey1C.VerifySchnorr(hashMsg, ssign1C));
BOOST_CHECK(!pubkey1C.VerifySchnorr(hashMsg, ssign2C));
BOOST_CHECK(!pubkey2C.VerifySchnorr(hashMsg, ssign1));
BOOST_CHECK(pubkey2C.VerifySchnorr(hashMsg, ssign2));
BOOST_CHECK(!pubkey2C.VerifySchnorr(hashMsg, ssign1C));
BOOST_CHECK(pubkey2C.VerifySchnorr(hashMsg, ssign2C));
// check deterministicity of ECDSA & Schnorr
BOOST_CHECK(sign1 == sign1C);
BOOST_CHECK(sign2 == sign2C);
BOOST_CHECK(ssign1 == ssign1C);
BOOST_CHECK(ssign2 == ssign2C);
// Extract r value from ECDSA and Schnorr. Make sure they are
// distinct (nonce reuse would be dangerous and can leak private key).
std::vector<uint8_t> rE1 = get_r_ECDSA(sign1);
BOOST_CHECK(ssign1.size() == 64);
std::vector<uint8_t> rS1(ssign1.begin(), ssign1.begin() + 32);
BOOST_CHECK(rE1.size() == 32);
BOOST_CHECK(rS1.size() == 32);
BOOST_CHECK(rE1 != rS1);
std::vector<uint8_t> rE2 = get_r_ECDSA(sign2);
BOOST_CHECK(ssign2.size() == 64);
std::vector<uint8_t> rS2(ssign2.begin(), ssign2.begin() + 32);
BOOST_CHECK(rE2.size() == 32);
BOOST_CHECK(rS2.size() == 32);
BOOST_CHECK(rE2 != rS2);
}
// test deterministic signing expected values
std::vector<uint8_t> detsig, detsigc;
std::string strMsg = "Very deterministic message";
uint256 hashMsg = Hash(strMsg.begin(), strMsg.end());
// ECDSA
BOOST_CHECK(key1.SignECDSA(hashMsg, detsig));
BOOST_CHECK(key1C.SignECDSA(hashMsg, detsigc));
BOOST_CHECK(detsig == detsigc);
BOOST_CHECK(detsig ==
ParseHex("3045022100c6ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8"
"24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40"
"c82e56a6cc37f9b50463111c9f9229b8f3b3"));
BOOST_CHECK(key2.SignECDSA(hashMsg, detsig));
BOOST_CHECK(key2C.SignECDSA(hashMsg, detsigc));
BOOST_CHECK(detsig == detsigc);
BOOST_CHECK(detsig ==
ParseHex("304502210094dc5a77b8d5db6b42b66c29d7033cd873fac7a1272"
"4a90373726f60bb9f852a02204eb4c98b9a2f5c017f9417ba7c43"
"279c20c84bb058dc05b3beeb9333016b15bb"));
// Compact
BOOST_CHECK(key1.SignCompact(hashMsg, detsig));
BOOST_CHECK(key1C.SignCompact(hashMsg, detsigc));
BOOST_CHECK(detsig ==
ParseHex("1b8c56f224d51415e6ce329144aa1e1c1563e297a005f450df015"
"14f3d047681760277e79d57502df27b8feebb001a588aa3a8c2bc"
"f5b2367273c15f840638cfc8"));
BOOST_CHECK(detsigc ==
ParseHex("1f8c56f224d51415e6ce329144aa1e1c1563e297a005f450df015"
"14f3d047681760277e79d57502df27b8feebb001a588aa3a8c2bc"
"f5b2367273c15f840638cfc8"));
BOOST_CHECK(key2.SignCompact(hashMsg, detsig));
BOOST_CHECK(key2C.SignCompact(hashMsg, detsigc));
BOOST_CHECK(detsig ==
ParseHex("1c9ffc56b38fbfc0e3eb2c42dff99d2375982449f35019c1b3d56"
"ca62bef187c5103e483a0ad481eaacc224fef4ee2995027300d5f"
"2457f7a20c43547aeddbae6e"));
BOOST_CHECK(detsigc ==
ParseHex("209ffc56b38fbfc0e3eb2c42dff99d2375982449f35019c1b3d56"
"ca62bef187c5103e483a0ad481eaacc224fef4ee2995027300d5f"
"2457f7a20c43547aeddbae6e"));
// Schnorr
BOOST_CHECK(key1.SignSchnorr(hashMsg, detsig));
BOOST_CHECK(key1C.SignSchnorr(hashMsg, detsigc));
BOOST_CHECK(detsig == detsigc);
BOOST_CHECK(detsig ==
ParseHex("2c56731ac2f7a7e7f11518fc7722a166b02438924ca9d8b4d1113"
"47b81d0717571846de67ad3d913a8fdf9d8f3f73161a4c48ae81c"
"b183b214765feb86e255ce"));
BOOST_CHECK(key2.SignSchnorr(hashMsg, detsig));
BOOST_CHECK(key2C.SignSchnorr(hashMsg, detsigc));
BOOST_CHECK(detsig == detsigc);
BOOST_CHECK(detsig ==
ParseHex("e7167ae0afbba6019b4c7fcfe6de79165d555e8295bd72da1b8aa"
"1a5b54305880517cace1bcb0cb515e2eeaffd49f1e4dd49fd7282"
"6b4b1573c84da49a38405d"));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/sigcache_tests.cpp b/src/test/sigcache_tests.cpp
index d949e0aca..d75c5f65b 100644
--- a/src/test/sigcache_tests.cpp
+++ b/src/test/sigcache_tests.cpp
@@ -1,123 +1,119 @@
// Copyright (c) 2012-2015 The Bitcoin Core developers
// Copyright (c) 2019- The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// (based on key_tests.cpp)
#include <script/sigcache.h>
#include <base58.h>
#include <dstencode.h>
#include <key.h>
#include <streams.h>
#include <tinyformat.h>
#include <utilstrencodings.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <string>
#include <vector>
static const std::string strSecret1 =
"5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj";
static const std::string strSecret1C =
"Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw";
/**
* Sigcache is only accessible via CachingTransactionSignatureChecker
* as friend.
*/
class TestCachingTransactionSignatureChecker {
CachingTransactionSignatureChecker *pchecker;
public:
TestCachingTransactionSignatureChecker(
CachingTransactionSignatureChecker &checkerarg) {
pchecker = &checkerarg;
}
inline bool VerifyAndStore(const std::vector<uint8_t> &vchSig,
const CPubKey &pubkey, const uint256 &sighash) {
return pchecker->VerifySignature(vchSig, pubkey, sighash);
}
inline bool IsCached(const std::vector<uint8_t> &vchSig,
const CPubKey &pubkey, const uint256 &sighash) {
return pchecker->IsCached(vchSig, pubkey, sighash);
}
};
BOOST_FIXTURE_TEST_SUITE(sigcache_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(sig_pubkey_hash_variations) {
/**
* Making CachingTransactionSignatureChecker requires a tx. So we make a
* dummy transaction (doesn't matter what it is) to construct it.
*/
CDataStream stream(
ParseHex(
"010000000122739e70fbee987a8be1788395a2f2e6ad18ccb7ff611cd798071539"
"dde3c38e000000000151ffffffff010000000000000000016a00000000"),
SER_NETWORK, PROTOCOL_VERSION);
CTransaction dummyTx(deserialize, stream);
PrecomputedTransactionData txdata(dummyTx);
CachingTransactionSignatureChecker checker(&dummyTx, 0, 0 * SATOSHI, true,
txdata);
TestCachingTransactionSignatureChecker testChecker(checker);
- CBitcoinSecret bsecret1, bsecret1C;
- BOOST_CHECK(bsecret1.SetString(strSecret1));
- BOOST_CHECK(bsecret1C.SetString(strSecret1C));
-
- CKey key1 = bsecret1.GetKey();
+ CKey key1 = DecodeSecret(strSecret1);
BOOST_CHECK(key1.IsCompressed() == false);
- CKey key1C = bsecret1C.GetKey();
+ CKey key1C = DecodeSecret(strSecret1C);
BOOST_CHECK(key1C.IsCompressed() == true);
CPubKey pubkey1 = key1.GetPubKey();
CPubKey pubkey1C = key1C.GetPubKey();
for (int n = 0; n < 16; n++) {
std::string strMsg = strprintf("Sigcache test1 %i: xx", n);
uint256 hashMsg = Hash(strMsg.begin(), strMsg.end());
uint256 hashMsg2 = Hash(strMsg.begin() + 1, strMsg.end());
std::vector<uint8_t> sig;
BOOST_CHECK(key1.SignECDSA(hashMsg, sig));
std::vector<uint8_t> sig2;
BOOST_CHECK(key1.SignECDSA(hashMsg2, sig2));
// cross-check
BOOST_CHECK(!testChecker.VerifyAndStore(sig2, pubkey1, hashMsg));
BOOST_CHECK(!testChecker.VerifyAndStore(sig, pubkey1, hashMsg2));
// that should not have put them in cache...
BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg));
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg2));
// check that it's not in cache at start
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg));
BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg2));
// Insert into cache
BOOST_CHECK(testChecker.VerifyAndStore(sig, pubkey1, hashMsg));
BOOST_CHECK(testChecker.VerifyAndStore(sig2, pubkey1, hashMsg2));
// check that it's in
BOOST_CHECK(testChecker.IsCached(sig, pubkey1, hashMsg));
BOOST_CHECK(testChecker.IsCached(sig2, pubkey1, hashMsg2));
// check that different signature hits different entry
BOOST_CHECK(!testChecker.IsCached(sig2, pubkey1, hashMsg));
// check that compressed pubkey hits different entry
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1C, hashMsg));
// check that different message hits different entry
BOOST_CHECK(!testChecker.IsCached(sig, pubkey1, hashMsg2));
// compressed key is for same privkey, so verifying works:
BOOST_CHECK(testChecker.VerifyAndStore(sig, pubkey1C, hashMsg));
// now we *should* get a hit
BOOST_CHECK(testChecker.IsCached(sig, pubkey1C, hashMsg));
}
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 91e7709bb..d4f22aaa6 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1,1486 +1,1461 @@
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <base58.h>
#include <chain.h>
#include <config.h>
#include <core_io.h>
#include <dstencode.h>
#include <init.h>
#include <merkleblock.h>
#include <rpc/server.h>
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
#include <util.h>
#include <utiltime.h>
#include <validation.h>
#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <univalue.h>
#include <cstdint>
#include <fstream>
#include <iostream>
static int64_t DecodeDumpTime(const std::string &str) {
static const boost::posix_time::ptime epoch =
boost::posix_time::from_time_t(0);
static const std::locale loc(
std::locale::classic(),
new boost::posix_time::time_input_facet("%Y-%m-%dT%H:%M:%SZ"));
std::istringstream iss(str);
iss.imbue(loc);
boost::posix_time::ptime ptime(boost::date_time::not_a_date_time);
iss >> ptime;
if (ptime.is_not_a_date_time()) return 0;
return (ptime - epoch).total_seconds();
}
static std::string EncodeDumpString(const std::string &str) {
std::stringstream ret;
for (uint8_t c : str) {
if (c <= 32 || c >= 128 || c == '%') {
ret << '%' << HexStr(&c, &c + 1);
} else {
ret << c;
}
}
return ret.str();
}
static std::string DecodeDumpString(const std::string &str) {
std::stringstream ret;
for (unsigned int pos = 0; pos < str.length(); pos++) {
uint8_t c = str[pos];
if (c == '%' && pos + 2 < str.length()) {
c = (((str[pos + 1] >> 6) * 9 + ((str[pos + 1] - '0') & 15)) << 4) |
((str[pos + 2] >> 6) * 9 + ((str[pos + 2] - '0') & 15));
pos += 2;
}
ret << c;
}
return ret.str();
}
UniValue importprivkey(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
"importprivkey \"privkey\" ( \"label\" ) ( rescan )\n"
"\nAdds a private key (as returned by dumpprivkey) to your wallet. "
"Requires a new wallet backup.\n"
"\nArguments:\n"
"1. \"privkey\" (string, required) The private key (see "
"dumpprivkey)\n"
"2. \"label\" (string, optional, default=\"\") An "
"optional label\n"
"3. rescan (boolean, optional, default=true) Rescan "
"the wallet for transactions\n"
"\nNote: This call can take minutes to complete if rescan is true, "
"during that time, other rpc calls\n"
"may report that the imported key exists but related transactions "
"are still missing, leading to temporarily incorrect/bogus "
"balances and unspent outputs until rescan completes.\n"
"\nExamples:\n"
"\nDump a private key\n" +
HelpExampleCli("dumpprivkey", "\"myaddress\"") +
"\nImport the private key with rescan\n" +
HelpExampleCli("importprivkey", "\"mykey\"") +
"\nImport using a label and without rescan\n" +
HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
"\nImport using default blank label and without rescan\n" +
HelpExampleCli("importprivkey", "\"mykey\" \"\" false") +
"\nAs a JSON-RPC call\n" +
HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false"));
WalletRescanReserver reserver(pwallet);
bool fRescan = true;
{
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
std::string strSecret = request.params[0].get_str();
std::string strLabel = "";
if (!request.params[1].isNull()) {
strLabel = request.params[1].get_str();
}
// Whether to perform rescan after import
if (!request.params[2].isNull()) {
fRescan = request.params[2].get_bool();
}
if (fRescan && fPruneMode) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Rescan is disabled in pruned mode");
}
if (fRescan && !reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Wallet is currently rescanning. Abort existing "
"rescan or wait.");
}
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(strSecret);
-
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Invalid private key encoding");
- }
-
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(strSecret);
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Private key outside allowed range");
+ "Invalid private key encoding");
}
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CKeyID vchAddress = pubkey.GetID();
{
pwallet->MarkDirty();
// We don't know which corresponding address will be used; label
// them all
for (const auto &dest : GetAllDestinationsForKey(pubkey)) {
pwallet->SetAddressBook(dest, strLabel, "receive");
}
// Don't throw error in case a key is already there
if (pwallet->HaveKey(vchAddress)) {
return NullUniValue;
}
pwallet->LearnAllRelatedScripts(pubkey);
// whenever a key is imported, we need to scan the whole chain
pwallet->UpdateTimeFirstKey(1);
pwallet->mapKeyMetadata[vchAddress].nCreateTime = 1;
if (!pwallet->AddKeyPubKey(key, pubkey)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding key to wallet");
}
}
}
if (fRescan) {
pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
}
return NullUniValue;
}
UniValue abortrescan(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error("abortrescan\n"
"\nStops current wallet rescan triggered e.g. "
"by an importprivkey call.\n"
"\nExamples:\n"
"\nImport a private key\n" +
HelpExampleCli("importprivkey", "\"mykey\"") +
"\nAbort the running wallet rescan\n" +
HelpExampleCli("abortrescan", "") +
"\nAs a JSON-RPC call\n" +
HelpExampleRpc("abortrescan", ""));
}
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) {
return false;
}
pwallet->AbortRescan();
return true;
}
static void ImportAddress(CWallet *, const CTxDestination &dest,
const std::string &strLabel);
static void ImportScript(CWallet *const pwallet, const CScript &script,
const std::string &strLabel, bool isRedeemScript) {
if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the "
"private key for this address or "
"script");
}
pwallet->MarkDirty();
if (!pwallet->HaveWatchOnly(script) &&
!pwallet->AddWatchOnly(script, 0 /* nCreateTime */)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
}
if (isRedeemScript) {
if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding p2sh redeemScript to wallet");
}
ImportAddress(pwallet, CScriptID(script), strLabel);
} else {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
pwallet->SetAddressBook(destination, strLabel, "receive");
}
}
}
static void ImportAddress(CWallet *const pwallet, const CTxDestination &dest,
const std::string &strLabel) {
CScript script = GetScriptForDestination(dest);
ImportScript(pwallet, script, strLabel, false);
// add to address book or update label
if (IsValidDestination(dest)) {
pwallet->SetAddressBook(dest, strLabel, "receive");
}
}
UniValue importaddress(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 4) {
throw std::runtime_error(
"importaddress \"address\" ( \"label\" rescan p2sh )\n"
"\nAdds a script (in hex) or address that can be watched as if it "
"were in your wallet but cannot be used to spend. Requires a new "
"wallet backup.\n"
"\nArguments:\n"
"1. \"script\" (string, required) The hex-encoded script "
"(or address)\n"
"2. \"label\" (string, optional, default=\"\") An "
"optional label\n"
"3. rescan (boolean, optional, default=true) Rescan "
"the wallet for transactions\n"
"4. p2sh (boolean, optional, default=false) Add "
"the P2SH version of the script as well\n"
"\nNote: This call can take minutes to complete if rescan is true, "
"during that time, other rpc calls\n"
"may report that the imported address exists but related "
"transactions are still missing, leading to temporarily "
"incorrect/bogus balances and unspent outputs until rescan "
"completes.\n"
"If you have the full public key, you should call importpubkey "
"instead of this.\n"
"\nNote: If you import a non-standard raw script in hex form, "
"outputs sending to it will be treated\n"
"as change, and not show up in many RPCs.\n"
"\nExamples:\n"
"\nImport a script with rescan\n" +
HelpExampleCli("importaddress", "\"myscript\"") +
"\nImport using a label without rescan\n" +
HelpExampleCli("importaddress", "\"myscript\" \"testing\" false") +
"\nAs a JSON-RPC call\n" +
HelpExampleRpc("importaddress",
"\"myscript\", \"testing\", false"));
}
std::string strLabel;
if (!request.params[1].isNull()) {
strLabel = request.params[1].get_str();
}
// Whether to perform rescan after import
bool fRescan = true;
if (!request.params[2].isNull()) {
fRescan = request.params[2].get_bool();
}
if (fRescan && fPruneMode) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Rescan is disabled in pruned mode");
}
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Wallet is currently rescanning. Abort existing rescan or wait.");
}
// Whether to import a p2sh version, too
bool fP2SH = false;
if (!request.params[3].isNull()) {
fP2SH = request.params[3].get_bool();
}
{
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str(),
config.GetChainParams());
if (IsValidDestination(dest)) {
if (fP2SH) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Cannot use the p2sh flag with an address - "
"use a script instead");
}
ImportAddress(pwallet, dest, strLabel);
} else if (IsHex(request.params[0].get_str())) {
std::vector<uint8_t> data(ParseHex(request.params[0].get_str()));
ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel,
fP2SH);
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address or script");
}
}
if (fRescan) {
pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
pwallet->ReacceptWalletTransactions();
}
return NullUniValue;
}
UniValue importprunedfunds(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"importprunedfunds\n"
"\nImports funds without rescan. Corresponding address or script "
"must previously be included in wallet. Aimed towards pruned "
"wallets. The end-user is responsible to import additional "
"transactions that subsequently spend the imported outputs or "
"rescan after the point in the blockchain the transaction is "
"included.\n"
"\nArguments:\n"
"1. \"rawtransaction\" (string, required) A raw transaction in hex "
"funding an already-existing address in wallet\n"
"2. \"txoutproof\" (string, required) The hex output from "
"gettxoutproof that contains the transaction\n");
}
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
uint256 txid = tx.GetId();
CWalletTx wtx(pwallet, MakeTransactionRef(std::move(tx)));
CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK,
PROTOCOL_VERSION);
CMerkleBlock merkleBlock;
ssMB >> merkleBlock;
// Search partial merkle tree in proof for our transaction and index in
// valid block
std::vector<uint256> vMatch;
std::vector<size_t> vIndex;
size_t txnIndex = 0;
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) ==
merkleBlock.header.hashMerkleRoot) {
LOCK(cs_main);
const CBlockIndex *pindex =
LookupBlockIndex(merkleBlock.header.GetHash());
if (!pindex || !chainActive.Contains(pindex)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Block not found in chain");
}
std::vector<uint256>::const_iterator it;
if ((it = std::find(vMatch.begin(), vMatch.end(), txid)) ==
vMatch.end()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Transaction given doesn't exist in proof");
}
txnIndex = vIndex[it - vMatch.begin()];
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Something wrong with merkleblock");
}
wtx.nIndex = txnIndex;
wtx.hashBlock = merkleBlock.header.GetHash();
LOCK2(cs_main, pwallet->cs_wallet);
if (pwallet->IsMine(*wtx.tx)) {
pwallet->AddToWallet(wtx, false);
return NullUniValue;
}
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"No addresses in wallet correspond to included transaction");
}
UniValue removeprunedfunds(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"removeprunedfunds \"txid\"\n"
"\nDeletes the specified transaction from the wallet. Meant for "
"use with pruned wallets and as a companion to importprunedfunds. "
"This will affect wallet balances.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The hex-encoded id of "
"the transaction you are deleting\n"
"\nExamples:\n" +
HelpExampleCli("removeprunedfunds", "\"a8d0c0184dde994a09ec054286f1"
"ce581bebf46446a512166eae762873"
"4ea0a5\"") +
"\nAs a JSON-RPC call\n" +
HelpExampleRpc("removprunedfunds", "\"a8d0c0184dde994a09ec054286f1c"
"e581bebf46446a512166eae7628734e"
"a0a5\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
TxId txid;
txid.SetHex(request.params[0].get_str());
std::vector<TxId> txIds;
txIds.push_back(txid);
std::vector<TxId> txIdsOut;
if (pwallet->ZapSelectTx(txIds, txIdsOut) != DBErrors::LOAD_OK) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Could not properly delete the transaction.");
}
if (txIdsOut.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Transaction does not exist in wallet.");
}
return NullUniValue;
}
UniValue importpubkey(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 4) {
throw std::runtime_error(
"importpubkey \"pubkey\" ( \"label\" rescan )\n"
"\nAdds a public key (in hex) that can be watched as if it were in "
"your wallet but cannot be used to spend. Requires a new wallet "
"backup.\n"
"\nArguments:\n"
"1. \"pubkey\" (string, required) The hex-encoded public "
"key\n"
"2. \"label\" (string, optional, default=\"\") An "
"optional label\n"
"3. rescan (boolean, optional, default=true) Rescan "
"the wallet for transactions\n"
"\nNote: This call can take minutes to complete if rescan is true, "
"during that time, other rpc calls\n"
"may report that the imported pubkey exists but related "
"transactions are still missing, leading to temporarily "
"incorrect/bogus balances and unspent outputs until rescan "
"completes.\n"
"\nExamples:\n"
"\nImport a public key with rescan\n" +
HelpExampleCli("importpubkey", "\"mypubkey\"") +
"\nImport using a label without rescan\n" +
HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
"\nAs a JSON-RPC call\n" +
HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false"));
}
std::string strLabel;
if (!request.params[1].isNull()) {
strLabel = request.params[1].get_str();
}
// Whether to perform rescan after import
bool fRescan = true;
if (!request.params[2].isNull()) {
fRescan = request.params[2].get_bool();
}
if (fRescan && fPruneMode) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Rescan is disabled in pruned mode");
}
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Wallet is currently rescanning. Abort existing rescan or wait.");
}
if (!IsHex(request.params[0].get_str())) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Pubkey must be a hex string");
}
std::vector<uint8_t> data(ParseHex(request.params[0].get_str()));
CPubKey pubKey(data.begin(), data.end());
if (!pubKey.IsFullyValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Pubkey is not a valid public key");
}
{
LOCK2(cs_main, pwallet->cs_wallet);
for (const auto &dest : GetAllDestinationsForKey(pubKey)) {
ImportAddress(pwallet, dest, strLabel);
}
ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
pwallet->LearnAllRelatedScripts(pubKey);
}
if (fRescan) {
pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
pwallet->ReacceptWalletTransactions();
}
return NullUniValue;
}
UniValue importwallet(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"importwallet \"filename\"\n"
"\nImports keys from a wallet dump file (see dumpwallet). Requires "
"a new wallet backup to include imported keys.\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The wallet file\n"
"\nExamples:\n"
"\nDump the wallet\n" +
HelpExampleCli("dumpwallet", "\"test\"") + "\nImport the wallet\n" +
HelpExampleCli("importwallet", "\"test\"") +
"\nImport using the json rpc call\n" +
HelpExampleRpc("importwallet", "\"test\""));
}
if (fPruneMode) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Importing wallets is disabled in pruned mode");
}
WalletRescanReserver reserver(pwallet);
if (!reserver.reserve()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Wallet is currently rescanning. Abort existing rescan or wait.");
}
int64_t nTimeBegin = 0;
bool fGood = true;
{
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
std::ifstream file;
file.open(request.params[0].get_str().c_str(),
std::ios::in | std::ios::ate);
if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Cannot open wallet dump file");
}
nTimeBegin = chainActive.Tip()->GetBlockTime();
int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
file.seekg(0, file.beg);
// show progress dialog in GUI
pwallet->ShowProgress(_("Importing..."), 0);
while (file.good()) {
pwallet->ShowProgress(
"", std::max(1, std::min(99, (int)(((double)file.tellg() /
(double)nFilesize) *
100))));
std::string line;
std::getline(file, line);
if (line.empty() || line[0] == '#') {
continue;
}
std::vector<std::string> vstr;
boost::split(vstr, line, boost::is_any_of(" "));
if (vstr.size() < 2) {
continue;
}
- CBitcoinSecret vchSecret;
- if (vchSecret.SetString(vstr[0])) {
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(vstr[0]);
+ if (key.IsValid()) {
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CKeyID keyid = pubkey.GetID();
if (pwallet->HaveKey(keyid)) {
LogPrintf("Skipping import of %s (key already present)\n",
EncodeDestination(keyid, config));
continue;
}
int64_t nTime = DecodeDumpTime(vstr[1]);
std::string strLabel;
bool fLabel = true;
for (unsigned int nStr = 2; nStr < vstr.size(); nStr++) {
if (boost::algorithm::starts_with(vstr[nStr], "#")) {
break;
}
if (vstr[nStr] == "change=1") {
fLabel = false;
}
if (vstr[nStr] == "reserve=1") {
fLabel = false;
}
if (boost::algorithm::starts_with(vstr[nStr], "label=")) {
strLabel = DecodeDumpString(vstr[nStr].substr(6));
fLabel = true;
}
}
LogPrintf("Importing %s...\n",
EncodeDestination(keyid, config));
if (!pwallet->AddKeyPubKey(key, pubkey)) {
fGood = false;
continue;
}
pwallet->mapKeyMetadata[keyid].nCreateTime = nTime;
if (fLabel) {
pwallet->SetAddressBook(keyid, strLabel, "receive");
}
nTimeBegin = std::min(nTimeBegin, nTime);
} else if (IsHex(vstr[0])) {
std::vector<uint8_t> vData(ParseHex(vstr[0]));
CScript script = CScript(vData.begin(), vData.end());
if (pwallet->HaveCScript(script)) {
LogPrintf(
"Skipping import of %s (script already present)\n",
vstr[0]);
continue;
}
if (!pwallet->AddCScript(script)) {
LogPrintf("Error importing script %s\n", vstr[0]);
fGood = false;
continue;
}
int64_t birth_time = DecodeDumpTime(vstr[1]);
if (birth_time > 0) {
pwallet->m_script_metadata[CScriptID(script)].nCreateTime =
birth_time;
nTimeBegin = std::min(nTimeBegin, birth_time);
}
}
}
file.close();
// hide progress dialog in GUI
pwallet->ShowProgress("", 100);
pwallet->UpdateTimeFirstKey(nTimeBegin);
}
pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */);
pwallet->MarkDirty();
if (!fGood) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding some keys/scripts to wallet");
}
return NullUniValue;
}
UniValue dumpprivkey(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"dumpprivkey \"address\"\n"
"\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address for the "
"private key\n"
"\nResult:\n"
"\"key\" (string) The private key\n"
"\nExamples:\n" +
HelpExampleCli("dumpprivkey", "\"myaddress\"") +
HelpExampleCli("importprivkey", "\"mykey\"") +
HelpExampleRpc("dumpprivkey", "\"myaddress\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
std::string strAddress = request.params[0].get_str();
CTxDestination dest =
DecodeDestination(strAddress, config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
auto keyid = GetKeyForDestination(*pwallet, dest);
if (keyid.IsNull()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key");
}
CKey vchSecret;
if (!pwallet->GetKey(keyid, vchSecret)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " +
strAddress + " is not known");
}
- return CBitcoinSecret(vchSecret).ToString();
+ return EncodeSecret(vchSecret);
}
UniValue dumpwallet(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"dumpwallet \"filename\"\n"
"\nDumps all wallet keys in a human-readable format to a "
"server-side file. This does not allow overwriting existing "
"files.\n"
"Imported scripts are included in the dumpsfile, but corresponding "
"addresses may not be added automatically by importwallet.\n"
"Note that if your wallet contains keys which are not derived from "
"your HD seed (e.g. imported keys), these are not covered by\n"
"only backing up the seed itself, and must be backed up too (e.g. "
"ensure you back up the whole dumpfile).\n"
"\nArguments:\n"
"1. \"filename\" (string, required) The filename with path "
"(either absolute or relative to bitcoind)\n"
"\nResult:\n"
"{ (json object)\n"
" \"filename\" : { (string) The filename with full "
"absolute path\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("dumpwallet", "\"test\"") +
HelpExampleRpc("dumpwallet", "\"test\""));
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
fs::path filepath = request.params[0].get_str();
filepath = fs::absolute(filepath);
/**
* Prevent arbitrary files from being overwritten. There have been reports
* that users have overwritten wallet files this way:
* https://github.com/bitcoin/bitcoin/issues/9934
* It may also avoid other security issues.
*/
if (fs::exists(filepath)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
filepath.string() + " already exists. If you are "
"sure this is what you want, "
"move it out of the way first");
}
std::ofstream file;
file.open(filepath.string().c_str());
if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Cannot open wallet dump file");
}
std::map<CTxDestination, int64_t> mapKeyBirth;
const std::map<CKeyID, int64_t> &mapKeyPool = pwallet->GetAllReserveKeys();
pwallet->GetKeyBirthTimes(mapKeyBirth);
std::set<CScriptID> scripts = pwallet->GetCScripts();
// TODO: include scripts in GetKeyBirthTimes() output instead of separate
// sort time/key pairs
std::vector<std::pair<int64_t, CKeyID>> vKeyBirth;
for (const auto &entry : mapKeyBirth) {
if (const CKeyID *keyID = boost::get<CKeyID>(&entry.first)) {
// set and test
vKeyBirth.push_back(std::make_pair(entry.second, *keyID));
}
}
mapKeyBirth.clear();
std::sort(vKeyBirth.begin(), vKeyBirth.end());
// produce output
file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD);
file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
file << strprintf("# * Best block at time of backup was %i (%s),\n",
chainActive.Height(),
chainActive.Tip()->GetBlockHash().ToString());
file << strprintf("# mined on %s\n",
FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()));
file << "\n";
// add the base58check encoded extended master if the wallet uses HD
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
if (!masterKeyID.IsNull()) {
CKey key;
if (pwallet->GetKey(masterKeyID, key)) {
CExtKey masterKey;
masterKey.SetMaster(key.begin(), key.size());
CBitcoinExtKey b58extkey;
b58extkey.SetKey(masterKey);
file << "# extended private masterkey: " << b58extkey.ToString()
<< "\n\n";
}
}
for (std::vector<std::pair<int64_t, CKeyID>>::const_iterator it =
vKeyBirth.begin();
it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second;
std::string strTime = FormatISO8601DateTime(it->first);
std::string strAddr = EncodeDestination(keyid, config);
CKey key;
if (pwallet->GetKey(keyid, key)) {
- file << strprintf("%s %s ", CBitcoinSecret(key).ToString(),
- strTime);
+ file << strprintf("%s %s ", EncodeSecret(key), strTime);
if (pwallet->mapAddressBook.count(keyid)) {
file << strprintf(
"label=%s",
EncodeDumpString(pwallet->mapAddressBook[keyid].name));
} else if (keyid == masterKeyID) {
file << "hdmaster=1";
} else if (mapKeyPool.count(keyid)) {
file << "reserve=1";
} else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") {
file << "inactivehdmaster=1";
} else {
file << "change=1";
}
file << strprintf(
" # addr=%s%s\n", strAddr,
(pwallet->mapKeyMetadata[keyid].hdKeypath.size() > 0
? " hdkeypath=" + pwallet->mapKeyMetadata[keyid].hdKeypath
: ""));
}
}
file << "\n";
for (const CScriptID &scriptid : scripts) {
CScript script;
std::string create_time = "0";
std::string address = EncodeDestination(scriptid, config);
// get birth times for scripts with metadata
auto it = pwallet->m_script_metadata.find(scriptid);
if (it != pwallet->m_script_metadata.end()) {
create_time = FormatISO8601DateTime(it->second.nCreateTime);
}
if (pwallet->GetCScript(scriptid, script)) {
file << strprintf("%s %s script=1",
HexStr(script.begin(), script.end()),
create_time);
file << strprintf(" # addr=%s\n", address);
}
}
file << "\n";
file << "# End of dump\n";
file.close();
UniValue reply(UniValue::VOBJ);
reply.pushKV("filename", filepath.string());
return reply;
}
static UniValue ProcessImport(CWallet *const pwallet, const UniValue &data,
const int64_t timestamp) {
try {
bool success = false;
// Required fields.
const UniValue &scriptPubKey = data["scriptPubKey"];
// Should have script or JSON with "address".
if (!(scriptPubKey.getType() == UniValue::VOBJ &&
scriptPubKey.exists("address")) &&
!(scriptPubKey.getType() == UniValue::VSTR)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scriptPubKey");
}
// Optional fields.
const std::string &strRedeemScript =
data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
const UniValue &pubKeys =
data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
const UniValue &keys =
data.exists("keys") ? data["keys"].get_array() : UniValue();
const bool internal =
data.exists("internal") ? data["internal"].get_bool() : false;
const bool watchOnly =
data.exists("watchonly") ? data["watchonly"].get_bool() : false;
const std::string &label =
data.exists("label") && !internal ? data["label"].get_str() : "";
bool isScript = scriptPubKey.getType() == UniValue::VSTR;
bool isP2SH = strRedeemScript.length() > 0;
const std::string &output = isScript
? scriptPubKey.get_str()
: scriptPubKey["address"].get_str();
// Parse the output.
CScript script;
CTxDestination dest;
if (!isScript) {
dest = DecodeDestination(output, pwallet->chainParams);
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid address");
}
script = GetScriptForDestination(dest);
} else {
if (!IsHex(output)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid scriptPubKey");
}
std::vector<uint8_t> vData(ParseHex(output));
script = CScript(vData.begin(), vData.end());
}
// Watchonly and private keys
if (watchOnly && keys.size()) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Incompatibility found between watchonly and keys");
}
// Internal + Label
if (internal && data.exists("label")) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Incompatibility found between internal and label");
}
// Not having Internal + Script
if (!internal && isScript) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Internal must be set for hex scriptPubKey");
}
// Keys / PubKeys size check.
if (!isP2SH &&
(keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey
throw JSONRPCError(RPC_INVALID_PARAMETER,
"More than private key given for one address");
}
// Invalid P2SH redeemScript
if (isP2SH && !IsHex(strRedeemScript)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid redeem script");
}
// Process. //
// P2SH
if (isP2SH) {
// Import redeem script.
std::vector<uint8_t> vData(ParseHex(strRedeemScript));
CScript redeemScript = CScript(vData.begin(), vData.end());
// Invalid P2SH address
if (!script.IsPayToScriptHash()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid P2SH address / script");
}
pwallet->MarkDirty();
if (!pwallet->AddWatchOnly(redeemScript, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding address to wallet");
}
if (!pwallet->HaveCScript(redeemScript) &&
!pwallet->AddCScript(redeemScript)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding p2sh redeemScript to wallet");
}
CTxDestination redeem_dest = CScriptID(redeemScript);
CScript redeemDestination = GetScriptForDestination(redeem_dest);
if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR,
"The wallet already contains the private "
"key for this address or script");
}
pwallet->MarkDirty();
if (!pwallet->AddWatchOnly(redeemDestination, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding address to wallet");
}
// add to address book or update label
if (IsValidDestination(dest)) {
pwallet->SetAddressBook(dest, label, "receive");
}
// Import private keys.
if (keys.size()) {
for (size_t i = 0; i < keys.size(); i++) {
const std::string &privkey = keys[i].get_str();
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(privkey);
-
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Invalid private key encoding");
- }
-
- CKey key = vchSecret.GetKey();
+ CKey key = DecodeSecret(privkey);
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Private key outside allowed range");
+ "Invalid private key encoding");
}
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
CKeyID vchAddress = pubkey.GetID();
pwallet->MarkDirty();
pwallet->SetAddressBook(vchAddress, label, "receive");
if (pwallet->HaveKey(vchAddress)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Already have this key");
}
pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
if (!pwallet->AddKeyPubKey(key, pubkey)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding key to wallet");
}
pwallet->UpdateTimeFirstKey(timestamp);
}
}
success = true;
} else {
// Import public keys.
if (pubKeys.size() && keys.size() == 0) {
const std::string &strPubKey = pubKeys[0].get_str();
if (!IsHex(strPubKey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Pubkey must be a hex string");
}
std::vector<uint8_t> vData(ParseHex(strPubKey));
CPubKey pubKey(vData.begin(), vData.end());
if (!pubKey.IsFullyValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Pubkey is not a valid public key");
}
CTxDestination pubkey_dest = pubKey.GetID();
// Consistency check.
if (!isScript && !(pubkey_dest == dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Consistency check failed");
}
// Consistency check.
if (isScript) {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
if (!(destination == pubkey_dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Consistency check failed");
}
}
}
CScript pubKeyScript = GetScriptForDestination(pubkey_dest);
if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already "
"contains the private "
"key for this address "
"or script");
}
pwallet->MarkDirty();
if (!pwallet->AddWatchOnly(pubKeyScript, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding address to wallet");
}
// add to address book or update label
if (IsValidDestination(pubkey_dest)) {
pwallet->SetAddressBook(pubkey_dest, label, "receive");
}
// TODO Is this necessary?
CScript scriptRawPubKey = GetScriptForRawPubKey(pubKey);
if (::IsMine(*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already "
"contains the private "
"key for this address "
"or script");
}
pwallet->MarkDirty();
if (!pwallet->AddWatchOnly(scriptRawPubKey, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding address to wallet");
}
success = true;
}
// Import private keys.
if (keys.size()) {
const std::string &strPrivkey = keys[0].get_str();
// Checks.
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(strPrivkey);
-
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Invalid private key encoding");
- }
+ CKey key = DecodeSecret(strPrivkey);
- CKey key = vchSecret.GetKey();
if (!key.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
- "Private key outside allowed range");
+ "Invalid private key encoding");
}
CPubKey pubKey = key.GetPubKey();
assert(key.VerifyPubKey(pubKey));
CTxDestination pubkey_dest = pubKey.GetID();
// Consistency check.
if (!isScript && !(pubkey_dest == dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Consistency check failed");
}
// Consistency check.
if (isScript) {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
if (!(destination == pubkey_dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Consistency check failed");
}
}
}
CKeyID vchAddress = pubKey.GetID();
pwallet->MarkDirty();
pwallet->SetAddressBook(vchAddress, label, "receive");
if (pwallet->HaveKey(vchAddress)) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already "
"contains the private "
"key for this address "
"or script");
}
pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp;
if (!pwallet->AddKeyPubKey(key, pubKey)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding key to wallet");
}
pwallet->UpdateTimeFirstKey(timestamp);
success = true;
}
// Import scriptPubKey only.
if (pubKeys.size() == 0 && keys.size() == 0) {
if (::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already "
"contains the private "
"key for this address "
"or script");
}
pwallet->MarkDirty();
if (!pwallet->AddWatchOnly(script, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR,
"Error adding address to wallet");
}
if (scriptPubKey.getType() == UniValue::VOBJ) {
// add to address book or update label
if (IsValidDestination(dest)) {
pwallet->SetAddressBook(dest, label, "receive");
}
}
success = true;
}
}
UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(success));
return result;
} catch (const UniValue &e) {
UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(false));
result.pushKV("error", e);
return result;
} catch (...) {
UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(false));
result.pushKV("error",
JSONRPCError(RPC_MISC_ERROR, "Missing required fields"));
return result;
}
}
static int64_t GetImportTimestamp(const UniValue &data, int64_t now) {
if (data.exists("timestamp")) {
const UniValue &timestamp = data["timestamp"];
if (timestamp.isNum()) {
return timestamp.get_int64();
} else if (timestamp.isStr() && timestamp.get_str() == "now") {
return now;
}
throw JSONRPCError(RPC_TYPE_ERROR,
strprintf("Expected number or \"now\" timestamp "
"value for key. got type %s",
uvTypeName(timestamp.type())));
}
throw JSONRPCError(RPC_TYPE_ERROR,
"Missing required timestamp field for key");
}
UniValue importmulti(const Config &config, const JSONRPCRequest &mainRequest) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(mainRequest);
if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) {
return NullUniValue;
}
// clang-format off
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2) {
throw std::runtime_error(
"importmulti \"requests\" ( \"options\" )\n\n"
"Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n"
"Arguments:\n"
"1. requests (array, required) Data to be imported\n"
" [ (array of json objects)\n"
" {\n"
" \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
" \"timestamp\": timestamp | \"now\" , (integer / string, required) Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n"
" or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n"
" key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n"
" \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n"
" 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n"
" creation time of all keys being imported by the importmulti call will be scanned.\n"
" \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
" \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
" \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
" \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
" \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
" \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n"
" }\n"
" ,...\n"
" ]\n"
"2. options (json, optional)\n"
" {\n"
" \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
" }\n"
"\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported keys, addresses or scripts exists but related transactions are still missing.\n"
"\nExamples:\n" +
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
"{ \"scriptPubKey\": { \"address\": \"<my 2nd address>\" }, \"label\": \"example 2\", \"timestamp\": 1455191480 }]'") +
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }]' '{ \"rescan\": false}'") +
"\nResponse is an array with the same size as the input that has the execution result :\n"
" [{ \"success\": true } , { \"success\": false, \"error\": { \"code\": -1, \"message\": \"Internal Server Error\"} }, ... ]\n");
}
// clang-format on
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
const UniValue &requests = mainRequest.params[0];
// Default options
bool fRescan = true;
if (!mainRequest.params[1].isNull()) {
const UniValue &options = mainRequest.params[1];
if (options.exists("rescan")) {
fRescan = options["rescan"].get_bool();
}
}
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"Wallet is currently rescanning. Abort existing rescan or wait.");
}
int64_t now = 0;
bool fRunScan = false;
int64_t nLowestTimestamp = 0;
UniValue response(UniValue::VARR);
{
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
// Verify all timestamps are present before importing any keys.
now = chainActive.Tip() ? chainActive.Tip()->GetMedianTimePast() : 0;
for (const UniValue &data : requests.getValues()) {
GetImportTimestamp(data, now);
}
const int64_t minimumTimestamp = 1;
if (fRescan && chainActive.Tip()) {
nLowestTimestamp = chainActive.Tip()->GetBlockTime();
} else {
fRescan = false;
}
for (const UniValue &data : requests.getValues()) {
const int64_t timestamp =
std::max(GetImportTimestamp(data, now), minimumTimestamp);
const UniValue result = ProcessImport(pwallet, data, timestamp);
response.push_back(result);
if (!fRescan) {
continue;
}
// If at least one request was successful then allow rescan.
if (result["success"].get_bool()) {
fRunScan = true;
}
// Get the lowest timestamp.
if (timestamp < nLowestTimestamp) {
nLowestTimestamp = timestamp;
}
}
}
if (fRescan && fRunScan && requests.size()) {
int64_t scannedTime = pwallet->RescanFromTime(
nLowestTimestamp, reserver, true /* update */);
pwallet->ReacceptWalletTransactions();
if (scannedTime > nLowestTimestamp) {
std::vector<UniValue> results = response.getValues();
response.clear();
response.setArray();
size_t i = 0;
for (const UniValue &request : requests.getValues()) {
// If key creation date is within the successfully scanned
// range, or if the import result already has an error set, let
// the result stand unmodified. Otherwise replace the result
// with an error message.
if (scannedTime <= GetImportTimestamp(request, now) ||
results.at(i).exists("error")) {
response.push_back(results.at(i));
} else {
UniValue result = UniValue(UniValue::VOBJ);
result.pushKV("success", UniValue(false));
result.pushKV(
"error",
JSONRPCError(
RPC_MISC_ERROR,
strprintf(
"Rescan failed for key with creation timestamp "
"%d. There was an error reading a block from "
"time %d, which is after or within %d seconds "
"of key creation, and could contain "
"transactions pertaining to the key. As a "
"result, transactions and coins using this key "
"may not appear in the wallet. This error "
"could be caused by pruning or data corruption "
"(see bitcoind log for details) and could be "
"dealt with by downloading and rescanning the "
"relevant blocks (see -reindex and -rescan "
"options).",
GetImportTimestamp(request, now),
scannedTime - TIMESTAMP_WINDOW - 1,
TIMESTAMP_WINDOW)));
response.push_back(std::move(result));
}
++i;
}
}
}
return response;
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
// category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
{ "wallet", "abortrescan", abortrescan, {} },
{ "wallet", "dumpprivkey", dumpprivkey, {"address"} },
{ "wallet", "dumpwallet", dumpwallet, {"filename"} },
{ "wallet", "importmulti", importmulti, {"requests","options"} },
{ "wallet", "importprivkey", importprivkey, {"privkey","label","rescan"} },
{ "wallet", "importwallet", importwallet, {"filename"} },
{ "wallet", "importaddress", importaddress, {"address","label","rescan","p2sh"} },
{ "wallet", "importprunedfunds", importprunedfunds, {"rawtransaction","txoutproof"} },
{ "wallet", "importpubkey", importpubkey, {"pubkey","label","rescan"} },
{ "wallet", "removeprunedfunds", removeprunedfunds, {"txid"} },
};
// clang-format on
void RegisterDumpRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jan 29, 17:04 (10 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5050326
Default Alt Text
(291 KB)

Event Timeline