mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
Merge pull request #5226 from knst/bc-bp-missing-3
backport: bitcoin#13424, #14092, #14380, #14879, #15693, #16185, #16489, #16556, #16806, #16866, #16873, partial #16197, fixes for #10637
This commit is contained in:
commit
daa7c032ce
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: Bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- This issue tracker is only for technical issues related to Dash Core.
|
||||
|
||||
General dash questions and/or support requests are best directed to the Dash Forum at https://www.dash.org/forum/
|
||||
|
||||
For reporting security issues, please read instructions at https://www.dash.org/bug-bounty/
|
||||
|
||||
If the node is "stuck" during sync or giving "block checksum mismatch" errors, please ensure your hardware is stable by running memtest and observe CPU temperature with a load-test tool such as linpack before creating an issue! -->
|
||||
|
||||
<!-- Describe the issue -->
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
<!--- What behavior did you expect? -->
|
||||
|
||||
**Actual behavior**
|
||||
|
||||
<!--- What was the actual behavior (provide screenshots if the issue is GUI-related)? -->
|
||||
|
||||
**To reproduce**
|
||||
|
||||
<!--- How reliably can you reproduce the issue, what are the steps to do so? -->
|
||||
|
||||
**System information**
|
||||
|
||||
<!-- What version of Dash Core are you using, where did you get it (website, self-compiled, etc)? -->
|
||||
|
||||
<!-- What type of machine are you observing the error on (OS/CPU and disk type)? -->
|
||||
|
||||
<!-- GUI-related issue? What is your operating system and its version? If Linux, what is your desktop environment and graphical shell? -->
|
||||
|
||||
<!-- Any extra information that might be useful in the debugging process. -->
|
||||
<!--- This is normally the contents of a `debug.log` or `config.log` file. Raw text or a link to a pastebin type site are preferred. -->
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: Feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
17
.travis.yml
17
.travis.yml
@ -2,6 +2,21 @@
|
||||
# - sudo/dist/group are set so as to get Blue Box VMs, necessary for [loopback]
|
||||
# IPv6 support
|
||||
|
||||
# The test build matrix (stage: test) is constructed to test a wide range of
|
||||
# configurations, rather than a single pass/fail. This helps to catch build
|
||||
# failures and logic errors that present on platforms other than the ones the
|
||||
# author has tested.
|
||||
#
|
||||
# Some builders use the dependency-generator in `./depends`, rather than using
|
||||
# apt-get to install build dependencies. This guarantees that the tester is
|
||||
# using the same versions as Gitian, so the build results are nearly identical
|
||||
# to what would be found in a final release.
|
||||
#
|
||||
# In order to avoid rebuilding all dependencies for each build, the binaries
|
||||
# are cached and re-used when possible. Changes in the dependency-generator
|
||||
# will trigger cache-invalidation and rebuilds as necessary.
|
||||
#
|
||||
|
||||
dist: xenial
|
||||
|
||||
os: linux
|
||||
@ -177,7 +192,7 @@ before_script:
|
||||
# Build docker image only for develop branch of the main repo
|
||||
- if [ "$TRAVIS_REPO_SLUG" != "dashpay/dash" -o "$TRAVIS_BRANCH" != "develop" -o "$TRAVIS_PULL_REQUEST" != "false" ]; then export DOCKER_BUILD="false"; echo DOCKER_BUILD=$DOCKER_BUILD; fi
|
||||
# TODO: Check keys and signed commits
|
||||
#- if [ "$TRAVIS_REPO_SLUG" = "dashpay/dash" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then while read LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys; fi
|
||||
#- if [ "$TRAVIS_REPO_SLUG" = "dashpay/dash" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then travis_retry gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys $(<contrib/verify-commits/trusted-keys); fi
|
||||
#- if [ "$TRAVIS_REPO_SLUG" = "dashpay/dash" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then travis_wait 30 contrib/verify-commits/verify-commits.py; fi
|
||||
after_script:
|
||||
- echo $TRAVIS_COMMIT_RANGE
|
||||
|
@ -20,6 +20,6 @@ test/lint/lint-all.sh
|
||||
|
||||
if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" ] && [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then
|
||||
git log --merges --before="2 days ago" -1 --format='%H' > ./contrib/verify-commits/trusted-sha512-root-commit
|
||||
while read -r LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys &&
|
||||
travis_retry gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys $(<contrib/verify-commits/trusted-keys) &&
|
||||
./contrib/verify-commits/verify-commits.py --clean-merge=2;
|
||||
fi
|
||||
|
@ -9,7 +9,6 @@ export LC_ALL=C.UTF-8
|
||||
export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev bsdmainutils libboost-filesystem-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev"
|
||||
export DEP_OPTS="NO_UPNP=1 DEBUG=1"
|
||||
export TEST_RUNNER_EXTRA="--extended --exclude feature_pruning,feature_dbcrash,wallet_multiwallet.py" # Temporarily suppress ASan heap-use-after-free (see issue #14163)
|
||||
export RUN_BENCH=true
|
||||
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"
|
||||
|
@ -5,8 +5,9 @@
|
||||
# See "man systemd.service" for details.
|
||||
|
||||
# Note that almost all daemon options could be specified in
|
||||
# /etc/dash/dash.conf, except for those explicitly specified as arguments
|
||||
# in ExecStart=
|
||||
# /etc/dash/dash.conf, but keep in mind those explicitly
|
||||
# specified as arguments in ExecStart= will override those in the
|
||||
# config file.
|
||||
|
||||
[Unit]
|
||||
Description=Dash daemon
|
||||
@ -18,6 +19,10 @@ ExecStart=/usr/bin/dashd -daemon \
|
||||
-conf=/etc/dash/dash.conf \
|
||||
-datadir=/var/lib/dashd
|
||||
|
||||
# Make sure the config directory is readable by the service user
|
||||
PermissionsStartOnly=true
|
||||
ExecStartPre=/bin/chgrp dashcore /etc/dash
|
||||
|
||||
# Process management
|
||||
####################
|
||||
|
||||
@ -54,6 +59,9 @@ PrivateTmp=true
|
||||
# Mount /usr, /boot/ and /etc read-only for the process.
|
||||
ProtectSystem=full
|
||||
|
||||
# Deny access to /home, /root and /run/user
|
||||
ProtectHome=true
|
||||
|
||||
# Disallow the process and all of its children to gain
|
||||
# new privileges through execve().
|
||||
NoNewPrivileges=true
|
||||
|
@ -52,7 +52,6 @@ The Dash Core repo's [root README](/README.md) contains relevant information on
|
||||
- Source Code Documentation ***TODO***
|
||||
- [Translation Process](translation_process.md)
|
||||
- [Translation Strings Policy](translation_strings_policy.md)
|
||||
- [Travis CI](travis-ci.md)
|
||||
- [JSON-RPC Interface](JSON-RPC-interface.md)
|
||||
- [Unauthenticated REST Interface](REST-interface.md)
|
||||
- [Shared Libraries](shared-libraries.md)
|
||||
|
10
doc/init.md
10
doc/init.md
@ -59,11 +59,11 @@ Data directory: `/var/lib/dashd`
|
||||
PID file: `/var/run/dashd/dashd.pid` (OpenRC and Upstart) or `/run/dashd/dashd.pid` (systemd)
|
||||
Lock file: `/var/lock/subsys/dashd` (CentOS)
|
||||
|
||||
The configuration file, PID directory (if applicable) and data directory
|
||||
should all be owned by the dashcore user and group. It is advised for security
|
||||
reasons to make the configuration file and data directory only readable by the
|
||||
dashcore user and group. Access to dash-cli and other dashd rpc clients
|
||||
can then be controlled by group membership.
|
||||
The PID directory (if applicable) and data directory should both be owned by the
|
||||
dashcore user and group. It is advised for security reasons to make the
|
||||
configuration file and data directory only readable by the dashcore user and
|
||||
group. Access to dash-cli and other dashd rpc clients can then be
|
||||
controlled by group membership.
|
||||
|
||||
NOTE: When using the systemd .service file, the creation of the aforementioned
|
||||
directories and the setting of their permissions is automatically handled by
|
||||
|
6
doc/release-notes-16185.md
Normal file
6
doc/release-notes-16185.md
Normal file
@ -0,0 +1,6 @@
|
||||
RPC changes
|
||||
-----------
|
||||
The `gettransaction` RPC now accepts a third (boolean) argument `verbose`. If
|
||||
set to `true`, a new `decoded` field will be added to the response containing
|
||||
the decoded transaction. This field is equivalent to RPC `decoderawtransaction`,
|
||||
or RPC `getrawtransaction` when `verbose` is passed.
|
@ -1,42 +0,0 @@
|
||||
Travis CI
|
||||
=========
|
||||
|
||||
Support for using travis-ci has been added in order to automate pull-testing.
|
||||
See [travis-ci.org](https://travis-ci.org/) for more info
|
||||
|
||||
This procedure is different than the pull-tester that came before it in a few
|
||||
ways.
|
||||
|
||||
There is nothing to administer. This is a major feature as it means
|
||||
that builds have no local state. Because there is no ability to login to the
|
||||
builders to install packages (tools, dependencies, etc), the entire build
|
||||
procedure must instead be controlled by a declarative script `.travis.yml`.
|
||||
This script declares each build configuration, creates virtual machines as
|
||||
necessary, builds, then discards the virtual machines.
|
||||
|
||||
A build matrix is constructed to test a wide range of configurations, rather
|
||||
than a single pass/fail. This helps to catch build failures and logic errors
|
||||
that present on platforms other than the ones the author has tested. This
|
||||
matrix is defined in the build script and can be changed at any time.
|
||||
|
||||
All builders use the dependency-generator in the [depends dir](/depends), rather than
|
||||
using apt-get to install build dependencies. This guarantees that the tester
|
||||
is using the same versions as Gitian, so the build results are nearly identical
|
||||
to what would be found in a final release. However, this also means that builds
|
||||
will fail if new dependencies are introduced without being added to the
|
||||
dependency generator.
|
||||
|
||||
In order to avoid rebuilding all dependencies for each build, the binaries are
|
||||
cached and re-used when possible. Changes in the dependency-generator will
|
||||
trigger cache-invalidation and rebuilds as necessary.
|
||||
|
||||
These caches can be manually removed if necessary. This is one of the very few
|
||||
manual operations that is possible with Travis, and it can be done by the
|
||||
Dash Core committer via the Travis web interface.
|
||||
|
||||
In some cases, secure strings may be needed for hiding sensitive info such as
|
||||
private keys or URLs. The travis client may be used to create these strings:
|
||||
http://docs.travis-ci.com/user/encryption-keys/
|
||||
|
||||
For the details of the build descriptor, see the official docs:
|
||||
http://docs.travis-ci.com/user/build-configuration/
|
@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2009-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.
|
||||
@ -66,12 +66,12 @@ static bool AppInit(int argc, char* argv[])
|
||||
|
||||
// Process help and version before taking care about datadir
|
||||
if (HelpRequested(args) || args.IsArgSet("-version")) {
|
||||
std::string strUsage = PACKAGE_NAME " Daemon version " + FormatFullVersion() + "\n";
|
||||
std::string strUsage = PACKAGE_NAME " version " + FormatFullVersion() + "\n";
|
||||
|
||||
if (args.IsArgSet("-version")) {
|
||||
strUsage += FormatParagraph(LicenseInfo()) + "\n";
|
||||
} else {
|
||||
strUsage += "\nUsage: dashd [options] Start " PACKAGE_NAME " Daemon\n";
|
||||
strUsage += "\nUsage: dashd [options] Start " PACKAGE_NAME "\n";
|
||||
strUsage += "\n" + args.GetHelpMessage();
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ static bool AppInit(int argc, char* argv[])
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
tfm::format(std::cout, PACKAGE_NAME " daemon starting\n");
|
||||
tfm::format(std::cout, PACKAGE_NAME " starting\n");
|
||||
|
||||
// Daemonize
|
||||
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
|
||||
|
@ -14,6 +14,22 @@
|
||||
<string>Tools window</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_alerts">
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_Buttons_2">
|
||||
<property name="spacing">
|
||||
|
@ -251,9 +251,8 @@ void OverviewPage::updateWatchOnlyLabels(bool showWatchOnly)
|
||||
void OverviewPage::setClientModel(ClientModel *model)
|
||||
{
|
||||
this->clientModel = model;
|
||||
if(model)
|
||||
{
|
||||
// Show warning if this is a prerelease version
|
||||
if (model) {
|
||||
// Show warning, for example if this is a prerelease version
|
||||
connect(model, &ClientModel::alertsChanged, this, &OverviewPage::updateAlerts);
|
||||
updateAlerts(model->getStatusBarWarnings());
|
||||
}
|
||||
|
@ -1586,6 +1586,13 @@ QWidget#RPCConsole QLabel#peerHeading,
|
||||
QWidget#RPCConsole QLabel#banHeading {
|
||||
}
|
||||
|
||||
QWidget#RPCConsole QLabel#label_alerts {
|
||||
background-color: #c79304;
|
||||
color: #222222;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
QWidget#RPCConsole QLabel#labelNetwork,
|
||||
QWidget#RPCConsole QLabel#label_10,
|
||||
QWidget#RPCConsole QLabel#labelMempoolTitle { /* Margin between Network and Block Chain headers */
|
||||
|
@ -250,6 +250,17 @@ QWidget .QFrame#frame_2 QListView { /* Transaction List */
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
RPCConsole
|
||||
******************************************************/
|
||||
|
||||
QWidget#RPCConsole QLabel#label_alerts {
|
||||
background-color: #c79304;
|
||||
color: #222222;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
SendCoinsDialog
|
||||
******************************************************/
|
||||
|
@ -583,6 +583,17 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
|
||||
void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_t bestblock_date, uint256 bestblock_hash, double verification_progress)
|
||||
{
|
||||
clientModel = model;
|
||||
|
||||
bool wallet_enabled{false};
|
||||
#ifdef ENABLE_WALLET
|
||||
wallet_enabled = WalletModel::isWalletEnabled();
|
||||
#endif // ENABLE_WALLET
|
||||
if (model && !wallet_enabled) {
|
||||
// Show warning, for example if this is a prerelease version
|
||||
connect(model, &ClientModel::alertsChanged, this, &RPCConsole::updateAlerts);
|
||||
updateAlerts(model->getStatusBarWarnings());
|
||||
}
|
||||
|
||||
ui->trafficGraph->setClientModel(model);
|
||||
if (model && clientModel->getPeerTableModel() && clientModel->getBanTableModel()) {
|
||||
// Keep up to date with client
|
||||
@ -1441,3 +1452,9 @@ QKeySequence RPCConsole::tabShortcut(TabTypes tab_type) const
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void RPCConsole::updateAlerts(const QString& warnings)
|
||||
{
|
||||
this->ui->label_alerts->setVisible(!warnings.isEmpty());
|
||||
this->ui->label_alerts->setText(warnings);
|
||||
}
|
||||
|
@ -192,6 +192,9 @@ private:
|
||||
|
||||
/** Update UI with latest network info from model. */
|
||||
void updateNetworkState();
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateAlerts(const QString& warnings);
|
||||
};
|
||||
|
||||
#endif // BITCOIN_QT_RPCCONSOLE_H
|
||||
|
@ -374,7 +374,7 @@ static UniValue waitforblock(const JSONRPCRequest& request)
|
||||
}.Check(request);
|
||||
int timeout = 0;
|
||||
|
||||
uint256 hash = uint256S(request.params[0].get_str());
|
||||
uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
||||
|
||||
if (!request.params[1].isNull())
|
||||
timeout = request.params[1].get_int();
|
||||
@ -634,7 +634,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
|
||||
if (!request.params[1].isNull())
|
||||
fVerbose = request.params[1].get_bool();
|
||||
|
||||
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||
uint256 hash(ParseHashV(request.params[0], "parameter 1"));
|
||||
|
||||
const CTxMemPool& mempool = EnsureMemPool(request.context);
|
||||
LOCK(mempool.cs);
|
||||
@ -698,7 +698,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
|
||||
if (!request.params[1].isNull())
|
||||
fVerbose = request.params[1].get_bool();
|
||||
|
||||
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||
uint256 hash(ParseHashV(request.params[0], "parameter 1"));
|
||||
|
||||
const CTxMemPool& mempool = EnsureMemPool(request.context);
|
||||
LOCK(mempool.cs);
|
||||
@ -749,7 +749,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||
uint256 hash(ParseHashV(request.params[0], "parameter 1"));
|
||||
|
||||
const CTxMemPool& mempool = EnsureMemPool(request.context);
|
||||
LOCK(mempool.cs);
|
||||
@ -862,8 +862,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
std::string strHash = request.params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[0], "hash"));
|
||||
|
||||
bool fVerbose = true;
|
||||
if (!request.params[1].isNull())
|
||||
@ -936,8 +935,7 @@ static UniValue getblockheaders(const JSONRPCRequest& request)
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
std::string strHash = request.params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
||||
|
||||
const CBlockIndex* pblockindex;
|
||||
const CBlockIndex* tip;
|
||||
@ -1048,8 +1046,7 @@ static UniValue getmerkleblocks(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Filter is not within size constraints");
|
||||
}
|
||||
|
||||
std::string strHash = request.params[1].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[1], "blockhash"));
|
||||
|
||||
const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
|
||||
if (!pblockindex) {
|
||||
@ -1154,8 +1151,7 @@ static UniValue getblock(const JSONRPCRequest& request)
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
std::string strHash = request.params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
||||
|
||||
int verbosity = 1;
|
||||
if (!request.params[1].isNull()) {
|
||||
@ -1356,8 +1352,7 @@ static UniValue gettxout(const JSONRPCRequest& request)
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
std::string strHash = request.params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[0], "txid"));
|
||||
int n = request.params[1].get_int();
|
||||
COutPoint out(hash, n);
|
||||
bool fMempool = true;
|
||||
@ -1805,8 +1800,7 @@ static UniValue preciousblock(const JSONRPCRequest& request)
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
std::string strHash = request.params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
||||
CBlockIndex* pblockindex;
|
||||
|
||||
{
|
||||
@ -1841,8 +1835,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request)
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
std::string strHash = request.params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
||||
CValidationState state;
|
||||
|
||||
CBlockIndex* pblockindex;
|
||||
@ -1881,8 +1874,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request)
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
std::string strHash = request.params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
@ -1937,7 +1929,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
|
||||
LOCK(cs_main);
|
||||
pindex = ::ChainActive().Tip();
|
||||
} else {
|
||||
uint256 hash = uint256S(request.params[1].get_str());
|
||||
uint256 hash(ParseHashV(request.params[1], "blockhash"));
|
||||
LOCK(cs_main);
|
||||
pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
|
||||
if (!pindex) {
|
||||
@ -2114,8 +2106,7 @@ static UniValue getblockstats(const JSONRPCRequest& request)
|
||||
|
||||
pindex = ::ChainActive()[height];
|
||||
} else {
|
||||
const uint256 hash = ParseHashV(request.params[0], "parameter 1");
|
||||
|
||||
const uint256 hash(ParseHashV(request.params[0], "hash_or_height"));
|
||||
pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
|
||||
if (!pindex) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||
@ -2307,8 +2298,7 @@ static UniValue getspecialtxes(const JSONRPCRequest& request)
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
std::string strHash = request.params[0].get_str();
|
||||
uint256 hash(uint256S(strHash));
|
||||
uint256 hash(ParseHashV(request.params[0], "blockhash"));
|
||||
|
||||
int nTxType = -1;
|
||||
if (!request.params[1].isNull()) {
|
||||
@ -2637,7 +2627,7 @@ static UniValue getblockfilter(const JSONRPCRequest& request)
|
||||
},
|
||||
}.Check(request);
|
||||
|
||||
uint256 block_hash = ParseHashV(request.params[0], "blockhash");
|
||||
uint256 block_hash(ParseHashV(request.params[0], "blockhash"));
|
||||
std::string filtertype_name = "basic";
|
||||
if (!request.params[1].isNull()) {
|
||||
filtertype_name = request.params[1].get_str();
|
||||
|
@ -105,6 +105,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "getchaintxstats", 0, "nblocks" },
|
||||
{ "getmerkleblocks", 2, "count" },
|
||||
{ "gettransaction", 1, "include_watchonly" },
|
||||
{ "gettransaction", 2, "verbose" },
|
||||
{ "getrawtransaction", 1, "verbose" },
|
||||
{ "createrawtransaction", 0, "inputs" },
|
||||
{ "createrawtransaction", 1, "outputs" },
|
||||
|
@ -640,7 +640,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
|
||||
|
||||
paramIdx++;
|
||||
} else {
|
||||
uint256 collateralHash = ParseHashV(request.params[paramIdx], "collateralHash");
|
||||
uint256 collateralHash(ParseHashV(request.params[paramIdx], "collateralHash"));
|
||||
int32_t collateralIndex = ParseInt32V(request.params[paramIdx + 1], "collateralIndex");
|
||||
if (collateralHash.IsNull() || collateralIndex < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid hash or index: %s-%d", collateralHash.ToString(), collateralIndex));
|
||||
@ -1405,7 +1405,7 @@ static UniValue protx_info(const JSONRPCRequest& request)
|
||||
g_txindex->BlockUntilSyncedToCurrentChain();
|
||||
}
|
||||
|
||||
uint256 proTxHash = ParseHashV(request.params[0], "proTxHash");
|
||||
uint256 proTxHash(ParseHashV(request.params[0], "proTxHash"));
|
||||
auto mnList = deterministicMNManager->GetListAtChainTip();
|
||||
auto dmn = mnList.GetMN(proTxHash);
|
||||
if (!dmn) {
|
||||
|
@ -213,7 +213,7 @@ static UniValue gobject_prepare(const JSONRPCRequest& request)
|
||||
COutPoint outpoint;
|
||||
outpoint.SetNull();
|
||||
if (!request.params[5].isNull() && !request.params[6].isNull()) {
|
||||
uint256 collateralHash = ParseHashV(request.params[5], "outputHash");
|
||||
uint256 collateralHash(ParseHashV(request.params[5], "outputHash"));
|
||||
int32_t collateralIndex = ParseInt32V(request.params[6], "outputIndex");
|
||||
if (collateralHash.IsNull() || collateralIndex < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid hash or index: %s-%d", collateralHash.ToString(), collateralIndex));
|
||||
@ -427,9 +427,7 @@ static UniValue gobject_vote_conf(const JSONRPCRequest& request)
|
||||
{
|
||||
gobject_vote_conf_help(request);
|
||||
|
||||
uint256 hash;
|
||||
|
||||
hash = ParseHashV(request.params[0], "Object hash");
|
||||
uint256 hash(ParseHashV(request.params[0], "Object hash"));
|
||||
std::string strVoteSignal = request.params[1].get_str();
|
||||
std::string strVoteOutcome = request.params[2].get_str();
|
||||
|
||||
@ -605,7 +603,7 @@ static UniValue gobject_vote_many(const JSONRPCRequest& request)
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!wallet) return NullUniValue;
|
||||
|
||||
uint256 hash = ParseHashV(request.params[0], "Object hash");
|
||||
uint256 hash(ParseHashV(request.params[0], "Object hash"));
|
||||
std::string strVoteSignal = request.params[1].get_str();
|
||||
std::string strVoteOutcome = request.params[2].get_str();
|
||||
|
||||
@ -664,7 +662,7 @@ static UniValue gobject_vote_alias(const JSONRPCRequest& request)
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!wallet) return NullUniValue;
|
||||
|
||||
uint256 hash = ParseHashV(request.params[0], "Object hash");
|
||||
uint256 hash(ParseHashV(request.params[0], "Object hash"));
|
||||
std::string strVoteSignal = request.params[1].get_str();
|
||||
std::string strVoteOutcome = request.params[2].get_str();
|
||||
|
||||
@ -682,7 +680,7 @@ static UniValue gobject_vote_alias(const JSONRPCRequest& request)
|
||||
|
||||
EnsureWalletIsUnlocked(wallet.get());
|
||||
|
||||
uint256 proTxHash = ParseHashV(request.params[3], "protx-hash");
|
||||
uint256 proTxHash(ParseHashV(request.params[3], "protx-hash"));
|
||||
auto dmn = deterministicMNManager->GetListAtChainTip().GetValidMN(proTxHash);
|
||||
if (!dmn) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unknown proTxHash");
|
||||
@ -850,7 +848,7 @@ static UniValue gobject_get(const JSONRPCRequest& request)
|
||||
gobject_get_help(request);
|
||||
|
||||
// COLLECT VARIABLES FROM OUR USER
|
||||
uint256 hash = ParseHashV(request.params[0], "GovObj hash");
|
||||
uint256 hash(ParseHashV(request.params[0], "GovObj hash"));
|
||||
|
||||
if (g_txindex) {
|
||||
g_txindex->BlockUntilSyncedToCurrentChain();
|
||||
@ -943,11 +941,11 @@ static UniValue gobject_getcurrentvotes(const JSONRPCRequest& request)
|
||||
|
||||
// COLLECT PARAMETERS FROM USER
|
||||
|
||||
uint256 hash = ParseHashV(request.params[0], "Governance hash");
|
||||
uint256 hash(ParseHashV(request.params[0], "Governance hash"));
|
||||
|
||||
COutPoint mnCollateralOutpoint;
|
||||
if (!request.params[1].isNull() && !request.params[2].isNull()) {
|
||||
uint256 txid = ParseHashV(request.params[1], "Masternode Collateral hash");
|
||||
uint256 txid(ParseHashV(request.params[1], "Masternode Collateral hash"));
|
||||
std::string strVout = request.params[2].get_str();
|
||||
mnCollateralOutpoint = COutPoint(txid, LocaleIndependentAtoi<uint32_t>(strVout));
|
||||
}
|
||||
@ -1076,11 +1074,11 @@ static UniValue voteraw(const JSONRPCRequest& request)
|
||||
RPCExamples{""}
|
||||
}.Check(request);
|
||||
|
||||
uint256 hashMnCollateralTx = ParseHashV(request.params[0], "mn collateral tx hash");
|
||||
uint256 hashMnCollateralTx(ParseHashV(request.params[0], "mn collateral tx hash"));
|
||||
int nMnCollateralTxIndex = request.params[1].get_int();
|
||||
COutPoint outpoint = COutPoint(hashMnCollateralTx, nMnCollateralTxIndex);
|
||||
|
||||
uint256 hashGovObj = ParseHashV(request.params[2], "Governance hash");
|
||||
uint256 hashGovObj(ParseHashV(request.params[2], "Governance hash"));
|
||||
std::string strVoteSignal = request.params[3].get_str();
|
||||
std::string strVoteOutcome = request.params[4].get_str();
|
||||
|
||||
|
@ -420,7 +420,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request)
|
||||
pindex = ::ChainActive().Tip();
|
||||
} else {
|
||||
LOCK(cs_main);
|
||||
uint256 blockHash = ParseHashV(request.params[0], "blockhash");
|
||||
uint256 blockHash(ParseHashV(request.params[0], "blockhash"));
|
||||
pindex = g_chainman.m_blockman.LookupBlockIndex(blockHash);
|
||||
if (pindex == nullptr) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||
|
@ -476,7 +476,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
uint256 hash = ParseHashV(request.params[0].get_str(), "txid");
|
||||
uint256 hash(ParseHashV(request.params[0].get_str(), "txid"));
|
||||
CAmount nAmount = request.params[1].get_int64();
|
||||
|
||||
EnsureMemPool(request.context).PrioritiseTransaction(hash, nAmount);
|
||||
@ -726,7 +726,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
|
||||
// Format: <hashBestChain><nTransactionsUpdatedLast>
|
||||
std::string lpstr = lpval.get_str();
|
||||
|
||||
hashWatchedChain.SetHex(lpstr.substr(0, 64));
|
||||
hashWatchedChain = ParseHashV(lpstr.substr(0, 64), "longpollid");
|
||||
nTransactionsUpdatedLastLP = LocaleIndependentAtoi<int64_t>(lpstr.substr(64));
|
||||
}
|
||||
else
|
||||
|
@ -240,7 +240,7 @@ static UniValue quorum_info(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
|
||||
}
|
||||
|
||||
uint256 quorumHash = ParseHashV(request.params[1], "quorumHash");
|
||||
uint256 quorumHash(ParseHashV(request.params[1], "quorumHash"));
|
||||
bool includeSkShare = false;
|
||||
if (!request.params[2].isNull()) {
|
||||
includeSkShare = ParseBoolV(request.params[2], "includeSkShare");
|
||||
@ -381,7 +381,7 @@ static UniValue quorum_memberof(const JSONRPCRequest& request)
|
||||
{
|
||||
quorum_memberof_help(request);
|
||||
|
||||
uint256 protxHash = ParseHashV(request.params[0], "proTxHash");
|
||||
uint256 protxHash(ParseHashV(request.params[0], "proTxHash"));
|
||||
int scanQuorumsCount = -1;
|
||||
if (!request.params[1].isNull()) {
|
||||
scanQuorumsCount = ParseInt32V(request.params[1], "scanQuorumsCount");
|
||||
@ -530,8 +530,8 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
|
||||
}
|
||||
|
||||
uint256 id = ParseHashV(request.params[1], "id");
|
||||
uint256 msgHash = ParseHashV(request.params[2], "msgHash");
|
||||
uint256 id(ParseHashV(request.params[1], "id"));
|
||||
uint256 msgHash(ParseHashV(request.params[2], "msgHash"));
|
||||
|
||||
if (cmd == "quorumsign") {
|
||||
uint256 quorumHash;
|
||||
@ -590,7 +590,7 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request)
|
||||
return llmq_ctx.sigman->VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, msgHash, sig, 0) ||
|
||||
llmq_ctx.sigman->VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, msgHash, sig, signOffset);
|
||||
} else {
|
||||
uint256 quorumHash = ParseHashV(request.params[4], "quorumHash");
|
||||
uint256 quorumHash(ParseHashV(request.params[4], "quorumHash"));
|
||||
llmq::CQuorumCPtr quorum = llmq_ctx.qman->GetQuorum(llmqType, quorumHash);
|
||||
|
||||
if (!quorum) {
|
||||
@ -642,7 +642,7 @@ static UniValue quorum_selectquorum(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
|
||||
}
|
||||
|
||||
uint256 id = ParseHashV(request.params[1], "id");
|
||||
uint256 id(ParseHashV(request.params[1], "id"));
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
@ -723,7 +723,7 @@ static UniValue quorum_getdata(const JSONRPCRequest& request)
|
||||
|
||||
NodeId nodeId = ParseInt64V(request.params[0], "nodeId");
|
||||
Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(ParseInt32V(request.params[1], "llmqType"));
|
||||
uint256 quorumHash = ParseHashV(request.params[2], "quorumHash");
|
||||
uint256 quorumHash(ParseHashV(request.params[2], "quorumHash"));
|
||||
uint16_t nDataMask = static_cast<uint16_t>(ParseInt32V(request.params[3], "dataMask"));
|
||||
uint256 proTxHash;
|
||||
|
||||
@ -871,7 +871,7 @@ static UniValue verifychainlock(const JSONRPCRequest& request)
|
||||
{
|
||||
verifychainlock_help(request);
|
||||
|
||||
const uint256 nBlockHash = ParseHashV(request.params[0], "blockHash");
|
||||
const uint256 nBlockHash(ParseHashV(request.params[0], "blockHash"));
|
||||
|
||||
CBLSSignature chainLockSig;
|
||||
if (!chainLockSig.SetHexStr(request.params[1].get_str())) {
|
||||
@ -915,8 +915,8 @@ static UniValue verifyislock(const JSONRPCRequest& request)
|
||||
{
|
||||
verifyislock_help(request);
|
||||
|
||||
uint256 id = ParseHashV(request.params[0], "id");
|
||||
uint256 txid = ParseHashV(request.params[1], "txid");
|
||||
uint256 id(ParseHashV(request.params[0], "id"));
|
||||
uint256 txid(ParseHashV(request.params[1], "txid"));
|
||||
|
||||
CBLSSignature sig;
|
||||
if (!sig.SetHexStr(request.params[2].get_str())) {
|
||||
|
@ -297,10 +297,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
|
||||
UniValue txids = request.params[0].get_array();
|
||||
for (unsigned int idx = 0; idx < txids.size(); idx++) {
|
||||
const UniValue& txid = txids[idx];
|
||||
if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid txid ")+txid.get_str());
|
||||
}
|
||||
uint256 hash(uint256S(txid.get_str()));
|
||||
uint256 hash(ParseHashV(txid, "txid"));
|
||||
if (setTxids.count(hash)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ")+txid.get_str());
|
||||
}
|
||||
@ -312,7 +309,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
|
||||
uint256 hashBlock;
|
||||
if (!request.params[1].isNull()) {
|
||||
LOCK(cs_main);
|
||||
hashBlock = uint256S(request.params[1].get_str());
|
||||
hashBlock = ParseHashV(request.params[1], "blockhash");
|
||||
pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
|
||||
if (!pblockindex) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||
|
@ -182,7 +182,7 @@ static RPCHelpMan stop()
|
||||
// Also accept the hidden 'wait' integer argument (milliseconds)
|
||||
// For instance, 'stop 1000' makes the call wait 1 second before returning
|
||||
// to the client (intended for testing)
|
||||
"\nStop Dash Core server.",
|
||||
"\nRequest a graceful shutdown of " PACKAGE_NAME ".",
|
||||
{
|
||||
{"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "how long to wait in ms", "", {}, /* hidden */ true},
|
||||
},
|
||||
|
@ -88,16 +88,12 @@ CAmount AmountFromValue(const UniValue& value)
|
||||
|
||||
uint256 ParseHashV(const UniValue& v, std::string strName)
|
||||
{
|
||||
std::string strHex;
|
||||
if (v.isStr())
|
||||
strHex = v.get_str();
|
||||
std::string strHex{v.get_str()};
|
||||
if (64 != strHex.length())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
|
||||
if (!IsHex(strHex)) // Note: IsHex("") is false
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
|
||||
if (64 != strHex.length())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length()));
|
||||
uint256 result;
|
||||
result.SetHex(strHex);
|
||||
return result;
|
||||
return uint256S(strHex);
|
||||
}
|
||||
uint256 ParseHashO(const UniValue& o, std::string strKey)
|
||||
{
|
||||
|
@ -358,8 +358,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request)
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
uint256 hash;
|
||||
hash.SetHex(request.params[0].get_str());
|
||||
uint256 hash(ParseHashV(request.params[0], "txid"));
|
||||
std::vector<uint256> vHash;
|
||||
vHash.push_back(hash);
|
||||
std::vector<uint256> vHashOut;
|
||||
|
@ -1582,7 +1582,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
|
||||
|
||||
uint256 blockId;
|
||||
if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
|
||||
blockId.SetHex(request.params[0].get_str());
|
||||
blockId = ParseHashV(request.params[0], "blockhash");
|
||||
height = pwallet->chain().findFork(blockId, &altheight);
|
||||
|
||||
if (!height) {
|
||||
@ -1655,6 +1655,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
|
||||
{"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses in balance calculation and details[]"},
|
||||
{"verbose", RPCArg::Type::BOOL, /* default */ "false", "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
|
||||
},
|
||||
RPCResult{
|
||||
RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
|
||||
@ -1688,6 +1689,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
|
||||
}},
|
||||
}},
|
||||
{RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
|
||||
{RPCResult::Type::OBJ, "decoded", /*optional=*/true, "the decoded transaction (only present when `verbose` is passed), equivalent to the",
|
||||
{
|
||||
{RPCResult::Type::ELISION, "", "RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."},
|
||||
}},
|
||||
}),
|
||||
},
|
||||
RPCExamples{
|
||||
@ -1707,8 +1712,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
uint256 hash;
|
||||
hash.SetHex(request.params[0].get_str());
|
||||
uint256 hash(ParseHashV(request.params[0], "txid"));
|
||||
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
|
||||
@ -1716,6 +1720,8 @@ static UniValue gettransaction(const JSONRPCRequest& request)
|
||||
filter |= ISMINE_WATCH_ONLY;
|
||||
}
|
||||
|
||||
bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
|
||||
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
auto it = pwallet->mapWallet.find(hash);
|
||||
if (it == pwallet->mapWallet.end()) {
|
||||
@ -1741,6 +1747,12 @@ static UniValue gettransaction(const JSONRPCRequest& request)
|
||||
std::string strHex = EncodeHexTx(*wtx.tx);
|
||||
entry.pushKV("hex", strHex);
|
||||
|
||||
if (verbose) {
|
||||
UniValue decoded(UniValue::VOBJ);
|
||||
TxToUniv(*wtx.tx, uint256(), decoded, false);
|
||||
entry.pushKV("decoded", decoded);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
@ -1772,8 +1784,7 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
uint256 hash;
|
||||
hash.SetHex(request.params[0].get_str());
|
||||
uint256 hash(ParseHashV(request.params[0], "txid"));
|
||||
|
||||
if (!pwallet->mapWallet.count(hash)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
|
||||
@ -2176,17 +2187,13 @@ static UniValue lockunspent(const JSONRPCRequest& request)
|
||||
{"vout", UniValueType(UniValue::VNUM)},
|
||||
});
|
||||
|
||||
const std::string& txid = find_value(o, "txid").get_str();
|
||||
if (!IsHex(txid)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
|
||||
}
|
||||
|
||||
const uint256 txid(ParseHashO(o, "txid"));
|
||||
const int nOutput = find_value(o, "vout").get_int();
|
||||
if (nOutput < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
|
||||
}
|
||||
|
||||
const COutPoint outpt(uint256S(txid), nOutput);
|
||||
const COutPoint outpt(txid, nOutput);
|
||||
|
||||
const auto it = pwallet->mapWallet.find(outpt.hash);
|
||||
if (it == pwallet->mapWallet.end()) {
|
||||
@ -4099,7 +4106,7 @@ static const CRPCCommand commands[] =
|
||||
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {} },
|
||||
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf","addlocked"} },
|
||||
{ "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf","addlocked"} },
|
||||
{ "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} },
|
||||
{ "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","verbose"} },
|
||||
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
|
||||
{ "wallet", "getbalances", &getbalances, {} },
|
||||
{ "wallet", "getwalletinfo", &getwalletinfo, {} },
|
||||
|
@ -1326,4 +1326,52 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup)
|
||||
TestUnloadWallet(std::move(wallet));
|
||||
}
|
||||
|
||||
// Explicit calculation which is used to test the wallet constant
|
||||
// We get the same virtual size due to rounding(weight/4) for both use_max_sig values
|
||||
static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
|
||||
{
|
||||
// Generate ephemeral valid pubkey
|
||||
CKey key;
|
||||
key.MakeNewKey(true);
|
||||
CPubKey pubkey = key.GetPubKey();
|
||||
|
||||
// Generate pubkey hash
|
||||
uint160 key_hash(Hash160(pubkey.begin(), pubkey.end()));
|
||||
|
||||
// Create inner-script to enter into keystore. Key hash can't be 0...
|
||||
CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end());
|
||||
|
||||
// Create outer P2SH script for the output
|
||||
CScript script_pubkey = GetScriptForRawPubKey(pubkey);
|
||||
|
||||
NodeContext node;
|
||||
node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
|
||||
node.mempool = std::make_unique<CTxMemPool>(node.fee_estimator.get());
|
||||
auto chain = interfaces::MakeChain(node);
|
||||
CWallet wallet(chain.get(), "", CreateDummyWalletDatabase());
|
||||
AddKey(wallet, key);
|
||||
auto spk_man = wallet.GetLegacyScriptPubKeyMan();
|
||||
spk_man->AddCScript(inner_script);
|
||||
|
||||
// Fill in dummy signatures for fee calculation.
|
||||
SignatureData sig_data;
|
||||
|
||||
if (!ProduceSignature(*spk_man, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) {
|
||||
// We're hand-feeding it correct arguments; shouldn't happen
|
||||
assert(false);
|
||||
}
|
||||
|
||||
CTxIn tx_in;
|
||||
UpdateInput(tx_in, sig_data);
|
||||
return ::GetSerializeSize(tx_in, PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
static constexpr size_t DUMMY_NESTED_P2PKH_INPUT_SIZE = 113;
|
||||
BOOST_FIXTURE_TEST_CASE(dummy_input_size_test, TestChain100Setup)
|
||||
{
|
||||
std::cerr << "CDEF " << CalculateNestedKeyhashInputSize(false) << std::endl;
|
||||
BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(false), DUMMY_NESTED_P2PKH_INPUT_SIZE);
|
||||
BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(true), DUMMY_NESTED_P2PKH_INPUT_SIZE + 1);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -1786,8 +1786,6 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet,
|
||||
CMutableTransaction txn;
|
||||
txn.vin.push_back(CTxIn(COutPoint()));
|
||||
if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) {
|
||||
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
|
||||
// implies that we can sign for every input.
|
||||
return -1;
|
||||
}
|
||||
return ::GetSerializeSize(txn.vin[0], PROTOCOL_VERSION);
|
||||
@ -3404,7 +3402,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
|
||||
|
||||
FeeCalculation feeCalc;
|
||||
CFeeRate discard_rate = coin_control.m_discard_feerate ? *coin_control.m_discard_feerate : GetDiscardRate(*this);
|
||||
unsigned int nBytes{0};
|
||||
int nBytes{0};
|
||||
{
|
||||
std::vector<CInputCoin> vecCoins;
|
||||
LOCK(cs_wallet);
|
||||
|
@ -30,7 +30,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
|
||||
assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0)
|
||||
|
||||
# Test `prioritisetransaction` invalid `txid`
|
||||
assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0)
|
||||
assert_raises_rpc_error(-8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0)
|
||||
assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'Zd1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000')", self.nodes[0].prioritisetransaction, txid='Zd1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000', fee_delta=0)
|
||||
|
||||
txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000'
|
||||
|
@ -279,7 +279,7 @@ class CompactBlocksTest(BitcoinTestFramework):
|
||||
block_hash = int(node.generate(1)[0], 16)
|
||||
|
||||
# Store the raw block in our internal format.
|
||||
block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False))
|
||||
block = FromHex(CBlock(), node.getblock("%064x" % block_hash, False))
|
||||
for tx in block.vtx:
|
||||
tx.calc_sha256()
|
||||
block.rehash()
|
||||
|
@ -394,7 +394,7 @@ class SendHeadersTest(BitcoinTestFramework):
|
||||
|
||||
block_time += 9
|
||||
|
||||
fork_point = self.nodes[0].getblock("%02x" % new_block_hashes[0])["previousblockhash"]
|
||||
fork_point = self.nodes[0].getblock("%064x" % new_block_hashes[0])["previousblockhash"]
|
||||
fork_point = int(fork_point, 16)
|
||||
|
||||
# Use getblocks/getdata
|
||||
|
176
test/functional/p2p_tx_download.py
Executable file
176
test/functional/p2p_tx_download.py
Executable file
@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2019 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""
|
||||
Test transaction download behavior
|
||||
"""
|
||||
|
||||
from test_framework.messages import (
|
||||
CInv,
|
||||
CTransaction,
|
||||
FromHex,
|
||||
MSG_TX,
|
||||
MSG_TYPE_MASK,
|
||||
msg_inv,
|
||||
msg_notfound,
|
||||
)
|
||||
from test_framework.mininode import (
|
||||
P2PInterface,
|
||||
mininode_lock,
|
||||
)
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
wait_until,
|
||||
)
|
||||
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
|
||||
|
||||
|
||||
class TestP2PConn(P2PInterface):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.tx_getdata_count = 0
|
||||
|
||||
def on_getdata(self, message):
|
||||
for i in message.inv:
|
||||
if i.type & MSG_TYPE_MASK == MSG_TX:
|
||||
self.tx_getdata_count += 1
|
||||
|
||||
|
||||
# Constants from net_processing
|
||||
GETDATA_TX_INTERVAL = 60 # seconds
|
||||
MAX_GETDATA_RANDOM_DELAY = 2 # seconds
|
||||
INBOUND_PEER_TX_DELAY = 2 # seconds
|
||||
MAX_GETDATA_IN_FLIGHT = 100
|
||||
TX_EXPIRY_INTERVAL = GETDATA_TX_INTERVAL * 10
|
||||
|
||||
# Python test constants
|
||||
NUM_INBOUND = 10
|
||||
MAX_GETDATA_INBOUND_WAIT = GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY
|
||||
|
||||
|
||||
class TxDownloadTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = False
|
||||
self.num_nodes = 2
|
||||
|
||||
def test_tx_requests(self):
|
||||
self.log.info("Test that we request transactions from all our peers, eventually")
|
||||
|
||||
txid = 0xdeadbeef
|
||||
|
||||
self.log.info("Announce the txid from each incoming peer to node 0")
|
||||
msg = msg_inv([CInv(t=1, h=txid)])
|
||||
for p in self.nodes[0].p2ps:
|
||||
p.send_message(msg)
|
||||
p.sync_with_ping()
|
||||
|
||||
outstanding_peer_index = [i for i in range(len(self.nodes[0].p2ps))]
|
||||
|
||||
def getdata_found(peer_index):
|
||||
p = self.nodes[0].p2ps[peer_index]
|
||||
with mininode_lock:
|
||||
return p.last_message.get("getdata") and p.last_message["getdata"].inv[-1].hash == txid
|
||||
|
||||
while outstanding_peer_index:
|
||||
self.bump_mocktime(MAX_GETDATA_INBOUND_WAIT)
|
||||
wait_until(lambda: any(getdata_found(i) for i in outstanding_peer_index))
|
||||
for i in outstanding_peer_index:
|
||||
if getdata_found(i):
|
||||
outstanding_peer_index.remove(i)
|
||||
|
||||
self.log.info("All outstanding peers received a getdata")
|
||||
|
||||
def test_inv_block(self):
|
||||
self.log.info("Generate a transaction on node 0")
|
||||
tx = self.nodes[0].createrawtransaction(
|
||||
inputs=[{ # coinbase
|
||||
"txid": self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0],
|
||||
"vout": 0
|
||||
}],
|
||||
outputs={ADDRESS_BCRT1_UNSPENDABLE: 500 - 0.00025},
|
||||
)
|
||||
tx = self.nodes[0].signrawtransactionwithkey(
|
||||
hexstring=tx,
|
||||
privkeys=[self.nodes[0].get_deterministic_priv_key().key],
|
||||
)['hex']
|
||||
ctx = FromHex(CTransaction(), tx)
|
||||
txid = int(ctx.rehash(), 16)
|
||||
|
||||
self.log.info(
|
||||
"Announce the transaction to all nodes from all {} incoming peers, but never send it".format(NUM_INBOUND))
|
||||
msg = msg_inv([CInv(t=1, h=txid)])
|
||||
for p in self.peers:
|
||||
p.send_message(msg)
|
||||
p.sync_with_ping()
|
||||
self.bump_mocktime(1)
|
||||
|
||||
self.log.info("Put the tx in node 0's mempool")
|
||||
self.nodes[0].sendrawtransaction(tx)
|
||||
|
||||
# Since node 1 is connected outbound to an honest peer (node 0), it
|
||||
# should get the tx within a timeout. (Assuming that node 0
|
||||
# announced the tx within the timeout)
|
||||
# The timeout is the sum of
|
||||
# * the worst case until the tx is first requested from an inbound
|
||||
# peer, plus
|
||||
# * the first time it is re-requested from the outbound peer, plus
|
||||
# * 2 seconds to avoid races
|
||||
timeout = 2 + (MAX_GETDATA_RANDOM_DELAY + INBOUND_PEER_TX_DELAY) + (
|
||||
GETDATA_TX_INTERVAL + MAX_GETDATA_RANDOM_DELAY)
|
||||
self.log.info("Tx should be received at node 1 after {} seconds".format(timeout))
|
||||
self.sync_mempools(timeout=timeout)
|
||||
|
||||
def test_in_flight_max(self):
|
||||
self.log.info("Test that we don't request more than {} transactions from any peer, every {} minutes".format(
|
||||
MAX_GETDATA_IN_FLIGHT, TX_EXPIRY_INTERVAL / 60))
|
||||
txids = [i for i in range(MAX_GETDATA_IN_FLIGHT + 2)]
|
||||
|
||||
p = self.nodes[0].p2ps[0]
|
||||
|
||||
with mininode_lock:
|
||||
p.tx_getdata_count = 0
|
||||
|
||||
p.send_message(msg_inv([CInv(t=1, h=i) for i in txids]))
|
||||
|
||||
def wait_for_tx_getdata(target):
|
||||
self.bump_mocktime(1)
|
||||
return p.tx_getdata_count >= target
|
||||
|
||||
wait_until(lambda: wait_for_tx_getdata(MAX_GETDATA_IN_FLIGHT), lock=mininode_lock)
|
||||
|
||||
with mininode_lock:
|
||||
assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT)
|
||||
|
||||
self.log.info("Now check that if we send a NOTFOUND for a transaction, we'll get one more request")
|
||||
p.send_message(msg_notfound(vec=[CInv(t=1, h=txids[0])]))
|
||||
wait_until(lambda: wait_for_tx_getdata(MAX_GETDATA_IN_FLIGHT + 1), timeout=10, lock=mininode_lock)
|
||||
with mininode_lock:
|
||||
assert_equal(p.tx_getdata_count, MAX_GETDATA_IN_FLIGHT + 1)
|
||||
|
||||
WAIT_TIME = TX_EXPIRY_INTERVAL // 2 + TX_EXPIRY_INTERVAL
|
||||
self.log.info("if we wait about {} minutes, we should eventually get more requests".format(WAIT_TIME / 60))
|
||||
self.bump_mocktime(WAIT_TIME)
|
||||
wait_until(lambda: wait_for_tx_getdata(MAX_GETDATA_IN_FLIGHT + 2))
|
||||
|
||||
def run_test(self):
|
||||
# Setup the p2p connections
|
||||
self.peers = []
|
||||
for node in self.nodes:
|
||||
for i in range(NUM_INBOUND):
|
||||
self.peers.append(node.add_p2p_connection(TestP2PConn()))
|
||||
|
||||
self.log.info("Nodes are setup with {} incoming connections each".format(NUM_INBOUND))
|
||||
|
||||
# Test the in-flight max first, because we want no transactions in
|
||||
# flight ahead of this test.
|
||||
self.test_in_flight_max()
|
||||
|
||||
self.test_inv_block()
|
||||
|
||||
self.test_tx_requests()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
TxDownloadTest().main()
|
@ -142,7 +142,9 @@ class BlockchainTest(BitcoinTestFramework):
|
||||
|
||||
# Test `getchaintxstats` invalid `blockhash`
|
||||
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getchaintxstats, blockhash=0)
|
||||
assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0')
|
||||
assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 1, for '0')", self.nodes[0].getchaintxstats, blockhash='0')
|
||||
assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getchaintxstats, blockhash='ZZZ0000000000000000000000000000000000000000000000000000000000000')
|
||||
assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0000000000000000000000000000000000000000000000000000000000000000')
|
||||
blockhash = self.nodes[0].getblockhash(200)
|
||||
self.nodes[0].invalidateblock(blockhash)
|
||||
assert_raises_rpc_error(-8, "Block is not in main chain", self.nodes[0].getchaintxstats, blockhash=blockhash)
|
||||
@ -244,7 +246,9 @@ class BlockchainTest(BitcoinTestFramework):
|
||||
def _test_getblockheader(self):
|
||||
node = self.nodes[0]
|
||||
|
||||
assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "nonsense")
|
||||
assert_raises_rpc_error(-8, "hash must be of length 64 (not 8, for 'nonsense')", node.getblockheader, "nonsense")
|
||||
assert_raises_rpc_error(-8, "hash must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", node.getblockheader, "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844")
|
||||
assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "0cf7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844")
|
||||
|
||||
besthash = node.getbestblockhash()
|
||||
secondbesthash = node.getblockhash(199)
|
||||
|
@ -158,6 +158,10 @@ class PSBTTest(BitcoinTestFramework):
|
||||
self.nodes[0].generate(6)
|
||||
self.sync_all()
|
||||
|
||||
# Make sure change address wallet does not have P2SH innerscript access to results in success
|
||||
# when attempting BnB coin selection
|
||||
block_height = self.nodes[0].getblockcount()
|
||||
self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():self.nodes[0].listunspent()[0]["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)
|
||||
|
||||
# Regression test for 14473 (mishandling of already-signed witness transaction):
|
||||
unspent = self.nodes[0].listunspent()[0]
|
||||
|
@ -90,8 +90,9 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000'
|
||||
assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {})
|
||||
assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {})
|
||||
assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{}], {})
|
||||
assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {})
|
||||
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].createrawtransaction, [{}], {})
|
||||
assert_raises_rpc_error(-8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {})
|
||||
assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", self.nodes[0].createrawtransaction, [{'txid': 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844'}], {})
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {})
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {})
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, vout cannot be negative", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {})
|
||||
@ -171,9 +172,10 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
# We should not get the tx if we provide an unrelated block
|
||||
assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2)
|
||||
# An invalid block hash should raise the correct errors
|
||||
assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, True)
|
||||
assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, "foobar")
|
||||
assert_raises_rpc_error(-8, "parameter 3 must be of length 64", self.nodes[0].getrawtransaction, tx, True, "abcd1234")
|
||||
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getrawtransaction, tx, True, True)
|
||||
assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[0].getrawtransaction, tx, True, "foobar")
|
||||
assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[0].getrawtransaction, tx, True, "abcd1234")
|
||||
assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getrawtransaction, tx, True, "ZZZ0000000000000000000000000000000000000000000000000000000000000")
|
||||
assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000")
|
||||
# Undo the blocks and check in_active_chain
|
||||
self.nodes[0].invalidateblock(block1)
|
||||
|
@ -69,13 +69,19 @@ class MerkleBlockTest(BitcoinTestFramework):
|
||||
txid_spent = txin_spent["txid"]
|
||||
txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2
|
||||
|
||||
# Invalid txids
|
||||
assert_raises_rpc_error(-8, "txid must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[2].gettxoutproof, ["00000000000000000000000000000000"], blockhash)
|
||||
assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[2].gettxoutproof, ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], blockhash)
|
||||
# Invalid blockhashes
|
||||
assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
|
||||
assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[2].gettxoutproof, [txid_spent], "ZZZ0000000000000000000000000000000000000000000000000000000000000")
|
||||
# We can't find the block from a fully-spent tx
|
||||
# Doesn't apply to Dash Core - we have txindex always on
|
||||
# assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent])
|
||||
# We can get the proof if we specify the block
|
||||
assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent])
|
||||
# We can't get the proof if we specify a non-existent block
|
||||
assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000")
|
||||
assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "0000000000000000000000000000000000000000000000000000000000000000")
|
||||
# We can get the proof if the transaction is unspent
|
||||
assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent])
|
||||
# We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter.
|
||||
|
@ -103,6 +103,7 @@ BASE_SCRIPTS = [
|
||||
'p2p_timeouts.py',
|
||||
'feature_bip68_sequence.py',
|
||||
'mempool_updatefromblock.py',
|
||||
'p2p_tx_download.py',
|
||||
'wallet_dump.py',
|
||||
'wallet_listtransactions.py',
|
||||
'feature_multikeysporks.py',
|
||||
|
@ -121,9 +121,15 @@ class WalletTest(BitcoinTestFramework):
|
||||
assert_equal([unspent_0], self.nodes[2].listlockunspent())
|
||||
self.nodes[2].lockunspent(True, [unspent_0])
|
||||
assert_equal(len(self.nodes[2].listlockunspent()), 0)
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction",
|
||||
assert_raises_rpc_error(-8, "txid must be of length 64 (not 34, for '0000000000000000000000000000000000')",
|
||||
self.nodes[2].lockunspent, False,
|
||||
[{"txid": "0000000000000000000000000000000000", "vout": 0}])
|
||||
assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')",
|
||||
self.nodes[2].lockunspent, False,
|
||||
[{"txid": "ZZZ0000000000000000000000000000000000000000000000000000000000000", "vout": 0}])
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction",
|
||||
self.nodes[2].lockunspent, False,
|
||||
[{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0}])
|
||||
assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds",
|
||||
self.nodes[2].lockunspent, False,
|
||||
[{"txid": unspent_0["txid"], "vout": 999}])
|
||||
@ -490,6 +496,38 @@ class WalletTest(BitcoinTestFramework):
|
||||
self.nodes[0].setlabel(change, 'foobar')
|
||||
assert_equal(self.nodes[0].getaddressinfo(change)['ischange'], False)
|
||||
|
||||
# Test gettransaction response with different arguments.
|
||||
self.log.info("Testing gettransaction response with different arguments...")
|
||||
self.nodes[0].setlabel(change, 'baz')
|
||||
baz = self.nodes[0].listtransactions(label="baz", count=1)[0]
|
||||
expected_receive_vout = {"label": "baz",
|
||||
"address": baz["address"],
|
||||
"amount": baz["amount"],
|
||||
"category": baz["category"],
|
||||
"vout": baz["vout"]}
|
||||
expected_fields = frozenset({'amount', 'chainlock', 'confirmations', 'details', 'fee',
|
||||
'instantlock', 'instantlock_internal',
|
||||
'hex', 'timereceived', 'time', 'trusted', 'txid', 'walletconflicts'})
|
||||
|
||||
verbose_field = "decoded"
|
||||
expected_verbose_fields = expected_fields | {verbose_field}
|
||||
|
||||
self.log.debug("Testing gettransaction response without verbose")
|
||||
tx = self.nodes[0].gettransaction(txid=txid)
|
||||
assert_equal(set([*tx]), expected_fields)
|
||||
assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
|
||||
|
||||
self.log.debug("Testing gettransaction response with verbose set to False")
|
||||
tx = self.nodes[0].gettransaction(txid=txid, verbose=False)
|
||||
assert_equal(set([*tx]), expected_fields)
|
||||
assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
|
||||
|
||||
self.log.debug("Testing gettransaction response with verbose set to True")
|
||||
tx = self.nodes[0].gettransaction(txid=txid, verbose=True)
|
||||
assert_equal(set([*tx]), expected_verbose_fields)
|
||||
assert_array_result(tx["details"], {"category": "receive"}, expected_receive_vout)
|
||||
assert_equal(tx[verbose_field], self.nodes[0].decoderawtransaction(tx["hex"]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletTest().main()
|
||||
|
@ -66,8 +66,10 @@ class ListSinceBlockTest(BitcoinTestFramework):
|
||||
"42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4")
|
||||
assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
|
||||
"0000000000000000000000000000000000000000000000000000000000000000")
|
||||
assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock,
|
||||
assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 11, for 'invalid-hex')", self.nodes[0].listsinceblock,
|
||||
"invalid-hex")
|
||||
assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'Z000000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].listsinceblock,
|
||||
"Z000000000000000000000000000000000000000000000000000000000000000")
|
||||
|
||||
def test_reorg(self):
|
||||
'''
|
||||
|
Loading…
Reference in New Issue
Block a user