mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
Merge pull request #4877 from Munkybooty/backports-0.20-pr5
Backports 0.20 pr5
This commit is contained in:
commit
ba7d5dc6c2
@ -6,6 +6,12 @@
|
||||
|
||||
export LC_ALL=C.UTF-8
|
||||
|
||||
# The root dir.
|
||||
# The ci system copies this folder.
|
||||
# This is where the build is done (depends and dist).
|
||||
BASE_ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd )
|
||||
export BASE_ROOT_DIR
|
||||
|
||||
echo "Setting specific values in env"
|
||||
if [ -n "${FILE_ENV}" ]; then
|
||||
set -o errexit;
|
||||
@ -17,9 +23,6 @@ export BUILD_TARGET=${BUILD_TARGET:-linux64}
|
||||
export PULL_REQUEST=${PULL_REQUEST:-false}
|
||||
export JOB_NUMBER=${JOB_NUMBER:-1}
|
||||
|
||||
BASE_ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd )
|
||||
export BASE_ROOT_DIR
|
||||
|
||||
echo "Fallback to default values in env (if not yet set)"
|
||||
# The number of parallel jobs to pass down to make and test_runner.py
|
||||
MAKEJOBS="-j$(nproc)"
|
||||
|
@ -14,3 +14,7 @@ export GOAL="install"
|
||||
export BITCOIN_CONFIG="--enable-zmq --enable-reduce-exports --enable-crash-hooks --with-sanitizers=thread"
|
||||
export CPPFLAGS="-DDEBUG_LOCKORDER -DENABLE_DASH_DEBUG -DARENA_DEBUG"
|
||||
export PYZMQ=true
|
||||
|
||||
# xenial comes with old clang versions that can not parse the sanitizer suppressions files
|
||||
# Remove unparseable lines as a hacky workaround
|
||||
sed -i '/^implicit-/d' "${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan"
|
||||
|
@ -43,7 +43,7 @@ mkdir -p "${CCACHE_DIR}"
|
||||
export ASAN_OPTIONS="detect_stack_use_after_return=1"
|
||||
export LSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/lsan"
|
||||
export TSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/tsan"
|
||||
export UBSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1"
|
||||
export UBSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1"
|
||||
env | grep -E '^(CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS)' | tee /tmp/env
|
||||
if [[ $HOST = *-mingw32 ]]; then
|
||||
DOCKER_ADMIN="--cap-add SYS_ADMIN"
|
||||
|
@ -19,8 +19,9 @@ MAX_SEEDS_PER_ASN=4
|
||||
|
||||
# These are hosts that have been observed to be behaving strangely (e.g.
|
||||
# aggressively connecting to every node).
|
||||
SUSPICIOUS_HOSTS = {
|
||||
}
|
||||
with open("suspicious_hosts.txt", mode="r", encoding="utf-8") as f:
|
||||
SUSPICIOUS_HOSTS = {s.strip() for s in f if s.strip()}
|
||||
|
||||
|
||||
PATTERN_IPV4 = re.compile(r"^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})):(\d+)$")
|
||||
PATTERN_IPV6 = re.compile(r"^\[([0-9a-z:]+)\]:(\d+)$")
|
||||
|
0
contrib/seeds/suspicious_hosts.txt
Normal file
0
contrib/seeds/suspicious_hosts.txt
Normal file
@ -7,8 +7,8 @@ A new wallet flag `avoid_reuse` has been added (default off). When enabled,
|
||||
a wallet will distinguish between used and unused addresses, and default to not
|
||||
use the former in coin selection.
|
||||
|
||||
(Note: rescanning the blockchain is required, to correctly mark previously
|
||||
used destinations.)
|
||||
Rescanning the blockchain is required, to correctly mark previously
|
||||
used destinations.
|
||||
|
||||
Together with "avoid partial spends" (present as of Bitcoin v0.17), this
|
||||
addresses a serious privacy issue where a malicious user can track spends by
|
||||
@ -30,10 +30,12 @@ These include:
|
||||
|
||||
- createwallet
|
||||
- getbalance
|
||||
- getbalances
|
||||
- sendtoaddress
|
||||
|
||||
In addition, `sendtoaddress` has been changed to enable `-avoidpartialspends` when
|
||||
`avoid_reuse` is enabled.
|
||||
In addition, `sendtoaddress` has been changed to avoid partial spends when `avoid_reuse`
|
||||
is enabled (if not already enabled via the `-avoidpartialspends` command line flag),
|
||||
as it would otherwise risk using up the "wrong" UTXO for an address reuse case.
|
||||
|
||||
The listunspent RPC has also been updated to now include a "reused" bool, for nodes
|
||||
with "avoid_reuse" enabled.
|
||||
|
@ -750,12 +750,9 @@ if TARGET_WINDOWS
|
||||
dashd_SOURCES += dashd-res.rc
|
||||
endif
|
||||
|
||||
# Libraries below may be listed more than once to resolve circular dependencies (see
|
||||
# https://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking#circular-dependency)
|
||||
dashd_LDADD = \
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBBITCOIN_WALLET) \
|
||||
$(LIBBITCOIN_SERVER) \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
|
@ -80,6 +80,8 @@ static const unsigned int MAX_GETDATA_SZ = 1000;
|
||||
static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
|
||||
/** Minimum time between orphan transactions expire time checks in seconds */
|
||||
static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
|
||||
/** How long to cache transactions in mapRelay for normal relay */
|
||||
static constexpr std::chrono::seconds RELAY_TX_CACHE_TIME{15 * 60};
|
||||
/** Headers download timeout expressed in microseconds
|
||||
* Timeout = base + per_header * (expected number of headers) */
|
||||
static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes
|
||||
@ -1179,7 +1181,7 @@ static bool TxRelayMayResultInDisconnect(const CValidationState& state)
|
||||
/**
|
||||
* Potentially mark a node discouraged based on the contents of a CValidationState object
|
||||
*
|
||||
* @param[in] via_compact_block: this bool is passed in because net_processing should
|
||||
* @param[in] via_compact_block this bool is passed in because net_processing should
|
||||
* punish peers differently depending on whether the data was provided in a compact
|
||||
* block message or not. If the compact block had a valid header, but contained invalid
|
||||
* txs, the peer should not be punished. See BIP 152.
|
||||
@ -1814,6 +1816,10 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
|
||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
|
||||
|
||||
{
|
||||
// mempool entries added before this time have likely expired from mapRelay
|
||||
const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME;
|
||||
const std::chrono::seconds mempool_req = pfrom->m_tx_relay->m_last_mempool_req.load();
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
while (it != pfrom->vRecvGetData.end() && it->IsKnownType()) {
|
||||
@ -1851,11 +1857,15 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
|
||||
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::TX, *mi->second));
|
||||
}
|
||||
push = true;
|
||||
} else if (pfrom->m_tx_relay->m_last_mempool_req.load().count()) {
|
||||
} else {
|
||||
auto txinfo = mempool.info(inv.hash);
|
||||
// To protect privacy, do not answer getdata using the mempool when
|
||||
// that TX couldn't have been INVed in reply to a MEMPOOL request.
|
||||
if (txinfo.tx && txinfo.m_time <= pfrom->m_tx_relay->m_last_mempool_req.load()) {
|
||||
// that TX couldn't have been INVed in reply to a MEMPOOL request,
|
||||
// or when it's too recent to have expired from mapRelay.
|
||||
if (txinfo.tx && (
|
||||
(mempool_req.count() && txinfo.m_time <= mempool_req)
|
||||
|| (txinfo.m_time <= longlived_mempool_time)))
|
||||
{
|
||||
if (dstx) {
|
||||
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::DSTX, dstx));
|
||||
} else {
|
||||
@ -4897,7 +4907,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
|
||||
auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
|
||||
if (ret.second) {
|
||||
vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first));
|
||||
vRelayExpiration.push_back(std::make_pair(nNow + std::chrono::microseconds{RELAY_TX_CACHE_TIME}.count(), ret.first));
|
||||
}
|
||||
}
|
||||
int nInvType = CCoinJoin::GetDSTX(hash) ? MSG_DSTX : MSG_TX;
|
||||
|
@ -782,7 +782,7 @@ bool IsProxy(const CNetAddr &addr) {
|
||||
* @param hSocket The socket on which to connect to the SOCKS5 proxy.
|
||||
* @param nTimeout Wait this many milliseconds for the connection to the SOCKS5
|
||||
* proxy to be established.
|
||||
* @param outProxyConnectionFailed[out] Whether or not the connection to the
|
||||
* @param[out] outProxyConnectionFailed Whether or not the connection to the
|
||||
* SOCKS5 proxy failed.
|
||||
*
|
||||
* @returns Whether or not the operation succeeded.
|
||||
|
@ -22,10 +22,10 @@ struct NodeContext;
|
||||
*
|
||||
* @param[in] node reference to node context
|
||||
* @param[in] tx the transaction to broadcast
|
||||
* @param[out] &err_string reference to std::string to fill with error string if available
|
||||
* @param[out] err_string reference to std::string to fill with error string if available
|
||||
* @param[in] max_tx_fee reject txs with fees higher than this (if 0, accept any fee)
|
||||
* @param[in] relay flag if both mempool insertion and p2p relay are requested
|
||||
* @param[in] wait_callback, wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
|
||||
* @param[in] wait_callback wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
|
||||
* return error
|
||||
*/
|
||||
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, const CAmount& highfee, bool relay, bool wait_callback, bool bypass_limits = false);
|
||||
|
@ -533,7 +533,7 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio
|
||||
/**
|
||||
* Finalizes a PSBT if possible, combining partial signatures.
|
||||
*
|
||||
* @param[in,out] &psbtx reference to PartiallySignedTransaction to finalize
|
||||
* @param[in,out] psbtx PartiallySignedTransaction to finalize
|
||||
* return True if the PSBT is now complete, false otherwise
|
||||
*/
|
||||
bool FinalizePSBT(PartiallySignedTransaction& psbtx);
|
||||
@ -541,7 +541,7 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx);
|
||||
/**
|
||||
* Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized.
|
||||
*
|
||||
* @param[in] &psbtx reference to PartiallySignedTransaction
|
||||
* @param[in] psbtx PartiallySignedTransaction
|
||||
* @param[out] result CMutableTransaction representing the complete transaction, if successful
|
||||
* @return True if we successfully extracted the transaction, false otherwise
|
||||
*/
|
||||
@ -550,7 +550,7 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
|
||||
/**
|
||||
* Combines PSBTs with the same underlying transaction, resulting in a single PSBT with all partial signatures from each input.
|
||||
*
|
||||
* @param[out] &out the combined PSBT, if successful
|
||||
* @param[out] out the combined PSBT, if successful
|
||||
* @param[in] psbtxs the PSBTs to combine
|
||||
* @return error (OK if we successfully combined the transactions, other error if they were not compatible)
|
||||
*/
|
||||
|
@ -19,6 +19,7 @@ class SigningProvider;
|
||||
* Sign a transaction with the given keystore and previous transactions
|
||||
*
|
||||
* @param mtx The transaction to-be-signed
|
||||
* @param prevTxsUnival Array of previous txns outputs that tx depends on but may not yet be in the block chain
|
||||
* @param keystore Temporary keystore containing signing keys
|
||||
* @param coins Map of unspent outputs
|
||||
* @param hashType The signature hash type
|
||||
|
@ -90,20 +90,20 @@ struct Descriptor {
|
||||
|
||||
/** Expand a descriptor at a specified position.
|
||||
*
|
||||
* @param[in] pos: The position at which to expand the descriptor. If IsRange() is false, this is ignored.
|
||||
* @param[in] provider: The provider to query for private keys in case of hardened derivation.
|
||||
* @param[out] output_scripts: The expanded scriptPubKeys.
|
||||
* @param[out] out: Scripts and public keys necessary for solving the expanded scriptPubKeys (may be equal to `provider`).
|
||||
* @param[out] write_cache: Cache data necessary to evaluate the descriptor at this point without access to private keys.
|
||||
* @param[in] pos The position at which to expand the descriptor. If IsRange() is false, this is ignored.
|
||||
* @param[in] provider The provider to query for private keys in case of hardened derivation.
|
||||
* @param[out] output_scripts The expanded scriptPubKeys.
|
||||
* @param[out] out Scripts and public keys necessary for solving the expanded scriptPubKeys (may be equal to `provider`).
|
||||
* @param[out] write_cache Cache data necessary to evaluate the descriptor at this point without access to private keys.
|
||||
*/
|
||||
virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, DescriptorCache* write_cache = nullptr) const = 0;
|
||||
|
||||
/** Expand a descriptor at a specified position using cached expansion data.
|
||||
*
|
||||
* @param[in] pos: The position at which to expand the descriptor. If IsRange() is false, this is ignored.
|
||||
* @param[in] read_cache: Cached expansion data.
|
||||
* @param[out] output_scripts: The expanded scriptPubKeys.
|
||||
* @param[out] out: Scripts and public keys necessary for solving the expanded scriptPubKeys (may be equal to `provider`).
|
||||
* @param[in] pos The position at which to expand the descriptor. If IsRange() is false, this is ignored.
|
||||
* @param[in] read_cache Cached expansion data.
|
||||
* @param[out] output_scripts The expanded scriptPubKeys.
|
||||
* @param[out] out Scripts and public keys necessary for solving the expanded scriptPubKeys (may be equal to `provider`).
|
||||
*/
|
||||
virtual bool ExpandFromCache(int pos, const DescriptorCache& read_cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
|
||||
|
||||
|
@ -190,14 +190,15 @@ static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &sa
|
||||
BOOST_CHECK(HexStr(out) == okm_check_hex);
|
||||
}
|
||||
|
||||
static std::string LongTestString() {
|
||||
static std::string LongTestString()
|
||||
{
|
||||
std::string ret;
|
||||
for (int i=0; i<200000; i++) {
|
||||
ret += (unsigned char)(i);
|
||||
ret += (unsigned char)(i >> 4);
|
||||
ret += (unsigned char)(i >> 8);
|
||||
ret += (unsigned char)(i >> 12);
|
||||
ret += (unsigned char)(i >> 16);
|
||||
for (int i = 0; i < 200000; i++) {
|
||||
ret += (char)(i);
|
||||
ret += (char)(i >> 4);
|
||||
ret += (char)(i >> 8);
|
||||
ret += (char)(i >> 12);
|
||||
ret += (char)(i >> 16);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -451,6 +451,30 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
|
||||
reason.clear();
|
||||
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
|
||||
BOOST_CHECK_EQUAL(reason, "multi-op-return");
|
||||
|
||||
// Check large scriptSig (non-standard if size is >1650 bytes)
|
||||
t.vout.resize(1);
|
||||
t.vout[0].nValue = MAX_MONEY;
|
||||
t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
|
||||
// OP_PUSHDATA2 with len (3 bytes) + data (1647 bytes) = 1650 bytes
|
||||
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1647, 0); // 1650
|
||||
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
|
||||
|
||||
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1648, 0); // 1651
|
||||
reason.clear();
|
||||
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
|
||||
BOOST_CHECK_EQUAL(reason, "scriptsig-size");
|
||||
|
||||
// Check bare multisig (standard if policy flag fIsBareMultisigStd is set)
|
||||
fIsBareMultisigStd = true;
|
||||
t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()}); // simple 1-of-1
|
||||
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0);
|
||||
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
|
||||
|
||||
fIsBareMultisigStd = false;
|
||||
reason.clear();
|
||||
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
|
||||
BOOST_CHECK_EQUAL(reason, "bare-multisig");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -15,8 +15,8 @@
|
||||
* finalize.) Sets `error` and returns false if something goes wrong.
|
||||
*
|
||||
* @param[in] pwallet pointer to a wallet
|
||||
* @param[in] &psbtx reference to PartiallySignedTransaction to fill in
|
||||
* @param[out] &complete indicates whether the PSBT is now complete
|
||||
* @param[in] psbtx PartiallySignedTransaction to fill in
|
||||
* @param[out] complete indicates whether the PSBT is now complete
|
||||
* @param[in] sighash_type the sighash type to use when signing (if PSBT does not specify)
|
||||
* @param[in] sign whether to sign or not
|
||||
* @param[in] bip32derivs whether to fill in bip32 derivation information if available
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Copyright (c) 2009-2019 The Bitcoin Core developers
|
||||
// Copyright (c) 2014-2022 The Dash Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
@ -20,10 +20,10 @@
|
||||
#include <util/bip32.h>
|
||||
#include <util/fees.h>
|
||||
#include <util/message.h> // For MessageSign()
|
||||
#include <util/system.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/ref.h>
|
||||
#include <util/string.h>
|
||||
#include <util/system.h>
|
||||
#include <util/translation.h>
|
||||
#include <util/url.h>
|
||||
#include <util/vector.h>
|
||||
@ -123,7 +123,7 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
|
||||
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
|
||||
}
|
||||
|
||||
void EnsureWalletIsUnlocked(CWallet * const pwallet)
|
||||
void EnsureWalletIsUnlocked(const CWallet* pwallet)
|
||||
{
|
||||
if (pwallet->IsLocked()) {
|
||||
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
|
||||
@ -366,7 +366,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
|
||||
" \"UNSET\"\n"
|
||||
" \"ECONOMICAL\"\n"
|
||||
" \"CONSERVATIVE\""},
|
||||
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) ? "true" : "unavailable", "Avoid spending from dirty addresses; addresses are considered\n"
|
||||
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
|
||||
" dirty if they have previously been used in a transaction."},
|
||||
},
|
||||
RPCResult{
|
||||
@ -753,7 +753,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
|
||||
{"minconf", RPCArg::Type::NUM, /* default */ "0", "Only include transactions confirmed at least this many times."},
|
||||
{"addlocked", RPCArg::Type::BOOL, /* default */ "false", "Whether to include transactions locked via InstantSend in the wallet's balance."},
|
||||
{"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Also include balance in watch-only addresses (see 'importaddress')"},
|
||||
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) ? "true" : "unavailable", "Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
|
||||
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
|
||||
},
|
||||
RPCResult{
|
||||
RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
|
||||
@ -801,7 +801,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
|
||||
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
|
||||
{
|
||||
RPCHelpMan{"getunconfirmedbalance",
|
||||
"Returns the server's total unconfirmed balance\n",
|
||||
"DEPRECATED\nIdentical to getbalances().mine.untrusted_pending\n",
|
||||
{},
|
||||
RPCResult{RPCResult::Type::NUM, "", "The balance"},
|
||||
RPCExamples{""},
|
||||
@ -2381,6 +2381,75 @@ static UniValue setcoinjoinamount(const JSONRPCRequest& request)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
static UniValue getbalances(const JSONRPCRequest& request)
|
||||
{
|
||||
RPCHelpMan{"getbalances",
|
||||
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
|
||||
{},
|
||||
RPCResult{
|
||||
RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
|
||||
{
|
||||
{RPCResult::Type::STR_AMOUNT, "trusted", " trusted balance (outputs created by the wallet or confirmed outputs)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", " untrusted pending balance (outputs created by others that are in the mempool)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "immature", " balance from immature coinbase outputs"},
|
||||
{RPCResult::Type::STR_AMOUNT, "used", "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "coinjoin", " CoinJoin balance (outputs with enough rounds created by the wallet via mixing)"},
|
||||
}},
|
||||
{RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)",
|
||||
{
|
||||
{RPCResult::Type::STR_AMOUNT, "trusted", " trusted balance (outputs created by the wallet or confirmed outputs)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "untrusted_pending", " untrusted pending balance (outputs created by others that are in the mempool)"},
|
||||
{RPCResult::Type::STR_AMOUNT, "immature", " balance from immature coinbase outputs"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
RPCExamples{
|
||||
HelpExampleCli("getbalances", "") +
|
||||
HelpExampleRpc("getbalances", "")
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
std::shared_ptr<CWallet> const rpc_wallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!rpc_wallet) return NullUniValue;
|
||||
CWallet& wallet = *rpc_wallet;
|
||||
|
||||
// Make sure the results are valid at least up to the most recent block
|
||||
// the user could have gotten from another RPC command prior to now
|
||||
wallet.BlockUntilSyncedToCurrentChain();
|
||||
|
||||
LOCK(wallet.cs_wallet);
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
|
||||
const auto bal = wallet.GetBalance();
|
||||
UniValue balances{UniValue::VOBJ};
|
||||
{
|
||||
UniValue balances_mine{UniValue::VOBJ};
|
||||
balances_mine.pushKV("trusted", ValueFromAmount(bal.m_mine_trusted));
|
||||
balances_mine.pushKV("untrusted_pending", ValueFromAmount(bal.m_mine_untrusted_pending));
|
||||
balances_mine.pushKV("immature", ValueFromAmount(bal.m_mine_immature));
|
||||
if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
|
||||
// If the AVOID_REUSE flag is set, bal has been set to just the un-reused address balance. Get
|
||||
// the total balance, and then subtract bal to get the reused address balance.
|
||||
const auto full_bal = wallet.GetBalance(0, false);
|
||||
balances_mine.pushKV("used", ValueFromAmount(full_bal.m_mine_trusted + full_bal.m_mine_untrusted_pending - bal.m_mine_trusted - bal.m_mine_untrusted_pending));
|
||||
}
|
||||
balances_mine.pushKV("coinjoin", ValueFromAmount(bal.m_anonymized));
|
||||
balances.pushKV("mine", balances_mine);
|
||||
}
|
||||
auto spk_man = wallet.GetLegacyScriptPubKeyMan();
|
||||
if (spk_man && spk_man->HaveWatchOnly()) {
|
||||
UniValue balances_watchonly{UniValue::VOBJ};
|
||||
balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted));
|
||||
balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending));
|
||||
balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature));
|
||||
balances.pushKV("watchonly", balances_watchonly);
|
||||
}
|
||||
return balances;
|
||||
}
|
||||
|
||||
static UniValue getwalletinfo(const JSONRPCRequest& request)
|
||||
{
|
||||
RPCHelpMan{"getwalletinfo",
|
||||
@ -2391,10 +2460,10 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
||||
{
|
||||
{RPCResult::Type::STR, "walletname", "the wallet name"},
|
||||
{RPCResult::Type::NUM, "walletversion", "the wallet version"},
|
||||
{RPCResult::Type::NUM, "balance", "the total confirmed balance of the wallet in " + CURRENCY_UNIT},
|
||||
{RPCResult::Type::NUM, "coinjoin_balance", "the CoinJoin balance in " + CURRENCY_UNIT},
|
||||
{RPCResult::Type::NUM, "unconfirmed_balance", "the total unconfirmed balance of the wallet in " + CURRENCY_UNIT},
|
||||
{RPCResult::Type::NUM, "immature_balance", "the total immature balance of the wallet in " + CURRENCY_UNIT},
|
||||
{RPCResult::Type::NUM, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
|
||||
{RPCResult::Type::NUM, "coinjoin_balance", "DEPRECATED. Identical to getbalances().mine.coinjoin"},
|
||||
{RPCResult::Type::NUM, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
|
||||
{RPCResult::Type::NUM, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
|
||||
{RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
|
||||
{RPCResult::Type::NUM_TIME, "timefirstkey", "the " + UNIX_EPOCH_TIME + " of the oldest known key in the wallet"},
|
||||
{RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool"},
|
||||
@ -2903,7 +2972,6 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
||||
if (!wallet) return NullUniValue;
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
|
||||
RPCHelpMan{"listunspent",
|
||||
"\nReturns array of unspent transaction outputs\n"
|
||||
"with between minconf and maxconf (inclusive) confirmations.\n"
|
||||
@ -2946,7 +3014,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
||||
{RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
|
||||
{RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
|
||||
{RPCResult::Type::STR, "desc", "(only when solvable) A descriptor for spending this output"},
|
||||
{RPCResult::Type::BOOL, "reused", /* optional*/ true, "Whether this output is reused/dirty (sent to an address that was previously spent from)"},
|
||||
{RPCResult::Type::BOOL, "reused", /* optional*/ true, "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
|
||||
{RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions"
|
||||
" from outside keys and unconfirmed replacement transactions are considered unsafe\n"
|
||||
"and are not eligible for spending by fundrawtransaction and sendtoaddress."},
|
||||
@ -3059,6 +3127,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
const bool avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
|
||||
|
||||
for (const COutput& out : vecOutputs) {
|
||||
CTxDestination address;
|
||||
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
|
||||
@ -3985,6 +4055,7 @@ static const CRPCCommand commands[] =
|
||||
{ "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf","addlocked"} },
|
||||
{ "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
|
||||
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
|
||||
{ "wallet", "getbalances", &getbalances, {} },
|
||||
{ "wallet", "getwalletinfo", &getwalletinfo, {} },
|
||||
{ "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} },
|
||||
{ "wallet", "importelectrumwallet", &importelectrumwallet, {"filename", "index"} },
|
||||
|
@ -35,7 +35,7 @@ Span<const CRPCCommand> GetWalletRPCCommands();
|
||||
*/
|
||||
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
|
||||
|
||||
void EnsureWalletIsUnlocked(CWallet *);
|
||||
void EnsureWalletIsUnlocked(const CWallet*);
|
||||
WalletContext& EnsureWalletContext(const util::Ref& context);
|
||||
|
||||
UniValue getaddressinfo(const JSONRPCRequest& request);
|
||||
|
@ -73,6 +73,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
|
||||
if (fIsFromMe)
|
||||
{
|
||||
wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
|
||||
wtx->m_is_cache_empty = false;
|
||||
}
|
||||
COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
|
||||
vCoins.push_back(output);
|
||||
|
@ -777,7 +777,7 @@ void CWallet::MarkDirty()
|
||||
fAnonymizableTallyCachedNonDenom = false;
|
||||
}
|
||||
|
||||
void CWallet::SetUsedDestinationState(const uint256& hash, unsigned int n, bool used)
|
||||
void CWallet::SetUsedDestinationState(const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
|
||||
{
|
||||
const CWalletTx* srctx = GetWalletTx(hash);
|
||||
if (!srctx) return;
|
||||
@ -787,7 +787,9 @@ void CWallet::SetUsedDestinationState(const uint256& hash, unsigned int n, bool
|
||||
if (IsMine(dst)) {
|
||||
LOCK(cs_wallet);
|
||||
if (used && !GetDestData(dst, "used", nullptr)) {
|
||||
AddDestData(dst, "used", "p"); // p for "present", opposite of absent (null)
|
||||
if (AddDestData(dst, "used", "p")) { // p for "present", opposite of absent (null)
|
||||
tx_destinations.insert(dst);
|
||||
}
|
||||
} else if (!used && GetDestData(dst, "used", nullptr)) {
|
||||
EraseDestData(dst, "used");
|
||||
}
|
||||
@ -818,10 +820,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
|
||||
|
||||
if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
|
||||
// Mark used destinations
|
||||
std::set<CTxDestination> tx_destinations;
|
||||
|
||||
for (const CTxIn& txin : wtxIn.tx->vin) {
|
||||
const COutPoint& op = txin.prevout;
|
||||
SetUsedDestinationState(op.hash, op.n, true);
|
||||
SetUsedDestinationState(op.hash, op.n, true, tx_destinations);
|
||||
}
|
||||
|
||||
MarkDestinationsDirty(tx_destinations);
|
||||
}
|
||||
|
||||
// Inserts only if not already there, returns tx inserted or tx found
|
||||
@ -2029,6 +2035,7 @@ CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter
|
||||
auto& amount = m_amounts[type];
|
||||
if (recalculate || !amount.m_cached[filter]) {
|
||||
amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter));
|
||||
m_is_cache_empty = false;
|
||||
}
|
||||
return amount.m_value[filter];
|
||||
}
|
||||
@ -2106,6 +2113,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
|
||||
|
||||
if (allow_cache) {
|
||||
m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit);
|
||||
m_is_cache_empty = false;
|
||||
}
|
||||
|
||||
return nCredit;
|
||||
@ -3785,6 +3793,20 @@ bool CWallet::GetNewChangeDestination(CTxDestination& dest, std::string& error)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations) {
|
||||
for (auto& entry : mapWallet) {
|
||||
CWalletTx& wtx = entry.second;
|
||||
if (wtx.m_is_cache_empty) continue;
|
||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
|
||||
CTxDestination dst;
|
||||
if (ExtractDestination(wtx.tx->vout[i].scriptPubKey, dst) && destinations.count(dst)) {
|
||||
wtx.MarkDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
|
||||
{
|
||||
std::map<CTxDestination, CAmount> balances;
|
||||
|
@ -327,6 +327,13 @@ public:
|
||||
enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, ANON_CREDIT, DENOM_UCREDIT, DENOM_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
|
||||
CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
|
||||
mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
|
||||
/**
|
||||
* This flag is true if all m_amounts caches are empty. This is particularly
|
||||
* useful in places where MarkDirty is conditionally called and the
|
||||
* condition can be expensive and thus can be skipped if the flag is true.
|
||||
* See MarkDestinationsDirty.
|
||||
*/
|
||||
mutable bool m_is_cache_empty{true};
|
||||
mutable bool fChangeCached;
|
||||
mutable bool fInMempool;
|
||||
mutable CAmount nChangeCached;
|
||||
@ -456,6 +463,7 @@ public:
|
||||
m_amounts[IMMATURE_CREDIT].Reset();
|
||||
m_amounts[AVAILABLE_CREDIT].Reset();
|
||||
fChangeCached = false;
|
||||
m_is_cache_empty = true;
|
||||
}
|
||||
|
||||
void BindWallet(CWallet *pwalletIn)
|
||||
@ -901,7 +909,7 @@ public:
|
||||
// Whether this or any UTXO with the same CTxDestination has been spent.
|
||||
bool IsUsedDestination(const CTxDestination& dst) const;
|
||||
bool IsUsedDestination(const uint256& hash, unsigned int n) const;
|
||||
void SetUsedDestinationState(const uint256& hash, unsigned int n, bool used);
|
||||
void SetUsedDestinationState(const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations);
|
||||
|
||||
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
|
||||
|
||||
@ -1020,9 +1028,9 @@ public:
|
||||
* Should be called after CreateTransaction unless you want to abort
|
||||
* broadcasting the transaction.
|
||||
*
|
||||
* @param tx[in] The transaction to be broadcast.
|
||||
* @param mapValue[in] key-values to be set on the transaction.
|
||||
* @param orderForm[in] BIP 70 / BIP 21 order form details to be set on the transaction.
|
||||
* @param[in] tx The transaction to be broadcast.
|
||||
* @param[in] mapValue key-values to be set on the transaction.
|
||||
* @param[in] orderForm BIP 70 / BIP 21 order form details to be set on the transaction.
|
||||
*/
|
||||
void CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm);
|
||||
|
||||
@ -1066,6 +1074,12 @@ public:
|
||||
|
||||
std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
|
||||
|
||||
/**
|
||||
* Marks all outputs in each one of the destinations dirty, so their cache is
|
||||
* reset and does not return outdated information.
|
||||
*/
|
||||
void MarkDestinationsDirty(const std::set<CTxDestination>& destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
bool GetNewDestination(const std::string label, CTxDestination& dest, std::string& error);
|
||||
bool GetNewChangeDestination(CTxDestination& dest, std::string& error);
|
||||
|
||||
|
@ -92,7 +92,7 @@ public:
|
||||
int nVersion;
|
||||
int64_t nCreateTime; // 0 means unknown
|
||||
KeyOriginInfo key_origin; // Key origin info with path and fingerprint
|
||||
bool has_key_origin = false; //< Whether the key_origin is useful
|
||||
bool has_key_origin = false; //!< Whether the key_origin is useful
|
||||
|
||||
CKeyMetadata()
|
||||
{
|
||||
|
@ -8,6 +8,7 @@ from io import BytesIO
|
||||
import math
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.messages import (
|
||||
BIP125_SEQUENCE_NUMBER,
|
||||
COIN,
|
||||
@ -21,6 +22,9 @@ from test_framework.script import (
|
||||
hash160,
|
||||
CScript,
|
||||
OP_0,
|
||||
OP_2,
|
||||
OP_3,
|
||||
OP_CHECKMULTISIG,
|
||||
OP_EQUAL,
|
||||
OP_HASH160,
|
||||
OP_RETURN,
|
||||
@ -36,7 +40,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
self.extra_args = [[
|
||||
'-txindex',
|
||||
'-txindex','-permitbaremultisig=0',
|
||||
]] * self.num_nodes
|
||||
self.supports_cli = False
|
||||
|
||||
@ -263,6 +267,15 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||
rawtxs=[tx.serialize().hex()],
|
||||
)
|
||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
|
||||
key = ECKey()
|
||||
key.generate()
|
||||
pubkey = key.get_pubkey().get_bytes()
|
||||
tx.vout[0].scriptPubKey = CScript([OP_2, pubkey, pubkey, pubkey, OP_3, OP_CHECKMULTISIG]) # Some bare multisig script (2-of-3)
|
||||
self.check_mempool_result(
|
||||
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: bare-multisig'}],
|
||||
rawtxs=[tx.serialize().hex()],
|
||||
)
|
||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
|
||||
tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig
|
||||
self.check_mempool_result(
|
||||
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly'}],
|
||||
@ -275,6 +288,12 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||
rawtxs=[tx.serialize().hex()],
|
||||
)
|
||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
|
||||
tx.vin[0].scriptSig = CScript([b'a' * 1648]) # Some too large scriptSig (>1650 bytes)
|
||||
self.check_mempool_result(
|
||||
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptsig-size'}],
|
||||
rawtxs=[tx.serialize().hex()],
|
||||
)
|
||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
|
||||
output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL]))
|
||||
num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy
|
||||
tx.vout = [output_p2sh_burn] * num_scripts
|
||||
|
@ -14,6 +14,7 @@ import os
|
||||
import pdb
|
||||
import random
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
@ -152,6 +153,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
except KeyError:
|
||||
self.log.exception("Key error")
|
||||
self.success = TestStatus.FAILED
|
||||
except subprocess.CalledProcessError as e:
|
||||
self.log.exception("Called Process failed with '{}'".format(e.output))
|
||||
self.success = TestStatus.FAILED
|
||||
except Exception:
|
||||
self.log.exception("Unexpected exception caught during testing")
|
||||
self.success = TestStatus.FAILED
|
||||
|
@ -62,6 +62,12 @@ def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None
|
||||
if reused_sum is not None:
|
||||
assert_approx(stats["reused"]["sum"], reused_sum, 0.001)
|
||||
|
||||
def assert_balances(node, mine):
|
||||
'''Make assertions about a node's getbalances output'''
|
||||
got = node.getbalances()["mine"]
|
||||
for k,v in mine.items():
|
||||
assert_approx(got[k], v, 0.001)
|
||||
|
||||
class AvoidReuseTest(BitcoinTestFramework):
|
||||
|
||||
def set_test_params(self):
|
||||
@ -86,6 +92,8 @@ class AvoidReuseTest(BitcoinTestFramework):
|
||||
self.test_fund_send_fund_senddirty()
|
||||
reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
|
||||
self.test_fund_send_fund_send()
|
||||
reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
|
||||
self.test_getbalances_used()
|
||||
|
||||
def test_persistence(self):
|
||||
'''Test that wallet files persist the avoid_reuse flag.'''
|
||||
@ -147,6 +155,10 @@ class AvoidReuseTest(BitcoinTestFramework):
|
||||
|
||||
# listunspent should show 1 single, unused 10 btc output
|
||||
assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0)
|
||||
# getbalances should show no used, 10 btc trusted
|
||||
assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10})
|
||||
# node 0 should not show a used entry, as it does not enable avoid_reuse
|
||||
assert("used" not in self.nodes[0].getbalances()["mine"])
|
||||
|
||||
self.nodes[1].sendtoaddress(retaddr, 5)
|
||||
self.nodes[0].generate(1)
|
||||
@ -154,6 +166,8 @@ class AvoidReuseTest(BitcoinTestFramework):
|
||||
|
||||
# listunspent should show 1 single, unused 5 btc output
|
||||
assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0)
|
||||
# getbalances should show no used, 5 btc trusted
|
||||
assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5})
|
||||
|
||||
self.nodes[0].sendtoaddress(fundaddr, 10)
|
||||
self.nodes[0].generate(1)
|
||||
@ -161,11 +175,15 @@ class AvoidReuseTest(BitcoinTestFramework):
|
||||
|
||||
# listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10)
|
||||
assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10)
|
||||
# getbalances should show 10 used, 5 btc trusted
|
||||
assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5})
|
||||
|
||||
self.nodes[1].sendtoaddress(address=retaddr, amount=10, avoid_reuse=False)
|
||||
|
||||
# listunspent should show 1 total outputs (5 btc), unused
|
||||
assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_count=0)
|
||||
# getbalances should show no used, 5 btc trusted
|
||||
assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5})
|
||||
|
||||
# node 1 should now have about 5 btc left (for both cases)
|
||||
assert_approx(self.nodes[1].getbalance(), 5, 0.001)
|
||||
@ -191,6 +209,8 @@ class AvoidReuseTest(BitcoinTestFramework):
|
||||
|
||||
# listunspent should show 1 single, unused 10 btc output
|
||||
assert_unspent(self.nodes[1], total_count=1, total_sum=10, reused_supported=True, reused_count=0)
|
||||
# getbalances should show no used, 10 btc trusted
|
||||
assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10})
|
||||
|
||||
self.nodes[1].sendtoaddress(retaddr, 5)
|
||||
self.nodes[0].generate(1)
|
||||
@ -198,6 +218,8 @@ class AvoidReuseTest(BitcoinTestFramework):
|
||||
|
||||
# listunspent should show 1 single, unused 5 btc output
|
||||
assert_unspent(self.nodes[1], total_count=1, total_sum=5, reused_supported=True, reused_count=0)
|
||||
# getbalances should show no used, 5 btc trusted
|
||||
assert_balances(self.nodes[1], mine={"used": 0, "trusted": 5})
|
||||
|
||||
self.nodes[0].sendtoaddress(fundaddr, 10)
|
||||
self.nodes[0].generate(1)
|
||||
@ -205,6 +227,8 @@ class AvoidReuseTest(BitcoinTestFramework):
|
||||
|
||||
# listunspent should show 2 total outputs (5, 10 btc), one unused (5), one reused (10)
|
||||
assert_unspent(self.nodes[1], total_count=2, total_sum=15, reused_count=1, reused_sum=10)
|
||||
# getbalances should show 10 used, 5 btc trusted
|
||||
assert_balances(self.nodes[1], mine={"used": 10, "trusted": 5})
|
||||
|
||||
# node 1 should now have a balance of 5 (no dirty) or 15 (including dirty)
|
||||
assert_approx(self.nodes[1].getbalance(), 5, 0.001)
|
||||
@ -216,10 +240,42 @@ class AvoidReuseTest(BitcoinTestFramework):
|
||||
|
||||
# listunspent should show 2 total outputs (1, 10 btc), one unused (1), one reused (10)
|
||||
assert_unspent(self.nodes[1], total_count=2, total_sum=11, reused_count=1, reused_sum=10)
|
||||
# getbalances should show 10 used, 1 btc trusted
|
||||
assert_balances(self.nodes[1], mine={"used": 10, "trusted": 1})
|
||||
|
||||
# node 1 should now have about 1 btc left (no dirty) and 11 (including dirty)
|
||||
assert_approx(self.nodes[1].getbalance(), 1, 0.001)
|
||||
assert_approx(self.nodes[1].getbalance(avoid_reuse=False), 11, 0.001)
|
||||
|
||||
def test_getbalances_used(self):
|
||||
'''
|
||||
getbalances and listunspent should pick up on reused addresses
|
||||
immediately, even for address reusing outputs created before the first
|
||||
transaction was spending from that address
|
||||
'''
|
||||
self.log.info("Test getbalances used category")
|
||||
|
||||
# node under test should be completely empty
|
||||
assert_equal(self.nodes[1].getbalance(avoid_reuse=False), 0)
|
||||
|
||||
new_addr = self.nodes[1].getnewaddress()
|
||||
ret_addr = self.nodes[0].getnewaddress()
|
||||
|
||||
# send multiple transactions, reusing one address
|
||||
for _ in range(11):
|
||||
self.nodes[0].sendtoaddress(new_addr, 1)
|
||||
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
# send transaction that should not use all the available outputs
|
||||
# per the current coin selection algorithm
|
||||
self.nodes[1].sendtoaddress(ret_addr, 5)
|
||||
|
||||
# getbalances and listunspent should show the remaining outputs
|
||||
# in the reused address as used/reused
|
||||
assert_unspent(self.nodes[1], total_count=2, total_sum=6, reused_count=1, reused_sum=1)
|
||||
assert_balances(self.nodes[1], mine={"used": 1, "trusted": 5})
|
||||
|
||||
if __name__ == '__main__':
|
||||
AvoidReuseTest().main()
|
||||
|
@ -62,14 +62,24 @@ class WalletTest(BitcoinTestFramework):
|
||||
assert_equal(len(self.nodes[0].listunspent()), 0)
|
||||
assert_equal(len(self.nodes[1].listunspent()), 0)
|
||||
|
||||
self.log.info("Mining blocks ...")
|
||||
self.log.info("Check that only node 0 is watching an address")
|
||||
assert 'watchonly' in self.nodes[0].getbalances()
|
||||
assert 'watchonly' not in self.nodes[1].getbalances()
|
||||
|
||||
self.log.info("Mining blocks ...")
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(1)
|
||||
self.nodes[1].generatetoaddress(101, ADDRESS_WATCHONLY)
|
||||
self.sync_all()
|
||||
|
||||
assert_equal(self.nodes[0].getbalances()['mine']['trusted'], 500)
|
||||
assert_equal(self.nodes[0].getwalletinfo()['balance'], 500)
|
||||
assert_equal(self.nodes[1].getbalances()['mine']['trusted'], 500)
|
||||
|
||||
assert_equal(self.nodes[0].getbalances()['watchonly']['immature'], 50000)
|
||||
assert 'watchonly' not in self.nodes[1].getbalances()
|
||||
|
||||
assert_equal(self.nodes[0].getbalance(), 500)
|
||||
assert_equal(self.nodes[1].getbalance(), 500)
|
||||
|
||||
@ -113,8 +123,11 @@ class WalletTest(BitcoinTestFramework):
|
||||
assert_equal(self.nodes[1].getbalance(minconf=1), Decimal('0'))
|
||||
# getunconfirmedbalance
|
||||
assert_equal(self.nodes[0].getunconfirmedbalance(), Decimal('960')) # output of node 1's spend
|
||||
assert_equal(self.nodes[0].getbalances()['mine']['untrusted_pending'], Decimal('960'))
|
||||
assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], Decimal('960'))
|
||||
|
||||
assert_equal(self.nodes[1].getunconfirmedbalance(), Decimal('0')) # Doesn't include output of node 0's send since it was spent
|
||||
assert_equal(self.nodes[1].getbalances()['mine']['untrusted_pending'], Decimal('0'))
|
||||
assert_equal(self.nodes[1].getwalletinfo()["unconfirmed_balance"], Decimal('0'))
|
||||
|
||||
test_balances(fee_node_1=Decimal('0.01'))
|
||||
|
@ -41,3 +41,45 @@ unsigned-integer-overflow:txmempool.cpp
|
||||
unsigned-integer-overflow:util/strencodings.cpp
|
||||
unsigned-integer-overflow:validation.cpp
|
||||
vptr:bls/bls.h
|
||||
|
||||
implicit-integer-sign-change:*/include/c++/*/bits/*.h
|
||||
implicit-integer-sign-change:*/new_allocator.h
|
||||
implicit-integer-sign-change:/usr/include/boost/date_time/format_date_parser.hpp
|
||||
implicit-integer-sign-change:arith_uint256.cpp
|
||||
implicit-integer-sign-change:bech32.cpp
|
||||
implicit-integer-sign-change:bloom.cpp
|
||||
implicit-integer-sign-change:chain.*
|
||||
implicit-integer-sign-change:coins.h
|
||||
implicit-integer-sign-change:compat/stdin.cpp
|
||||
implicit-integer-sign-change:compressor.h
|
||||
implicit-integer-sign-change:crypto/*
|
||||
implicit-integer-sign-change:key.cpp
|
||||
implicit-integer-sign-change:noui.cpp
|
||||
implicit-integer-sign-change:prevector.h
|
||||
implicit-integer-sign-change:protocol.cpp
|
||||
implicit-integer-sign-change:script/bitcoinconsensus.cpp
|
||||
implicit-integer-sign-change:script/interpreter.cpp
|
||||
implicit-integer-sign-change:serialize.h
|
||||
implicit-integer-sign-change:test/arith_uint256_tests.cpp
|
||||
implicit-integer-sign-change:test/coins_tests.cpp
|
||||
implicit-integer-sign-change:test/pow_tests.cpp
|
||||
implicit-integer-sign-change:test/prevector_tests.cpp
|
||||
implicit-integer-sign-change:test/sighash_tests.cpp
|
||||
implicit-integer-sign-change:test/streams_tests.cpp
|
||||
implicit-integer-sign-change:test/transaction_tests.cpp
|
||||
implicit-integer-sign-change:txmempool.cpp
|
||||
implicit-integer-sign-change:util/strencodings.*
|
||||
implicit-integer-sign-change:validation.cpp
|
||||
implicit-integer-sign-change:zmq/zmqpublishnotifier.cpp
|
||||
implicit-signed-integer-truncation,implicit-integer-sign-change:chain.h
|
||||
implicit-signed-integer-truncation,implicit-integer-sign-change:test/skiplist_tests.cpp
|
||||
implicit-signed-integer-truncation:chain.h
|
||||
implicit-signed-integer-truncation:crypto/*
|
||||
implicit-signed-integer-truncation:cuckoocache.h
|
||||
implicit-signed-integer-truncation:leveldb/*
|
||||
implicit-signed-integer-truncation:streams.h
|
||||
implicit-signed-integer-truncation:test/arith_uint256_tests.cpp
|
||||
implicit-signed-integer-truncation:test/skiplist_tests.cpp
|
||||
implicit-signed-integer-truncation:torcontrol.cpp
|
||||
implicit-unsigned-integer-truncation:crypto/*
|
||||
implicit-unsigned-integer-truncation:leveldb/*
|
||||
|
Loading…
Reference in New Issue
Block a user