Merge #6114: backport: bitcoin#19801, #20556, #21141, #21235, #21331, #21343, #21424, #21691, #21777

4731f7045f docs: release notes for bitcoin#21141 - walletnotify %h %b (Konstantin Akimov)
044ddb4c80 Merge bitcoin/bitcoin#21777: test: Fix feature_notifications.py intermittent issue (MarcoFalke)
5336f42ea8 Merge bitcoin/bitcoin#19801: test: check for all possible OP_CLTV fail reasons in feature_cltv.py (BIP 65) (MarcoFalke)
1cc6aa6c83 Merge bitcoin/bitcoin#21691: test: Check that no versionbits are re-used (MarcoFalke)
b55fdf8e68 Merge #21235: p2p: Clarify disconnect log message in ProcessGetBlockData, remove send bool (MarcoFalke)
ddc6fca7f3 Merge #21343: doc: revamp macOS build doc (fanquake)
709652bff7 Merge #21141: wallet: Add new format string placeholders for walletnotify (Wladimir J. van der Laan)
a3bee9c8ec Merge #21424: Net processing: Tidy up CNodeState ctor (MarcoFalke)
aab2a665c3 Merge #20556: rpc: Properly document return values (submitblock, gettxout, getblocktemplate, scantxoutset) (fanquake)
c26722fc0e Merge #21331: rpc: replace wallet raw pointers with references (#18592 rebased) (MarcoFalke)

Pull request description:

  ## What was done?
  Backports from bitcoin v22:
   - bitcoin/bitcoin#21331
   - bitcoin/bitcoin#20556
   - bitcoin/bitcoin#21424
   - bitcoin/bitcoin#21141
   - bitcoin/bitcoin#21343
   - bitcoin/bitcoin#21235
   - bitcoin/bitcoin#21691
   - bitcoin/bitcoin#19801
   - bitcoin/bitcoin#21777

  ## How Has This Been Tested?
  Run unit and functional tests

  ## Breaking Changes
  N/A

  ## Checklist:
  - [ ] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  UdjinM6:
    utACK 4731f7045f
  PastaPastaPasta:
    utACK 4731f7045f

Tree-SHA512: dcee684563e4e07e838e96df0bf5e49d59e8c85aea6beca3239782e7d90a24f13d828b1201073585b888b0b154b4ba8bb90f88a36e3f923ac03b46f595d75500
This commit is contained in:
pasta 2024-07-20 11:26:28 -05:00
commit 5211886fb4
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
15 changed files with 762 additions and 557 deletions

View File

@ -1,51 +1,238 @@
# macOS Build Instructions and Notes # macOS Build Guide
**Updated for MacOS [11.2](https://www.apple.com/macos/big-sur/)**
This guide describes how to build dashd, command-line utilities, and GUI on macOS
**Note:** The following is for Intel Macs only!
## Dependencies
The following dependencies are **required**:
Library | Purpose | Description
-----------------------------------------------------------|------------|----------------------
[automake](https://formulae.brew.sh/formula/automake) | Build | Generate makefile
[libtool](https://formulae.brew.sh/formula/libtool) | Build | Shared library support
[pkg-config](https://formulae.brew.sh/formula/pkg-config) | Build | Configure compiler and linker flags
[boost](https://formulae.brew.sh/formula/boost) | Utility | Library for threading, data structures, etc
[libevent](https://formulae.brew.sh/formula/libevent) | Networking | OS independent asynchronous networking
The following dependencies are **optional**:
Library | Purpose | Description
--------------------------------------------------------------- |------------------|----------------------
[berkeley-db@4](https://formulae.brew.sh/formula/berkeley-db@4) | Berkeley DB | Wallet storage (only needed when wallet enabled)
[qt@5](https://formulae.brew.sh/formula/qt@5) | GUI | GUI toolkit (only needed when GUI enabled)
[qrencode](https://formulae.brew.sh/formula/qrencode) | QR codes in GUI | Generating QR codes (only needed when GUI enabled)
[zeromq](https://formulae.brew.sh/formula/zeromq) | ZMQ notification | Allows generating ZMQ notifications (requires ZMQ version >= 4.0.0)
[sqlite](https://formulae.brew.sh/formula/sqlite) | SQLite DB | Wallet storage (only needed when wallet enabled)
[miniupnpc](https://formulae.brew.sh/formula/miniupnpc) | UPnP Support | Firewall-jumping support (needed for port mapping support)
[libnatpmp](https://formulae.brew.sh/formula/libnatpmp) | NAT-PMP Support | Firewall-jumping support (needed for port mapping support)
[python3](https://formulae.brew.sh/formula/python@3.9) | Testing | Python Interpreter (only needed when running the test suite)
The following dependencies are **optional** packages required for deploying:
Library | Purpose | Description
----------------------------------------------------|------------------|----------------------
[librsvg](https://formulae.brew.sh/formula/librsvg) | Deploy Dependency| Library to render SVG files
[ds_store](https://pypi.org/project/ds-store/) | Deploy Dependency| Examine and modify .DS_Store files
[mac_alias](https://pypi.org/project/mac-alias/) | Deploy Dependency| Generate/Read binary alias and bookmark records
See [dependencies.md](dependencies.md) for a complete overview.
## Preparation
The commands in this guide should be executed in a Terminal application. The commands in this guide should be executed in a Terminal application.
The built-in one is located in macOS comes with a built-in Terminal located in:
``` ```
/Applications/Utilities/Terminal.app /Applications/Utilities/Terminal.app
``` ```
## Preparation ### 1. Xcode Command Line Tools
Install the macOS command line tools:
```shell The Xcode Command Line Tools are a collection of build tools for macOS.
These tools must be installed in order to build Dash Core from source.
To install, run the following command from your terminal:
``` bash
xcode-select --install xcode-select --install
``` ```
When the popup appears, click `Install`. Upon running the command, you should see a popup appear.
Click on `Install` to continue the installation process.
Then install [Homebrew](https://brew.sh). ### 2. Homebrew Package Manager
## Dependencies Homebrew is a package manager for macOS that allows one to install packages from the command line easily.
```shell While several package managers are available for macOS, this guide will focus on Homebrew as it is the most popular.
brew install automake libtool boost gmp miniupnpc pkg-config python qt@5 libevent libnatpmp qrencode Since the examples in this guide which walk through the installation of a package will use Homebrew, it is recommended that you install it to follow along.
Otherwise, you can adapt the commands to your package manager of choice.
To install the Homebrew package manager, see: https://brew.sh
Note: If you run into issues while installing Homebrew or pulling packages, refer to [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting).
### 3. Install Required Dependencies
The first step is to download the required dependencies.
These dependencies represent the packages required to get a barebones installation up and running.
To install, run the following from your terminal:
``` bash
brew install automake libtool boost gmp pkg-config libevent
``` ```
If you run into issues, check [Homebrew's troubleshooting page](https://docs.brew.sh/Troubleshooting). ### 4. Clone Dash repository
See [dependencies.md](dependencies.md) for a complete overview.
The wallet support requires one or both of the dependencies ([*SQLite*](#sqlite) and [*Berkeley DB*](#berkeley-db)) in the sections below. `git` should already be installed by default on your system.
To build Dash Core without wallet, see [*Disable-wallet mode*](#disable-wallet-mode). Now that all the required dependencies are installed, let's clone the Dash Core repository to a directory.
All build scripts and commands will run from this directory.
#### SQLite ``` bash
git clone https://github.com/dashpay/dash.git
```
Usually, macOS installation already has a suitable SQLite installation. ### 5. Install Optional Dependencies
Also, the Homebrew package could be installed:
```shell #### Wallet Dependencies
It is not necessary to build wallet functionality to run `dashd` or `dash-qt`.
To enable legacy wallets, you must install `berkeley-db@4`.
To enable [descriptor wallets](https://github.com/dashpay/dash/blob/master/doc/descriptors.md), `sqlite` is required.
Skip `berkeley-db@4` if you intend to *exclusively* use descriptor wallets.
###### Legacy Wallet Support
`berkeley-db@4` is required to enable support for legacy wallets.
Skip if you don't intend to use legacy wallets.
``` bash
brew install berkeley-db@4
```
###### Descriptor Wallet Support
Note: Apple has included a useable `sqlite` package since macOS 10.14.
You may not need to install this package.
`sqlite` is required to enable support for descriptor wallets.
Skip if you don't intend to use descriptor wallets.
``` bash
brew install sqlite brew install sqlite
``` ```
---
In that case the Homebrew package will prevail. #### GUI Dependencies
If you want to build the disk image with `make deploy` (.dmg / optional), you need: ###### Qt
[`macdeployqtplus`](../contrib/macdeploy/README.md) dependencies:
```shell Dash Core includes a GUI built with the cross-platform Qt Framework.
To compile the GUI, we need to install `qt@5`.
Skip if you don't intend to use the GUI.
``` bash
brew install qt@5
```
Note: Building with Qt binaries downloaded from the Qt website is not officially supported.
See the notes in [#7714](https://github.com/dashpay/dash/issues/7714).
###### qrencode
The GUI can encode addresses in a QR Code. To build in QR support for the GUI, install `qrencode`.
Skip if not using the GUI or don't want QR code functionality.
``` bash
brew install qrencode
```
---
#### Port Mapping Dependencies
###### miniupnpc
miniupnpc may be used for UPnP port mapping.
Skip if you do not need this functionality.
``` bash
brew install miniupnpc
```
###### libnatpmp
libnatpmp may be used for NAT-PMP port mapping.
Skip if you do not need this functionality.
``` bash
brew install libnatpmp
```
Note: UPnP and NAT-PMP support will be compiled in and disabled by default.
Check out the [further configuration](#further-configuration) section for more information.
---
#### ZMQ Dependencies
Support for ZMQ notifications requires the following dependency.
Skip if you do not need ZMQ functionality.
``` bash
brew install zeromq
```
ZMQ is automatically compiled in and enabled if the dependency is detected.
Check out the [further configuration](#further-configuration) section for more information.
For more information on ZMQ, see: [zmq.md](zmq.md)
---
#### Test Suite Dependencies
There is an included test suite that is useful for testing code changes when developing.
To run the test suite (recommended), you will need to have Python 3 installed:
``` bash
brew install python
```
---
#### Deploy Dependencies
You can deploy a `.dmg` containing the Dash Core application using `make deploy`.
This command depends on a couple of python packages, so it is required that you have `python` installed.
Ensuring that `python` is installed, you can install the deploy dependencies by running the following commands in your terminal:
``` bash
pip3 install ds_store mac_alias pip3 install ds_store mac_alias
``` ```
#### Berkeley DB ## Building Dash Core
### 1. Configuration
There are many ways to configure Dash Core, here are a few common examples:
##### Wallet (BDB + SQlite) Support, No GUI:
If `berkeley-db@4` is installed, then legacy wallet support will be built.
If `berkeley-db@4` is not installed, then this will throw an error.
If `sqlite` is installed, then descriptor wallet support will also be built.
Additionally, this explicitly disables the GUI.
``` bash
./autogen.sh
./configure --with-gui=no
```
###### Berkeley DB
It is recommended to use Berkeley DB 4.8. If you have to build it yourself, It is recommended to use Berkeley DB 4.8. If you have to build it yourself,
you can use [the installation script included in contrib/](contrib/install_db4.sh) you can use [the installation script included in contrib/](contrib/install_db4.sh)
@ -55,59 +242,68 @@ like so:
./contrib/install_db4.sh . ./contrib/install_db4.sh .
``` ```
from the root of the repository. ##### Wallet (only SQlite) and GUI Support:
Also, the Homebrew package could be installed: This explicitly enables the GUI and disables legacy wallet support.
If `qt` is not installed, this will throw an error.
If `sqlite` is installed then descriptor wallet functionality will be built.
If `sqlite` is not installed, then wallet functionality will be disabled.
```shell ``` bash
brew install berkeley-db4 ./autogen.sh
./configure --without-bdb --with-gui=yes
``` ```
## Build Dash Core ##### No Wallet or GUI
1. Clone the Dash Core source code: ``` bash
```shell ./autogen.sh
git clone https://github.com/dashpay/dash ./configure --without-wallet --with-gui=no
cd dash
```
2. Build Dash Core:
Configure and build the headless Dash Core binaries as well as the GUI (if Qt is found).
You can disable the GUI build by passing `--without-gui` to configure.
```shell
./autogen.sh
./configure
make
```
3. It is recommended to build and run the unit tests:
```shell
make check
```
4. You can also create a `.dmg` that contains the `.app` bundle (optional):
```shell
make deploy
```
## Disable-wallet mode
When the intention is to run only a P2P node without a wallet, Dash Core may be
compiled in disable-wallet mode with:
```shell
./configure --disable-wallet
``` ```
In this case there is no dependency on [*Berkeley DB*](#berkeley-db) and [*SQLite*](#sqlite). ##### Further Configuration
Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call. You may want to dig deeper into the configuration options to achieve your desired behavior.
Examine the output of the following command for a full list of configuration options:
## Running ``` bash
./configure -help
```
Dash Core is now available at `./src/dashd` ### 2. Compile
After configuration, you are ready to compile.
Run the following in your terminal to compile Dash Core:
``` bash
make -jx # use -jX here for parallelism
make check # Run tests if Python 3 is available
```
### 3. Deploy (optional)
You can also create a `.dmg` containing the `.app` bundle by running the following command:
``` bash
make deploy
```
## Running Dash Core
Dash Core should now be available at `./src/dashd`.
If you compiled support for the GUI, it should be available at `./src/qt/dash-qt`.
The first time you run `dashd` or `dash-qt`, it will start downloading the blockchain.
This process could take many hours, or even days on slower than average systems.
By default, blockchain and wallet data files will be stored in:
``` bash
/Users/${USER}/Library/Application Support/Dash/
```
Before running, you may create an empty configuration file: Before running, you may create an empty configuration file:
```shell ```shell
mkdir -p "/Users/${USER}/Library/Application Support/DashCore" mkdir -p "/Users/${USER}/Library/Application Support/DashCore"
@ -116,9 +312,8 @@ touch "/Users/${USER}/Library/Application Support/DashCore/dash.conf"
chmod 600 "/Users/${USER}/Library/Application Support/DashCore/dash.conf" chmod 600 "/Users/${USER}/Library/Application Support/DashCore/dash.conf"
``` ```
The first time you run dashd, it will start downloading the blockchain. This process could take many hours, or even days on slower than average systems.
You can monitor the download process by looking at the debug.log file: You can monitor the download process by looking at the debug.log file:
```shell ```shell
tail -f $HOME/Library/Application\ Support/DashCore/debug.log tail -f $HOME/Library/Application\ Support/DashCore/debug.log
``` ```
@ -126,13 +321,8 @@ tail -f $HOME/Library/Application\ Support/DashCore/debug.log
## Other commands: ## Other commands:
```shell ```shell
./src/dashd -daemon # Starts the dash daemon. ./src/dashd -daemon # Starts the dashd daemon.
./src/dash-cli --help # Outputs a list of command-line options. ./src/dash-cli --help # Outputs a list of command-line options.
./src/dash-cli help # Outputs a list of RPC commands when the daemon is running. ./src/dash-cli help # Outputs a list of RPC commands when the daemon is running.
./src/qt/dash-qt -server # Starts the dash-qt server mode, allows dash-cli control
``` ```
## Notes
* Tested on OS X 10.12 Sierra through macOS 10.15 Catalina on 64-bit Intel
processors only.
* Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714).

View File

@ -0,0 +1,6 @@
## Command-line options
### Changes in existing cmd-line options:
- `-walletnotify=<cmd>` has a new format options "%h" and "%b".
%b is replaced by the hash of the block including the transaction (set to 'unconfirmed' if the transaction is not included).
%h is replaced by the block height (-1 if not included).

View File

@ -826,7 +826,7 @@ struct CNodeState {
bool m_protect{false}; bool m_protect{false};
}; };
ChainSyncTimeoutState m_chain_sync{0, nullptr, false, false}; ChainSyncTimeoutState m_chain_sync;
//! Time of last new block announcement //! Time of last new block announcement
int64_t m_last_block_announcement{0}; int64_t m_last_block_announcement{0};
@ -900,11 +900,7 @@ struct CNodeState {
//! A rolling bloom filter of all announced tx CInvs to this peer. //! A rolling bloom filter of all announced tx CInvs to this peer.
CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001}; CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001};
CNodeState(bool is_inbound) : CNodeState(bool is_inbound) : m_is_inbound(is_inbound) {}
m_is_inbound(is_inbound)
{
m_recently_announced_invs.reset();
}
}; };
// Keeps track of the time (in microseconds) when transactions were requested last time // Keeps track of the time (in microseconds) when transactions were requested last time
@ -2385,7 +2381,6 @@ void PeerManagerImpl::RelayAddress(NodeId originator,
void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv, llmq::CInstantSendManager& isman) void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv, llmq::CInstantSendManager& isman)
{ {
bool send = false;
std::shared_ptr<const CBlock> a_recent_block; std::shared_ptr<const CBlock> a_recent_block;
std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block; std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block;
{ {
@ -2419,115 +2414,112 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
LOCK(cs_main); LOCK(cs_main);
const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash); const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(inv.hash);
if (pindex) { if (!pindex) {
send = BlockRequestAllowed(pindex); return;
if (!send) { }
LogPrint(BCLog::NET,"%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId()); if (!BlockRequestAllowed(pindex)) {
} LogPrint(BCLog::NET, "%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom.GetId());
return;
} }
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks // disconnect node in case we have reached the outbound limit for serving historical blocks
if (send && if (m_connman.OutboundTargetReached(true) &&
m_connman.OutboundTargetReached(true) &&
(((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) && (((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.IsMsgFilteredBlk()) &&
!pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target !pfrom.HasPermission(NetPermissionFlags::Download) // nodes with the download permission may exceed target
) { ) {
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId()); LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom.GetId());
//disconnect node
pfrom.fDisconnect = true; pfrom.fDisconnect = true;
send = false; return;
} }
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold // Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
if (send && !pfrom.HasPermission(NetPermissionFlags::NoBan) && ( if (!pfrom.HasPermission(NetPermissionFlags::NoBan) && (
(((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (m_chainman.ActiveChain().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) ) (((pfrom.GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom.GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (m_chainman.ActiveChain().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
)) { )) {
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom.GetId()); LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold, disconnect peer=%d\n", pfrom.GetId());
//disconnect node and prevent it from stalling (would otherwise wait for the missing block) //disconnect node and prevent it from stalling (would otherwise wait for the missing block)
pfrom.fDisconnect = true; pfrom.fDisconnect = true;
send = false; return;
} }
// Pruned nodes may have deleted the block, so check whether // Pruned nodes may have deleted the block, so check whether
// it's available before trying to send. // it's available before trying to send.
if (send && (pindex->nStatus & BLOCK_HAVE_DATA)) if (!(pindex->nStatus & BLOCK_HAVE_DATA)) {
{ return;
std::shared_ptr<const CBlock> pblock; }
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) { std::shared_ptr<const CBlock> pblock;
pblock = a_recent_block; if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
} else { pblock = a_recent_block;
// Send block from disk } else {
std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>(); // Send block from disk
if (!ReadBlockFromDisk(*pblockRead, pindex, m_chainparams.GetConsensus())) std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
assert(!"cannot load block from disk"); if (!ReadBlockFromDisk(*pblockRead, pindex, m_chainparams.GetConsensus()))
pblock = pblockRead; assert(!"cannot load block from disk");
} pblock = pblockRead;
if (pblock) { }
if (inv.IsMsgBlk()) { if (pblock) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); if (inv.IsMsgBlk()) {
} else if (inv.IsMsgFilteredBlk()) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
bool sendMerkleBlock = false; } else if (inv.IsMsgFilteredBlk()) {
CMerkleBlock merkleBlock; bool sendMerkleBlock = false;
if (!pfrom.IsBlockOnlyConn()) { CMerkleBlock merkleBlock;
LOCK(peer.m_tx_relay->m_bloom_filter_mutex); if (!pfrom.IsBlockOnlyConn()) {
if (peer.m_tx_relay->m_bloom_filter) { LOCK(peer.m_tx_relay->m_bloom_filter_mutex);
sendMerkleBlock = true; if (peer.m_tx_relay->m_bloom_filter) {
merkleBlock = CMerkleBlock(*pblock, *peer.m_tx_relay->m_bloom_filter); sendMerkleBlock = true;
merkleBlock = CMerkleBlock(*pblock, *peer.m_tx_relay->m_bloom_filter);
}
}
if (sendMerkleBlock) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock));
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
// Note that there is currently no way for a node to request any single transactions we didn't send here -
// they must either disconnect and retry or request the full block.
// Thus, the protocol spec specified allows for us to provide duplicate txn here,
// however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType;
for (PairType &pair : merkleBlock.vMatchedTxn) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::TX, *pblock->vtx[pair.first]));
}
for (PairType &pair : merkleBlock.vMatchedTxn) {
auto islock = isman.GetInstantSendLockByTxid(pair.second);
if (islock != nullptr) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::ISDLOCK, *islock));
} }
} }
if (sendMerkleBlock) { }
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); // else
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see // no response
// This avoids hurting performance by pointlessly requiring a round-trip } else if (inv.IsMsgCmpctBlk()) {
// Note that there is currently no way for a node to request any single transactions we didn't send here - // If a peer is asking for old blocks, we're almost guaranteed
// they must either disconnect and retry or request the full block. // they won't have a useful mempool to match against a compact block,
// Thus, the protocol spec specified allows for us to provide duplicate txn here, // and we don't feel like constructing the object for them, so
// however we MUST always provide at least what the remote peer needs // instead we respond with the full, non-compact block.
typedef std::pair<unsigned int, uint256> PairType; if (CanDirectFetch() &&
for (PairType &pair : merkleBlock.vMatchedTxn) { pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::TX, *pblock->vtx[pair.first])); if (a_recent_compact_block &&
} a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
for (PairType &pair : merkleBlock.vMatchedTxn) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
auto islock = isman.GetInstantSendLockByTxid(pair.second);
if (islock != nullptr) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::ISDLOCK, *islock));
}
}
}
// else
// no response
} else if (inv.IsMsgCmpctBlk()) {
// If a peer is asking for old blocks, we're almost guaranteed
// they won't have a useful mempool to match against a compact block,
// and we don't feel like constructing the object for them, so
// instead we respond with the full, non-compact block.
if (CanDirectFetch() &&
pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_CMPCTBLOCK_DEPTH) {
if (a_recent_compact_block &&
a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::CMPCTBLOCK, *a_recent_compact_block));
} else {
CBlockHeaderAndShortTxIDs cmpctblock{*pblock};
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::CMPCTBLOCK, cmpctblock));
}
} else { } else {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); CBlockHeaderAndShortTxIDs cmpctblock{*pblock};
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::CMPCTBLOCK, cmpctblock));
} }
} else {
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock));
} }
} }
}
{ {
LOCK(peer.m_block_inv_mutex); LOCK(peer.m_block_inv_mutex);
// Trigger the peer node to send a getblocks request for the next batch of inventory // Trigger the peer node to send a getblocks request for the next batch of inventory
if (inv.hash == peer.m_continuation_block) { if (inv.hash == peer.m_continuation_block) {
// Send immediately. This must send even if redundant, // Send immediately. This must send even if redundant,
// and we want it right after the last block so they don't // and we want it right after the last block so they don't
// wait for other stuff first. // wait for other stuff first.
std::vector<CInv> vInv; std::vector<CInv> vInv;
vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash())); vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash()));
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv)); m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
peer.m_continuation_block.SetNull(); peer.m_continuation_block.SetNull();
}
} }
} }
} }

View File

@ -1494,9 +1494,9 @@ static RPCHelpMan gettxout()
{"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"}, {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
{"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."}, {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
}, },
RPCResult{ {
RPCResult::Type::OBJ, "", "", RPCResult{"If the UTXO was not found", RPCResult::Type::NONE, "", ""},
{ RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"}, {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations"}, {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
{RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT}, {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
@ -1512,6 +1512,7 @@ static RPCHelpMan gettxout()
}}, }},
{RPCResult::Type::BOOL, "coinbase", "Coinbase or not"}, {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
}}, }},
},
RPCExamples{ RPCExamples{
"\nGet unspent transactions\n" "\nGet unspent transactions\n"
+ HelpExampleCli("listunspent", "") + + HelpExampleCli("listunspent", "") +
@ -2721,9 +2722,14 @@ static RPCHelpMan scantxoutset()
}, },
"[scanobjects,...]"}, "[scanobjects,...]"},
}, },
RPCResult{ {
RPCResult::Type::OBJ, "", "", RPCResult{"When action=='abort'", RPCResult::Type::BOOL, "", ""},
RPCResult{"When action=='status' and no scan is in progress", RPCResult::Type::NONE, "", ""},
RPCResult{"When action=='status' and scan is in progress", RPCResult::Type::OBJ, "", "",
{ {
{RPCResult::Type::NUM, "progress", "The scan progress"},
}},
RPCResult{"When action=='start'", RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::BOOL, "success", "Whether the scan was completed"}, {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"}, {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
{RPCResult::Type::NUM, "height", "The current block height (index)"}, {RPCResult::Type::NUM, "height", "The current block height (index)"},
@ -2742,6 +2748,7 @@ static RPCHelpMan scantxoutset()
}}, }},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT}, {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
}}, }},
},
RPCExamples{""}, RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {

View File

@ -214,15 +214,13 @@ static bool ValidatePlatformPort(const int32_t port)
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
template<typename SpecialTxPayload> template<typename SpecialTxPayload>
static void FundSpecialTx(CWallet* pwallet, CMutableTransaction& tx, const SpecialTxPayload& payload, const CTxDestination& fundDest) static void FundSpecialTx(CWallet& wallet, CMutableTransaction& tx, const SpecialTxPayload& payload, const CTxDestination& fundDest)
{ {
CHECK_NONFATAL(pwallet != nullptr);
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); wallet.BlockUntilSyncedToCurrentChain();
LOCK(pwallet->cs_wallet); LOCK(wallet.cs_wallet);
CTxDestination nodest = CNoDestination(); CTxDestination nodest = CNoDestination();
if (fundDest == nodest) { if (fundDest == nodest) {
@ -253,7 +251,7 @@ static void FundSpecialTx(CWallet* pwallet, CMutableTransaction& tx, const Speci
coinControl.fRequireAllInputs = false; coinControl.fRequireAllInputs = false;
std::vector<COutput> vecOutputs; std::vector<COutput> vecOutputs;
pwallet->AvailableCoins(vecOutputs); wallet.AvailableCoins(vecOutputs);
for (const auto& out : vecOutputs) { for (const auto& out : vecOutputs) {
CTxDestination txDest; CTxDestination txDest;
@ -272,7 +270,7 @@ static void FundSpecialTx(CWallet* pwallet, CMutableTransaction& tx, const Speci
bilingual_str strFailReason; bilingual_str strFailReason;
FeeCalculation fee_calc_out; FeeCalculation fee_calc_out;
if (!pwallet->CreateTransaction(vecSend, newTx, nFee, nChangePos, strFailReason, coinControl, fee_calc_out, false, tx.vExtraPayload.size())) { if (!wallet.CreateTransaction(vecSend, newTx, nFee, nChangePos, strFailReason, coinControl, fee_calc_out, false, tx.vExtraPayload.size())) {
throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason.original); throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason.original);
} }
@ -677,11 +675,11 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
const bool isEvoRequested = mnType == MnType::Evo; const bool isEvoRequested = mnType == MnType::Evo;
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
if (action == ProTxRegisterAction::External || action == ProTxRegisterAction::Fund) { if (action == ProTxRegisterAction::External || action == ProTxRegisterAction::Fund) {
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*pwallet);
} }
const bool isV19active{DeploymentActiveAfter(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();), Params().GetConsensus(), Consensus::DEPLOYMENT_V19)}; const bool isV19active{DeploymentActiveAfter(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();), Params().GetConsensus(), Consensus::DEPLOYMENT_V19)};
@ -796,7 +794,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
} }
if (action == ProTxRegisterAction::Fund) { if (action == ProTxRegisterAction::Fund) {
FundSpecialTx(wallet.get(), tx, ptx, fundDest); FundSpecialTx(*pwallet, tx, ptx, fundDest);
UpdateSpecialTxInputsHash(tx, ptx); UpdateSpecialTxInputsHash(tx, ptx);
CAmount fundCollateral = GetMnType(mnType).collat_amount; CAmount fundCollateral = GetMnType(mnType).collat_amount;
uint32_t collateralIndex = (uint32_t) -1; uint32_t collateralIndex = (uint32_t) -1;
@ -815,14 +813,14 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
// referencing external collateral // referencing external collateral
const bool unlockOnError = [&]() { const bool unlockOnError = [&]() {
if (LOCK(wallet->cs_wallet); !wallet->IsLockedCoin(ptx.collateralOutpoint.hash, ptx.collateralOutpoint.n)) { if (LOCK(pwallet->cs_wallet); !pwallet->IsLockedCoin(ptx.collateralOutpoint.hash, ptx.collateralOutpoint.n)) {
wallet->LockCoin(ptx.collateralOutpoint); pwallet->LockCoin(ptx.collateralOutpoint);
return true; return true;
} }
return false; return false;
}(); }();
try { try {
FundSpecialTx(wallet.get(), tx, ptx, fundDest); FundSpecialTx(*pwallet, tx, ptx, fundDest);
UpdateSpecialTxInputsHash(tx, ptx); UpdateSpecialTxInputsHash(tx, ptx);
Coin coin; Coin coin;
if (!GetUTXOCoin(chainman.ActiveChainstate(), ptx.collateralOutpoint, coin)) { if (!GetUTXOCoin(chainman.ActiveChainstate(), ptx.collateralOutpoint, coin)) {
@ -847,13 +845,13 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
return ret; return ret;
} else { } else {
{ {
LOCK(wallet->cs_wallet); LOCK(pwallet->cs_wallet);
// lets prove we own the collateral // lets prove we own the collateral
CScript scriptPubKey = GetScriptForDestination(txDest); CScript scriptPubKey = GetScriptForDestination(txDest);
std::unique_ptr<SigningProvider> provider = wallet->GetSolvingProvider(scriptPubKey); std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
std::string signed_payload; std::string signed_payload;
SigningResult err = wallet->SignMessage(ptx.MakeSignString(), *pkhash, signed_payload); SigningResult err = pwallet->SignMessage(ptx.MakeSignString(), *pkhash, signed_payload);
if (err == SigningResult::SIGNING_FAILED) { if (err == SigningResult::SIGNING_FAILED) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, SigningResultString(err)); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, SigningResultString(err));
} else if (err != SigningResult::OK){ } else if (err != SigningResult::OK){
@ -868,7 +866,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
} }
} catch (...) { } catch (...) {
if (unlockOnError) { if (unlockOnError) {
WITH_LOCK(wallet->cs_wallet, wallet->UnlockCoin(ptx.collateralOutpoint)); WITH_LOCK(pwallet->cs_wallet, pwallet->UnlockCoin(ptx.collateralOutpoint));
} }
throw; throw;
} }
@ -903,7 +901,7 @@ static RPCHelpMan protx_register_submit()
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!wallet) return NullUniValue;
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*wallet);
CMutableTransaction tx; CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str())) { if (!DecodeHexTx(tx, request.params[0].get_str())) {
@ -1020,7 +1018,7 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!wallet) return NullUniValue;
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*wallet);
const bool isV19active{DeploymentActiveAfter(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();), Params().GetConsensus(), Consensus::DEPLOYMENT_V19)}; const bool isV19active{DeploymentActiveAfter(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();), Params().GetConsensus(), Consensus::DEPLOYMENT_V19)};
const bool is_bls_legacy = !isV19active; const bool is_bls_legacy = !isV19active;
@ -1109,7 +1107,7 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques
} }
} }
FundSpecialTx(wallet.get(), tx, ptx, feeSource); FundSpecialTx(*wallet, tx, ptx, feeSource);
SignSpecialTxPayloadByHash(tx, ptx, keyOperator); SignSpecialTxPayloadByHash(tx, ptx, keyOperator);
SetTxPayload(tx, ptx); SetTxPayload(tx, ptx);
@ -1155,7 +1153,7 @@ static RPCHelpMan protx_update_registrar_wrapper(bool specific_legacy_bls_scheme
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!wallet) return NullUniValue;
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*wallet);
CProUpRegTx ptx; CProUpRegTx ptx;
ptx.proTxHash = ParseHashV(request.params[0], "proTxHash"); ptx.proTxHash = ParseHashV(request.params[0], "proTxHash");
@ -1217,7 +1215,7 @@ static RPCHelpMan protx_update_registrar_wrapper(bool specific_legacy_bls_scheme
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[4].get_str()); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[4].get_str());
} }
FundSpecialTx(wallet.get(), tx, ptx, feeSourceDest); FundSpecialTx(*wallet, tx, ptx, feeSourceDest);
SignSpecialTxPayloadByHash(tx, ptx, dmn->pdmnState->keyIDOwner, *wallet); SignSpecialTxPayloadByHash(tx, ptx, dmn->pdmnState->keyIDOwner, *wallet);
SetTxPayload(tx, ptx); SetTxPayload(tx, ptx);
@ -1268,10 +1266,10 @@ static RPCHelpMan protx_revoke()
CChainstateHelper& chain_helper = *node.chain_helper; CChainstateHelper& chain_helper = *node.chain_helper;
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*pwallet);
const bool isV19active{DeploymentActiveAfter(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();), Params().GetConsensus(), Consensus::DEPLOYMENT_V19)}; const bool isV19active{DeploymentActiveAfter(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip();), Params().GetConsensus(), Consensus::DEPLOYMENT_V19)};
const bool is_bls_legacy = !isV19active; const bool is_bls_legacy = !isV19active;
@ -1306,17 +1304,17 @@ static RPCHelpMan protx_revoke()
CTxDestination feeSourceDest = DecodeDestination(request.params[3].get_str()); CTxDestination feeSourceDest = DecodeDestination(request.params[3].get_str());
if (!IsValidDestination(feeSourceDest)) if (!IsValidDestination(feeSourceDest))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[3].get_str()); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[3].get_str());
FundSpecialTx(wallet.get(), tx, ptx, feeSourceDest); FundSpecialTx(*pwallet, tx, ptx, feeSourceDest);
} else if (dmn->pdmnState->scriptOperatorPayout != CScript()) { } else if (dmn->pdmnState->scriptOperatorPayout != CScript()) {
// Using funds from previousely specified operator payout address // Using funds from previousely specified operator payout address
CTxDestination txDest; CTxDestination txDest;
ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest);
FundSpecialTx(wallet.get(), tx, ptx, txDest); FundSpecialTx(*pwallet, tx, ptx, txDest);
} else if (dmn->pdmnState->scriptPayout != CScript()) { } else if (dmn->pdmnState->scriptPayout != CScript()) {
// Using funds from previousely specified masternode payout address // Using funds from previousely specified masternode payout address
CTxDestination txDest; CTxDestination txDest;
ExtractDestination(dmn->pdmnState->scriptPayout, txDest); ExtractDestination(dmn->pdmnState->scriptPayout, txDest);
FundSpecialTx(wallet.get(), tx, ptx, txDest); FundSpecialTx(*pwallet, tx, ptx, txDest);
} else { } else {
throw JSONRPCError(RPC_INTERNAL_ERROR, "No payout or fee source addresses found, can't revoke"); throw JSONRPCError(RPC_INTERNAL_ERROR, "No payout or fee source addresses found, can't revoke");
} }

View File

@ -144,7 +144,7 @@ static RPCHelpMan gobject_prepare()
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!wallet) return NullUniValue;
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*wallet);
// ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS // ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS
@ -253,7 +253,7 @@ static RPCHelpMan gobject_list_prepared()
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!wallet) return NullUniValue;
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*wallet);
int64_t nCount = request.params.empty() ? 10 : ParseInt64V(request.params[0], "count"); int64_t nCount = request.params.empty() ? 10 : ParseInt64V(request.params[0], "count");
if (nCount < 0) { if (nCount < 0) {
@ -546,7 +546,7 @@ static RPCHelpMan gobject_vote_many()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid vote outcome. Please use one of the following: 'yes', 'no' or 'abstain'"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid vote outcome. Please use one of the following: 'yes', 'no' or 'abstain'");
} }
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*wallet);
std::map<uint256, CKeyID> votingKeys; std::map<uint256, CKeyID> votingKeys;
@ -600,7 +600,7 @@ static RPCHelpMan gobject_vote_alias()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid vote outcome. Please use one of the following: 'yes', 'no' or 'abstain'"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid vote outcome. Please use one of the following: 'yes', 'no' or 'abstain'");
} }
EnsureWalletIsUnlocked(wallet.get()); EnsureWalletIsUnlocked(*wallet);
uint256 proTxHash(ParseHashV(request.params[3], "protx-hash")); uint256 proTxHash(ParseHashV(request.params[3], "protx-hash"));
auto dmn = node.dmnman->GetListAtChainTip().GetValidMN(proTxHash); auto dmn = node.dmnman->GetListAtChainTip().GetValidMN(proTxHash);

View File

@ -579,8 +579,10 @@ static RPCHelpMan getblocktemplate()
}, },
"\"template_request\""}, "\"template_request\""},
}, },
RPCResult{ {
RPCResult::Type::OBJ, "", "", RPCResult{"If the proposal was accepted with mode=='proposal'", RPCResult::Type::NONE, "", ""},
RPCResult{"If the proposal was not accepted with mode=='proposal'", RPCResult::Type::STR, "", "According to BIP22"},
RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "",
{ {
{RPCResult::Type::ARR, "capabilities", "specific client side supported features", {RPCResult::Type::ARR, "capabilities", "specific client side supported features",
{ {
@ -655,6 +657,7 @@ static RPCHelpMan getblocktemplate()
{RPCResult::Type::BOOL, "superblocks_enabled", "true, if superblock payments are enabled"}, {RPCResult::Type::BOOL, "superblocks_enabled", "true, if superblock payments are enabled"},
{RPCResult::Type::STR_HEX, "coinbase_payload", "coinbase transaction payload data encoded in hexadecimal"}, {RPCResult::Type::STR_HEX, "coinbase_payload", "coinbase transaction payload data encoded in hexadecimal"},
}}, }},
},
RPCExamples{ RPCExamples{
HelpExampleCli("getblocktemplate", "") HelpExampleCli("getblocktemplate", "")
+ HelpExampleRpc("getblocktemplate", "") + HelpExampleRpc("getblocktemplate", "")
@ -1019,7 +1022,10 @@ static RPCHelpMan submitblock()
{"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"}, {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"},
{"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."}, {"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."},
}, },
RPCResult{RPCResult::Type::NONE, "", "Returns JSON Null when valid, a string according to BIP22 otherwise"}, {
RPCResult{"If the block was accepted", RPCResult::Type::NONE, "", ""},
RPCResult{"Otherwise", RPCResult::Type::STR, "", "According to BIP22"},
},
RPCExamples{ RPCExamples{
HelpExampleCli("submitblock", "\"mydata\"") HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"") + HelpExampleRpc("submitblock", "\"mydata\"")

View File

@ -83,11 +83,9 @@ class VersionBitsTester
TestNeverActiveConditionChecker checker_never[CHECKERS]; TestNeverActiveConditionChecker checker_never[CHECKERS];
// Test counter (to identify failures) // Test counter (to identify failures)
int num; int num{1000};
public: public:
VersionBitsTester() : num(1000) {}
VersionBitsTester& Reset() { VersionBitsTester& Reset() {
// Have each group of tests be counted by the 1000s part, starting at 1000 // Have each group of tests be counted by the 1000s part, starting at 1000
num = num - (num % 1000) + 1000; num = num - (num % 1000) + 1000;
@ -259,39 +257,6 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
} }
} }
BOOST_AUTO_TEST_CASE(versionbits_sanity)
{
// Sanity checks of version bit deployments
const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
uint32_t bitmask = g_versionbitscache.Mask(mainnetParams, static_cast<Consensus::DeploymentPos>(i));
// Make sure that no deployment tries to set an invalid bit.
BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask);
// Check min_activation_height is on a retarget boundary
BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height % mainnetParams.nMinerConfirmationWindow, 0);
// Check min_activation_height is 0 for ALWAYS_ACTIVE and never active deployments
if (mainnetParams.vDeployments[i].nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE || mainnetParams.vDeployments[i].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) {
BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height, 0);
}
// Verify that the deployment windows of different deployment using the
// same bit are disjoint.
// This test may need modification at such time as a new deployment
// is proposed that reuses the bit of an activated soft fork, before the
// end time of that soft fork. (Alternatively, the end time of that
// activated soft fork could be later changed to be earlier to avoid
// overlap.)
for (int j=i+1; j<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; j++) {
if (g_versionbitscache.Mask(mainnetParams, static_cast<Consensus::DeploymentPos>(j)) == bitmask) {
BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > mainnetParams.vDeployments[i].nTimeout ||
mainnetParams.vDeployments[i].nStartTime > mainnetParams.vDeployments[j].nTimeout);
}
}
}
}
/** Check that ComputeBlockVersion will set the appropriate bit correctly */ /** Check that ComputeBlockVersion will set the appropriate bit correctly */
static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep) static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep)
{ {
@ -313,16 +278,25 @@ static void check_computeblockversion(const Consensus::Params& params, Consensus
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS); BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS);
// always/never active deployments shouldn't need to be tested further // always/never active deployments shouldn't need to be tested further
if (nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE) return; if (nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE ||
if (nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return; nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE)
{
BOOST_CHECK_EQUAL(min_activation_height, 0);
return;
}
BOOST_REQUIRE(nStartTime < nTimeout); BOOST_REQUIRE(nStartTime < nTimeout);
BOOST_REQUIRE(nStartTime >= 0); BOOST_REQUIRE(nStartTime >= 0);
BOOST_REQUIRE(nTimeout <= std::numeric_limits<uint32_t>::max() || nTimeout == Consensus::BIP9Deployment::NO_TIMEOUT); BOOST_REQUIRE(nTimeout <= std::numeric_limits<uint32_t>::max() || nTimeout == Consensus::BIP9Deployment::NO_TIMEOUT);
BOOST_REQUIRE(0 <= bit && bit < 32); BOOST_REQUIRE(0 <= bit && bit < 32);
// Make sure that no deployment tries to set an invalid bit.
BOOST_REQUIRE(((1 << bit) & VERSIONBITS_TOP_MASK) == 0); BOOST_REQUIRE(((1 << bit) & VERSIONBITS_TOP_MASK) == 0);
BOOST_REQUIRE(min_activation_height >= 0); BOOST_REQUIRE(min_activation_height >= 0);
BOOST_REQUIRE_EQUAL(min_activation_height % window_size, 0); // Check min_activation_height is on a retarget boundary
BOOST_REQUIRE_EQUAL(min_activation_height % window_size, 0U);
const uint32_t bitmask{g_versionbitscache.Mask(params, dep)};
BOOST_CHECK_EQUAL(bitmask, uint32_t{1} << bit);
// In the first chain, test that the bit is set by CBV until it has failed. // In the first chain, test that the bit is set by CBV until it has failed.
// In the second chain, test the bit is set by CBV while STARTED and // In the second chain, test the bit is set by CBV while STARTED and
@ -453,8 +427,19 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
if (chain_name == CBaseChainParams::DEVNET) gArgs.SoftSetBoolArg("-devnet", true); if (chain_name == CBaseChainParams::DEVNET) gArgs.SoftSetBoolArg("-devnet", true);
const auto chainParams = CreateChainParams(*m_node.args, chain_name); const auto chainParams = CreateChainParams(*m_node.args, chain_name);
if (chain_name == CBaseChainParams::DEVNET) gArgs.ForceRemoveArg("devnet"); if (chain_name == CBaseChainParams::DEVNET) gArgs.ForceRemoveArg("devnet");
uint32_t chain_all_vbits{0};
for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) { for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) {
check_computeblockversion(chainParams->GetConsensus(), static_cast<Consensus::DeploymentPos>(i)); const auto dep = static_cast<Consensus::DeploymentPos>(i);
// Check that no bits are re-used (within the same chain). This is
// disallowed because the transition to FAILED (on timeout) does
// not take precedence over STARTED/LOCKED_IN. So all softforks on
// the same bit might overlap, even when non-overlapping start-end
// times are picked.
const uint32_t dep_mask{g_versionbitscache.Mask(chainParams->GetConsensus(), dep)};
BOOST_CHECK(!(chain_all_vbits & dep_mask));
chain_all_vbits |= dep_mask;
check_computeblockversion(chainParams->GetConsensus(), dep);
} }
} }

View File

@ -69,7 +69,7 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET); argsman.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
#if HAVE_SYSTEM #if HAVE_SYSTEM
argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); argsman.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID, %w is replaced by wallet name, %b is replaced by the hash of the block including the transaction (set to 'unconfirmed' if the transaction is not included) and %h is replaced by the block height (-1 if not included). %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif #endif
argsman.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " argsman.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "

View File

@ -96,15 +96,14 @@ RPCHelpMan importprivkey()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled"); throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
} }
EnsureLegacyScriptPubKeyMan(*wallet, true); EnsureLegacyScriptPubKeyMan(*pwallet, true);
WalletBatch batch(pwallet->GetDatabase()); WalletBatch batch(pwallet->GetDatabase());
WalletRescanReserver reserver(*pwallet); WalletRescanReserver reserver(*pwallet);
@ -112,7 +111,7 @@ RPCHelpMan importprivkey()
{ {
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
std::string strSecret = request.params[0].get_str(); std::string strSecret = request.params[0].get_str();
std::string strLabel = ""; std::string strLabel = "";
@ -177,9 +176,8 @@ RPCHelpMan abortrescan()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false; if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false;
pwallet->AbortRescan(); pwallet->AbortRescan();
@ -215,9 +213,9 @@ RPCHelpMan importaddress()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureLegacyScriptPubKeyMan(*pwallet, true); EnsureLegacyScriptPubKeyMan(*pwallet, true);
std::string strLabel; std::string strLabel;
@ -300,9 +298,8 @@ RPCHelpMan importprunedfunds()
RPCExamples{""}, RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
CMutableTransaction tx; CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str())) { if (!DecodeHexTx(tx, request.params[0].get_str())) {
@ -362,9 +359,8 @@ RPCHelpMan removeprunedfunds()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -409,10 +405,10 @@ RPCHelpMan importpubkey()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureLegacyScriptPubKeyMan(*wallet, true); EnsureLegacyScriptPubKeyMan(*pwallet, true);
std::string strLabel; std::string strLabel;
if (!request.params[1].isNull()) if (!request.params[1].isNull())
@ -488,10 +484,10 @@ RPCHelpMan importwallet()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureLegacyScriptPubKeyMan(*wallet, true); EnsureLegacyScriptPubKeyMan(*pwallet, true);
if (pwallet->chain().havePruned()) { if (pwallet->chain().havePruned()) {
// Exit early and print an error. // Exit early and print an error.
@ -511,7 +507,7 @@ RPCHelpMan importwallet()
{ {
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
fsbridge::ifstream file; fsbridge::ifstream file;
file.open(request.params[0].get_str(), std::ios::in | std::ios::ate); file.open(request.params[0].get_str(), std::ios::in | std::ios::ate);
@ -650,9 +646,8 @@ RPCHelpMan importelectrumwallet()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (pwallet->chain().havePruned()) if (pwallet->chain().havePruned())
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode"); throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
@ -661,11 +656,11 @@ RPCHelpMan importelectrumwallet()
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
} }
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet); LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore); LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
fsbridge::ifstream file; fsbridge::ifstream file;
std::string strFileName = request.params[0].get_str(); std::string strFileName = request.params[0].get_str();
@ -828,15 +823,14 @@ RPCHelpMan dumpprivkey()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet); LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore); LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
std::string strAddress = request.params[0].get_str(); std::string strAddress = request.params[0].get_str();
CTxDestination dest = DecodeDestination(strAddress); CTxDestination dest = DecodeDestination(strAddress);
@ -875,15 +869,14 @@ RPCHelpMan dumphdinfo()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*wallet); LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
CHDChain hdChainCurrent; CHDChain hdChainCurrent;
if (!spk_man.GetHDChain(hdChainCurrent)) if (!spk_man.GetHDChain(hdChainCurrent))
throw JSONRPCError(RPC_WALLET_ERROR, "This wallet is not a HD wallet."); throw JSONRPCError(RPC_WALLET_ERROR, "This wallet is not a HD wallet.");
@ -941,7 +934,7 @@ RPCHelpMan dumpwallet()
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
EnsureWalletIsUnlocked(&wallet); EnsureWalletIsUnlocked(wallet);
fs::path filepath = request.params[0].get_str(); fs::path filepath = request.params[0].get_str();
filepath = fs::absolute(filepath); filepath = fs::absolute(filepath);
@ -1364,7 +1357,7 @@ static UniValue ProcessImportDescriptor(ImportData& import_data, std::map<CKeyID
return warnings; return warnings;
} }
static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) static UniValue ProcessImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{ {
UniValue warnings(UniValue::VARR); UniValue warnings(UniValue::VARR);
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
@ -1379,7 +1372,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false; const bool add_keypool = data.exists("keypool") ? data["keypool"].get_bool() : false;
// Add to keypool only works with privkeys disabled // Add to keypool only works with privkeys disabled
if (add_keypool && !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { if (add_keypool && !wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Keys can only be imported to the keypool when private keys are disabled"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Keys can only be imported to the keypool when private keys are disabled");
} }
@ -1401,29 +1394,29 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
} }
// If private keys are disabled, abort if private keys are being imported // If private keys are disabled, abort if private keys are being imported
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) { if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled"); throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
} }
// Check whether we have any work to do // Check whether we have any work to do
for (const CScript& script : script_pub_keys) { for (const CScript& script : script_pub_keys) {
if (pwallet->IsMine(script) & ISMINE_SPENDABLE) { if (wallet.IsMine(script) & ISMINE_SPENDABLE) {
throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script) + "\")"); throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script) + "\")");
} }
} }
// All good, time to import // All good, time to import
pwallet->MarkDirty(); wallet.MarkDirty();
if (!pwallet->ImportScripts(import_data.import_scripts, timestamp)) { if (!wallet.ImportScripts(import_data.import_scripts, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error adding script to wallet");
} }
if (!pwallet->ImportPrivKeys(privkey_map, timestamp)) { if (!wallet.ImportPrivKeys(privkey_map, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error adding key to wallet");
} }
if (!pwallet->ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) { if (!wallet.ImportPubKeys(ordered_pubkeys, pubkey_map, import_data.key_origins, add_keypool, internal, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
} }
if (!pwallet->ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) { if (!wallet.ImportScriptPubKeys(label, script_pub_keys, have_solving_data, !internal, timestamp)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
} }
@ -1530,17 +1523,14 @@ RPCHelpMan importmulti()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& mainRequest) -> UniValue
{ {
RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ}); RPCTypeCheck(mainRequest.params, {UniValue::VARR, UniValue::VOBJ});
const UniValue& requests = mainRequest.params[0]; const UniValue& requests = mainRequest.params[0];
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(mainRequest);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
EnsureLegacyScriptPubKeyMan(*wallet, true); EnsureLegacyScriptPubKeyMan(*pwallet, true);
//Default options //Default options
bool fRescan = true; bool fRescan = true;
@ -1564,7 +1554,7 @@ RPCHelpMan importmulti()
UniValue response(UniValue::VARR); UniValue response(UniValue::VARR);
{ {
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
// Verify all timestamps are present before importing any keys. // Verify all timestamps are present before importing any keys.
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now))); CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
@ -1576,7 +1566,7 @@ RPCHelpMan importmulti()
for (const UniValue& data : requests.getValues()) { for (const UniValue& data : requests.getValues()) {
const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp); const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
const UniValue result = ProcessImport(pwallet, data, timestamp); const UniValue result = ProcessImport(*pwallet, data, timestamp);
response.push_back(result); response.push_back(result);
if (!fRescan) { if (!fRescan) {
@ -1643,7 +1633,7 @@ RPCHelpMan importmulti()
}; };
} }
static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{ {
UniValue warnings(UniValue::VARR); UniValue warnings(UniValue::VARR);
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
@ -1712,7 +1702,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
} }
// If the wallet disabled private keys, abort if private keys exist // If the wallet disabled private keys, abort if private keys exist
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) { if (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !keys.keys.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled"); throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import private keys to a wallet with private keys disabled");
} }
@ -1736,7 +1726,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
} }
// If private keys are enabled, check some things. // If private keys are enabled, check some things.
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (keys.keys.empty()) { if (keys.keys.empty()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled"); throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import descriptor without private keys to a wallet with private keys enabled");
} }
@ -1748,7 +1738,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index); WalletDescriptor w_desc(std::move(parsed_desc), timestamp, range_start, range_end, next_index);
// Check if the wallet already contains the descriptor // Check if the wallet already contains the descriptor
auto existing_spk_manager = pwallet->GetDescriptorScriptPubKeyMan(w_desc); auto existing_spk_manager = wallet.GetDescriptorScriptPubKeyMan(w_desc);
if (existing_spk_manager) { if (existing_spk_manager) {
if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) { if (!existing_spk_manager->CanUpdateToWalletDescriptor(w_desc, error)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, error); throw JSONRPCError(RPC_INVALID_PARAMETER, error);
@ -1756,7 +1746,7 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
} }
// Add descriptor to the wallet // Add descriptor to the wallet
auto spk_manager = pwallet->AddWalletDescriptor(w_desc, keys, label, internal); auto spk_manager = wallet.AddWalletDescriptor(w_desc, keys, label, internal);
if (spk_manager == nullptr) { if (spk_manager == nullptr) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor)); throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Could not add descriptor '%s'", descriptor));
} }
@ -1766,11 +1756,11 @@ static UniValue ProcessDescriptorImport(CWallet * const pwallet, const UniValue&
if (!w_desc.descriptor->GetOutputType()) { if (!w_desc.descriptor->GetOutputType()) {
warnings.push_back("Unknown output type, cannot set descriptor to active."); warnings.push_back("Unknown output type, cannot set descriptor to active.");
} else { } else {
pwallet->AddActiveScriptPubKeyMan(spk_manager->GetID(), internal); wallet.AddActiveScriptPubKeyMan(spk_manager->GetID(), internal);
} }
} else { } else {
if (w_desc.descriptor->GetOutputType()) { if (w_desc.descriptor->GetOutputType()) {
pwallet->DeactivateScriptPubKeyMan(spk_manager->GetID(), internal); wallet.DeactivateScriptPubKeyMan(spk_manager->GetID(), internal);
} }
} }
@ -1836,10 +1826,8 @@ RPCHelpMan importdescriptors() {
[&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& main_request) -> UniValue
{ {
// Acquire the wallet // Acquire the wallet
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(main_request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(main_request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
// Make sure wallet is a descriptor wallet // Make sure wallet is a descriptor wallet
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
@ -1861,7 +1849,7 @@ RPCHelpMan importdescriptors() {
UniValue response(UniValue::VARR); UniValue response(UniValue::VARR);
{ {
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now))); CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now)));
@ -1869,7 +1857,7 @@ RPCHelpMan importdescriptors() {
for (const UniValue& request : requests.getValues()) { for (const UniValue& request : requests.getValues()) {
// This throws an error if "timestamp" doesn't exist // This throws an error if "timestamp" doesn't exist
const int64_t timestamp = std::max(GetImportTimestamp(request, now), minimum_timestamp); const int64_t timestamp = std::max(GetImportTimestamp(request, now), minimum_timestamp);
const UniValue result = ProcessDescriptorImport(pwallet, request, timestamp); const UniValue result = ProcessDescriptorImport(*pwallet, request, timestamp);
response.push_back(result); response.push_back(result);
if (lowest_timestamp > timestamp ) { if (lowest_timestamp > timestamp ) {

View File

@ -54,8 +54,8 @@ static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static const uint32_t WALLET_DASH_KB_TO_DUFF_B = COIN / 1000; // 1 duff / B = 0.00001 DASH / kB static const uint32_t WALLET_DASH_KB_TO_DUFF_B = COIN / 1000; // 1 duff / B = 0.00001 DASH / kB
static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) { static inline bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool(); bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
if (avoid_reuse && !can_avoid_reuse) { if (avoid_reuse && !can_avoid_reuse) {
@ -70,11 +70,11 @@ static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValu
* We default to true for watchonly wallets if include_watchonly isn't * We default to true for watchonly wallets if include_watchonly isn't
* explicitly set. * explicitly set.
*/ */
static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& pwallet) static bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
{ {
if (include_watchonly.isNull()) { if (include_watchonly.isNull()) {
// if include_watchonly isn't explicitly set, then check if we have a watchonly wallet // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
return pwallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
} }
// otherwise return whatever include_watchonly was set to // otherwise return whatever include_watchonly was set to
@ -124,9 +124,9 @@ std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& reques
"Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path)."); "Wallet file not specified (must request wallet RPC through /wallet/<filename> uri-path).");
} }
void EnsureWalletIsUnlocked(const CWallet* pwallet) void EnsureWalletIsUnlocked(const CWallet& wallet)
{ {
if (pwallet->IsLocked()) { if (wallet.IsLocked()) {
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
} }
} }
@ -203,13 +203,13 @@ static std::string LabelFromValue(const UniValue& value)
/** /**
* Update coin control with fee estimation based on the given parameters * Update coin control with fee estimation based on the given parameters
* *
* @param[in] pwallet Wallet pointer * @param[in] wallet Wallet reference
* @param[in,out] cc Coin control which is to be updated * @param[in,out] cc Coin control which is to be updated
* @param[in] estimate_mode String value (e.g. "ECONOMICAL") * @param[in] estimate_mode String value (e.g. "ECONOMICAL")
* @param[in] estimate_param Parameter (blocks to confirm, explicit fee rate, etc) * @param[in] estimate_param Parameter (blocks to confirm, explicit fee rate, etc)
* @throws a JSONRPCError if estimate_mode is unknown, or if estimate_param is missing when required * @throws a JSONRPCError if estimate_mode is unknown, or if estimate_param is missing when required
*/ */
static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const UniValue& estimate_mode, const UniValue& estimate_param) static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const UniValue& estimate_mode, const UniValue& estimate_param)
{ {
if (!estimate_mode.isNull()) { if (!estimate_mode.isNull()) {
if (!FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) { if (!FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
@ -230,7 +230,7 @@ static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const U
cc.m_feerate = CFeeRate(fee_rate); cc.m_feerate = CFeeRate(fee_rate);
} else if (!estimate_param.isNull()) { } else if (!estimate_param.isNull()) {
cc.m_confirm_target = ParseConfirmTarget(estimate_param, pwallet->chain().estimateMaxBlocks()); cc.m_confirm_target = ParseConfirmTarget(estimate_param, wallet.chain().estimateMaxBlocks());
} }
} }
@ -252,9 +252,8 @@ RPCHelpMan getnewaddress()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -292,9 +291,8 @@ RPCHelpMan getrawchangeaddress()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -328,9 +326,8 @@ static RPCHelpMan setlabel()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -382,9 +379,9 @@ void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_f
} }
} }
UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose) UniValue SendMoney(CWallet& wallet, const CCoinControl &coin_control, std::vector<CRecipient> &recipients, mapValue_t map_value, bool verbose)
{ {
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(wallet);
if (coin_control.IsUsingCoinJoin()) { if (coin_control.IsUsingCoinJoin()) {
map_value["DS"] = "1"; map_value["DS"] = "1";
@ -396,11 +393,11 @@ UniValue SendMoney(CWallet* const pwallet, const CCoinControl &coin_control, std
bilingual_str error; bilingual_str error;
CTransactionRef tx; CTransactionRef tx;
FeeCalculation fee_calc_out; FeeCalculation fee_calc_out;
bool fCreated = pwallet->CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); bool fCreated = wallet.CreateTransaction(recipients, tx, nFeeRequired, nChangePosRet, error, coin_control, fee_calc_out, !wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
if (!fCreated) { if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original); throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, error.original);
} }
pwallet->CommitTransaction(tx, std::move(map_value), {} /* orderForm */); wallet.CommitTransaction(tx, std::move(map_value), {} /* orderForm */);
if (verbose) { if (verbose) {
UniValue entry(UniValue::VOBJ); UniValue entry(UniValue::VOBJ);
entry.pushKV("txid", tx->GetHash().GetHex()); entry.pushKV("txid", tx->GetHash().GetHex());
@ -456,9 +453,8 @@ static RPCHelpMan sendtoaddress()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -484,14 +480,13 @@ static RPCHelpMan sendtoaddress()
coin_control.UseCoinJoin(request.params[6].get_bool()); coin_control.UseCoinJoin(request.params[6].get_bool());
} }
coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(*pwallet, request.params[9]);
coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(pwallet, request.params[9]);
// We also enable partial spend avoidance if reuse avoidance is set. // We also enable partial spend avoidance if reuse avoidance is set.
coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse; coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
SetFeeEstimateMode(pwallet, coin_control, request.params[8], request.params[7]); SetFeeEstimateMode(*pwallet, coin_control, request.params[8], request.params[7]);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
UniValue address_amounts(UniValue::VOBJ); UniValue address_amounts(UniValue::VOBJ);
const std::string address = request.params[0].get_str(); const std::string address = request.params[0].get_str();
@ -505,7 +500,7 @@ static RPCHelpMan sendtoaddress()
ParseRecipients(address_amounts, subtractFeeFromAmount, recipients); ParseRecipients(address_amounts, subtractFeeFromAmount, recipients);
bool verbose = request.params[10].isNull() ? false: request.params[10].get_bool(); bool verbose = request.params[10].isNull() ? false: request.params[10].get_bool();
return SendMoney(pwallet, coin_control, recipients, mapValue, verbose); return SendMoney(*pwallet, coin_control, recipients, mapValue, verbose);
}, },
}; };
} }
@ -554,9 +549,8 @@ static RPCHelpMan listaddressgroupings()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -609,9 +603,8 @@ static RPCHelpMan listaddressbalances()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -657,13 +650,12 @@ static RPCHelpMan signmessage()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
std::string strAddress = request.params[0].get_str(); std::string strAddress = request.params[0].get_str();
std::string strMessage = request.params[1].get_str(); std::string strMessage = request.params[1].get_str();
@ -763,9 +755,8 @@ static RPCHelpMan getreceivedbyaddress()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -803,9 +794,8 @@ static RPCHelpMan getreceivedbylabel()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -845,9 +835,8 @@ static RPCHelpMan getbalance()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -873,7 +862,7 @@ static RPCHelpMan getbalance()
bool include_watchonly = ParseIncludeWatchonly(request.params[3], *pwallet); bool include_watchonly = ParseIncludeWatchonly(request.params[3], *pwallet);
bool avoid_reuse = GetAvoidReuseFlag(pwallet, request.params[4]); bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[4]);
const auto bal = pwallet->GetBalance(min_depth, avoid_reuse, fAddLocked); const auto bal = pwallet->GetBalance(min_depth, avoid_reuse, fAddLocked);
return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0)); return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
@ -890,10 +879,8 @@ static RPCHelpMan getunconfirmedbalance()
RPCExamples{""}, RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); if (!pwallet) return NullUniValue;
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -961,9 +948,8 @@ static RPCHelpMan sendmany()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -991,13 +977,13 @@ static RPCHelpMan sendmany()
coin_control.UseCoinJoin(request.params[7].get_bool()); coin_control.UseCoinJoin(request.params[7].get_bool());
} }
SetFeeEstimateMode(pwallet, coin_control, request.params[9], request.params[8]); SetFeeEstimateMode(*pwallet, coin_control, request.params[9], request.params[8]);
std::vector<CRecipient> recipients; std::vector<CRecipient> recipients;
ParseRecipients(sendTo, subtractFeeFromAmount, recipients); ParseRecipients(sendTo, subtractFeeFromAmount, recipients);
bool verbose = request.params[10].isNull() ? false : request.params[10].get_bool(); bool verbose = request.params[10].isNull() ? false : request.params[10].get_bool();
return SendMoney(pwallet, coin_control, recipients, std::move(mapValue), verbose); return SendMoney(*pwallet, coin_control, recipients, std::move(mapValue), verbose);
}, },
}; };
} }
@ -1035,9 +1021,8 @@ RPCHelpMan addmultisigaddress()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet); LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet);
@ -1089,7 +1074,7 @@ struct tallyitem
} }
}; };
static UniValue ListReceived(const CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) static UniValue ListReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{ {
// Minimum confirmations // Minimum confirmations
int nMinDepth = 1; int nMinDepth = 1;
@ -1105,7 +1090,8 @@ static UniValue ListReceived(const CWallet * const pwallet, const UniValue& para
fIncludeEmpty = params[2].get_bool(); fIncludeEmpty = params[2].get_bool();
isminefilter filter = ISMINE_SPENDABLE; isminefilter filter = ISMINE_SPENDABLE;
if (ParseIncludeWatchonly(params[3], *pwallet)) {
if (ParseIncludeWatchonly(params[3], wallet)) {
filter |= ISMINE_WATCH_ONLY; filter |= ISMINE_WATCH_ONLY;
} }
@ -1121,10 +1107,10 @@ static UniValue ListReceived(const CWallet * const pwallet, const UniValue& para
// Tally // Tally
std::map<CTxDestination, tallyitem> mapTally; std::map<CTxDestination, tallyitem> mapTally;
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { for (const std::pair<const uint256, CWalletTx>& pairWtx : wallet.mapWallet) {
const CWalletTx& wtx = pairWtx.second; const CWalletTx& wtx = pairWtx.second;
if (wtx.IsCoinBase() || !pwallet->chain().checkFinalTx(*wtx.tx)) if (wtx.IsCoinBase() || !wallet.chain().checkFinalTx(*wtx.tx))
continue; continue;
int nDepth = wtx.GetDepthInMainChain(); int nDepth = wtx.GetDepthInMainChain();
@ -1141,7 +1127,7 @@ static UniValue ListReceived(const CWallet * const pwallet, const UniValue& para
continue; continue;
} }
isminefilter mine = pwallet->IsMine(address); isminefilter mine = wallet.IsMine(address);
if(!(mine & filter)) if(!(mine & filter))
continue; continue;
@ -1160,11 +1146,11 @@ static UniValue ListReceived(const CWallet * const pwallet, const UniValue& para
// Create m_address_book iterator // Create m_address_book iterator
// If we aren't filtering, go from begin() to end() // If we aren't filtering, go from begin() to end()
auto start = pwallet->m_address_book.begin(); auto start = wallet.m_address_book.begin();
auto end = pwallet->m_address_book.end(); auto end = wallet.m_address_book.end();
// If we are filtering, find() the applicable entry // If we are filtering, find() the applicable entry
if (has_filtered_address) { if (has_filtered_address) {
start = pwallet->m_address_book.find(filtered_address); start = wallet.m_address_book.find(filtered_address);
if (start != end) { if (start != end) {
end = std::next(start); end = std::next(start);
} }
@ -1179,7 +1165,7 @@ static UniValue ListReceived(const CWallet * const pwallet, const UniValue& para
if (it == mapTally.end() && !fIncludeEmpty) if (it == mapTally.end() && !fIncludeEmpty)
continue; continue;
isminefilter mine = pwallet->IsMine(address); isminefilter mine = wallet.IsMine(address);
if(!(mine & filter)) if(!(mine & filter))
continue; continue;
@ -1279,9 +1265,8 @@ static RPCHelpMan listreceivedbyaddress()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -1289,7 +1274,7 @@ static RPCHelpMan listreceivedbyaddress()
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
return ListReceived(pwallet, request.params, false); return ListReceived(*pwallet, request.params, false);
}, },
}; };
} }
@ -1323,9 +1308,8 @@ static RPCHelpMan listreceivedbylabel()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -1333,7 +1317,7 @@ static RPCHelpMan listreceivedbylabel()
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
return ListReceived(pwallet, request.params, true); return ListReceived(*pwallet, request.params, true);
}, },
}; };
} }
@ -1356,7 +1340,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param filter_ismine The "is mine" filter flags. * @param filter_ismine The "is mine" filter flags.
* @param filter_label Optional label string to filter incoming transactions. * @param filter_label Optional label string to filter incoming transactions.
*/ */
static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter_ismine, const std::string* filter_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{ {
CAmount nFee; CAmount nFee;
std::list<COutputEntry> listReceived; std::list<COutputEntry> listReceived;
@ -1372,21 +1356,21 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
for (const COutputEntry& s : listSent) for (const COutputEntry& s : listSent)
{ {
UniValue entry(UniValue::VOBJ); UniValue entry(UniValue::VOBJ);
if (involvesWatchonly || (pwallet->IsMine(s.destination) & ISMINE_WATCH_ONLY)) { if (involvesWatchonly || (wallet.IsMine(s.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true); entry.pushKV("involvesWatchonly", true);
} }
MaybePushAddress(entry, s.destination); MaybePushAddress(entry, s.destination);
std::map<std::string, std::string>::const_iterator it = wtx.mapValue.find("DS"); std::map<std::string, std::string>::const_iterator it = wtx.mapValue.find("DS");
entry.pushKV("category", (it != wtx.mapValue.end() && it->second == "1") ? "coinjoin" : "send"); entry.pushKV("category", (it != wtx.mapValue.end() && it->second == "1") ? "coinjoin" : "send");
entry.pushKV("amount", ValueFromAmount(-s.amount)); entry.pushKV("amount", ValueFromAmount(-s.amount));
const auto* address_book_entry = pwallet->FindAddressBookEntry(s.destination); const auto* address_book_entry = wallet.FindAddressBookEntry(s.destination);
if (address_book_entry) { if (address_book_entry) {
entry.pushKV("label", address_book_entry->GetLabel()); entry.pushKV("label", address_book_entry->GetLabel());
} }
entry.pushKV("vout", s.vout); entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee)); entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong) if (fLong)
WalletTxToJSON(pwallet->chain(), wtx, entry); WalletTxToJSON(wallet.chain(), wtx, entry);
entry.pushKV("abandoned", wtx.isAbandoned()); entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry); ret.push_back(entry);
} }
@ -1398,7 +1382,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
for (const COutputEntry& r : listReceived) for (const COutputEntry& r : listReceived)
{ {
std::string label; std::string label;
const auto* address_book_entry = pwallet->FindAddressBookEntry(r.destination); const auto* address_book_entry = wallet.FindAddressBookEntry(r.destination);
if (address_book_entry) { if (address_book_entry) {
label = address_book_entry->GetLabel(); label = address_book_entry->GetLabel();
} }
@ -1406,7 +1390,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
continue; continue;
} }
UniValue entry(UniValue::VOBJ); UniValue entry(UniValue::VOBJ);
if (involvesWatchonly || (pwallet->IsMine(r.destination) & ISMINE_WATCH_ONLY)) { if (involvesWatchonly || (wallet.IsMine(r.destination) & ISMINE_WATCH_ONLY)) {
entry.pushKV("involvesWatchonly", true); entry.pushKV("involvesWatchonly", true);
} }
MaybePushAddress(entry, r.destination); MaybePushAddress(entry, r.destination);
@ -1429,7 +1413,7 @@ static void ListTransactions(const CWallet* const pwallet, const CWalletTx& wtx,
} }
entry.pushKV("vout", r.vout); entry.pushKV("vout", r.vout);
if (fLong) if (fLong)
WalletTxToJSON(pwallet->chain(), wtx, entry); WalletTxToJSON(wallet.chain(), wtx, entry);
ret.push_back(entry); ret.push_back(entry);
} }
} }
@ -1508,9 +1492,8 @@ static RPCHelpMan listtransactions()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -1551,7 +1534,7 @@ static RPCHelpMan listtransactions()
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{ {
CWalletTx *const pwtx = (*it).second; CWalletTx *const pwtx = (*it).second;
ListTransactions(pwallet, *pwtx, 0, true, ret, filter, filter_label); ListTransactions(*pwallet, *pwtx, 0, true, ret, filter, filter_label);
if ((int)ret.size() >= (nCount+nFrom)) break; if ((int)ret.size() >= (nCount+nFrom)) break;
} }
} }
@ -1672,7 +1655,7 @@ static RPCHelpMan listsinceblock()
const CWalletTx& tx = pairWtx.second; const CWalletTx& tx = pairWtx.second;
if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) { if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) {
ListTransactions(&wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */); ListTransactions(wallet, tx, 0, true, transactions, filter, nullptr /* filter_label */);
} }
} }
@ -1689,7 +1672,7 @@ static RPCHelpMan listsinceblock()
if (it != wallet.mapWallet.end()) { if (it != wallet.mapWallet.end()) {
// We want all transactions regardless of confirmation count to appear here, // We want all transactions regardless of confirmation count to appear here,
// even negative confirmation ones, hence the big negative. // even negative confirmation ones, hence the big negative.
ListTransactions(&wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */); ListTransactions(wallet, it->second, -100000000, true, removed, filter, nullptr /* filter_label */);
} }
} }
blockId = block.hashPrevBlock; blockId = block.hashPrevBlock;
@ -1764,9 +1747,8 @@ static RPCHelpMan gettransaction()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -1803,7 +1785,7 @@ static RPCHelpMan gettransaction()
WalletTxToJSON(pwallet->chain(), wtx, entry); WalletTxToJSON(pwallet->chain(), wtx, entry);
UniValue details(UniValue::VARR); UniValue details(UniValue::VARR);
ListTransactions(pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */); ListTransactions(*pwallet, wtx, 0, false, details, filter, nullptr /* filter_label */);
entry.pushKV("details", details); entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx); std::string strHex = EncodeHexTx(*wtx.tx);
@ -1838,9 +1820,8 @@ static RPCHelpMan abandontransaction()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -1877,9 +1858,8 @@ static RPCHelpMan backupwallet()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -1913,9 +1893,8 @@ static RPCHelpMan keypoolrefill()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
@ -1931,7 +1910,7 @@ static RPCHelpMan keypoolrefill()
kpSize = (unsigned int)request.params[0].get_int(); kpSize = (unsigned int)request.params[0].get_int();
} }
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
pwallet->TopUpKeyPool(kpSize); pwallet->TopUpKeyPool(kpSize);
if (pwallet->GetKeyPoolSize() < (pwallet->IsHDEnabled() ? kpSize * 2 : kpSize)) { if (pwallet->GetKeyPoolSize() < (pwallet->IsHDEnabled() ? kpSize * 2 : kpSize)) {
@ -2068,9 +2047,8 @@ static RPCHelpMan walletpassphrasechange()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -2122,9 +2100,8 @@ static RPCHelpMan walletlock()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -2167,9 +2144,8 @@ static RPCHelpMan encryptwallet()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -2241,9 +2217,8 @@ static RPCHelpMan lockunspent()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -2356,9 +2331,8 @@ static RPCHelpMan listlockunspent()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -2397,9 +2371,8 @@ static RPCHelpMan settxfee()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -2603,9 +2576,8 @@ static RPCHelpMan getwalletinfo()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
@ -2938,9 +2910,8 @@ static RPCHelpMan setwalletflag()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
std::string flag_str = request.params[0].get_str(); std::string flag_str = request.params[0].get_str();
@ -3243,9 +3214,8 @@ static RPCHelpMan listunspent()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
int nMinDepth = 1; int nMinDepth = 1;
if (!request.params[0].isNull()) { if (!request.params[0].isNull()) {
@ -3405,11 +3375,11 @@ static RPCHelpMan listunspent()
}; };
} }
void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options, CCoinControl& coinControl) void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options, CCoinControl& coinControl)
{ {
// Make sure the results are valid at least up to the most recent block // 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 // the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain(); wallet.BlockUntilSyncedToCurrentChain();
change_position = -1; change_position = -1;
bool lockUnspents = false; bool lockUnspents = false;
@ -3467,7 +3437,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
} }
const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"]; const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, *pwallet); coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, wallet);
if (options.exists("lockUnspents") || options.exists("lock_unspents")) { if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool(); lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
@ -3491,12 +3461,12 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("subtractFeeFromOutputs") || options.exists("subtract_fee_from_outputs") ) if (options.exists("subtractFeeFromOutputs") || options.exists("subtract_fee_from_outputs") )
subtractFeeFromOutputs = (options.exists("subtract_fee_from_outputs") ? options["subtract_fee_from_outputs"] : options["subtractFeeFromOutputs"]).get_array(); subtractFeeFromOutputs = (options.exists("subtract_fee_from_outputs") ? options["subtract_fee_from_outputs"] : options["subtractFeeFromOutputs"]).get_array();
SetFeeEstimateMode(pwallet, coinControl, options["estimate_mode"], options["conf_target"]); SetFeeEstimateMode(wallet, coinControl, options["estimate_mode"], options["conf_target"]);
} }
} else { } else {
// if options is null and not a bool // if options is null and not a bool
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, *pwallet); coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
} }
if (tx.vout.size() == 0) if (tx.vout.size() == 0)
@ -3518,7 +3488,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
bilingual_str error; bilingual_str error;
if (!pwallet->FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) { if (!wallet.FundTransaction(tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, error.original); throw JSONRPCError(RPC_WALLET_ERROR, error.original);
} }
} }
@ -3586,12 +3556,10 @@ static RPCHelpMan fundrawtransaction()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL}); RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType(), UniValue::VBOOL});
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
// parse hex string from parameter // parse hex string from parameter
CMutableTransaction tx; CMutableTransaction tx;
@ -3604,7 +3572,7 @@ static RPCHelpMan fundrawtransaction()
CCoinControl coin_control; CCoinControl coin_control;
// Automatically select (additional) coins. Can be overridden by options.add_inputs. // Automatically select (additional) coins. Can be overridden by options.add_inputs.
coin_control.m_add_inputs = true; coin_control.m_add_inputs = true;
FundTransaction(pwallet, tx, fee, change_position, request.params[1], coin_control); FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control);
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(tx))); result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
@ -3673,9 +3641,8 @@ RPCHelpMan signrawtransactionwithwallet()
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
CMutableTransaction mtx; CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str())) { if (!DecodeHexTx(mtx, request.params[0].get_str())) {
@ -3684,7 +3651,7 @@ RPCHelpMan signrawtransactionwithwallet()
// Sign the transaction // Sign the transaction
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
// Fetch previous transactions (inputs): // Fetch previous transactions (inputs):
std::map<COutPoint, Coin> coins; std::map<COutPoint, Coin> coins;
@ -3731,9 +3698,8 @@ static RPCHelpMan rescanblockchain()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
WalletRescanReserver reserver(*pwallet); WalletRescanReserver reserver(*pwallet);
if (!reserver.reserve()) { if (!reserver.reserve()) {
@ -3908,15 +3874,13 @@ public:
} }
}; };
static UniValue DescribeWalletAddress(const CWallet* const pwallet, const CTxDestination& dest) static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
{ {
UniValue ret(UniValue::VOBJ); UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest); UniValue detail = DescribeAddress(dest);
CScript script = GetScriptForDestination(dest); CScript script = GetScriptForDestination(dest);
std::unique_ptr<SigningProvider> provider = nullptr; std::unique_ptr<SigningProvider> provider = nullptr;
if (pwallet) { provider = wallet.GetSolvingProvider(script);
provider = pwallet->GetSolvingProvider(script);
}
ret.pushKVs(detail); ret.pushKVs(detail);
ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest)); ret.pushKVs(std::visit(DescribeWalletAddressVisitor(provider.get()), dest));
return ret; return ret;
@ -3983,9 +3947,8 @@ RPCHelpMan getaddressinfo()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -4030,7 +3993,7 @@ RPCHelpMan getaddressinfo()
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
UniValue detail = DescribeWalletAddress(pwallet, dest); UniValue detail = DescribeWalletAddress(*pwallet, dest);
ret.pushKVs(detail); ret.pushKVs(detail);
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
@ -4094,9 +4057,8 @@ static RPCHelpMan getaddressesbylabel()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -4156,9 +4118,8 @@ static RPCHelpMan listlabels()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -4272,9 +4233,8 @@ static RPCHelpMan send()
}, true }, true
); );
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
UniValue options = request.params[3]; UniValue options = request.params[3];
if (options.exists("feeRate") || options.exists("fee_rate") || options.exists("estimate_mode") || options.exists("conf_target")) { if (options.exists("feeRate") || options.exists("fee_rate") || options.exists("estimate_mode") || options.exists("conf_target")) {
@ -4313,7 +4273,7 @@ static RPCHelpMan send()
// Automatically select coins, unless at least one is manually selected. Can // Automatically select coins, unless at least one is manually selected. Can
// be overriden by options.add_inputs. // be overriden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0; coin_control.m_add_inputs = rawTx.vin.size() == 0;
FundTransaction(pwallet, rawTx, fee, change_position, options, coin_control); FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control);
bool add_to_wallet = true; bool add_to_wallet = true;
if (options.exists("add_to_wallet")) { if (options.exists("add_to_wallet")) {
@ -4384,9 +4344,8 @@ static RPCHelpMan sethdseed()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
// TODO: add mnemonic feature to sethdseed or remove it in favour of upgradetohd // TODO: add mnemonic feature to sethdseed or remove it in favour of upgradetohd
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true); LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
@ -4404,7 +4363,7 @@ static RPCHelpMan sethdseed()
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed. The wallet already has a seed"); throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed. The wallet already has a seed");
} }
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
bool flush_key_pool = true; bool flush_key_pool = true;
if (!request.params[0].isNull()) { if (!request.params[0].isNull()) {
@ -4612,7 +4571,7 @@ static RPCHelpMan walletcreatefundedpsbt()
// Automatically select coins, unless at least one is manually selected. Can // Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs. // be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0; coin_control.m_add_inputs = rawTx.vin.size() == 0;
FundTransaction(&wallet, rawTx, fee, change_position, request.params[3], coin_control); FundTransaction(*pwallet, rawTx, fee, change_position, request.params[3], coin_control);
// Make a blank psbt // Make a blank psbt
PartiallySignedTransaction psbtx{rawTx}; PartiallySignedTransaction psbtx{rawTx};
@ -4662,13 +4621,12 @@ static RPCHelpMan upgradewallet()
}, },
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue; if (!pwallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
RPCTypeCheck(request.params, {UniValue::VNUM}, true); RPCTypeCheck(request.params, {UniValue::VNUM}, true);
EnsureWalletIsUnlocked(pwallet); EnsureWalletIsUnlocked(*pwallet);
int version = 0; int version = 0;
if (!request.params[0].isNull()) { if (!request.params[0].isNull()) {

View File

@ -33,7 +33,7 @@ Span<const CRPCCommand> GetWalletRPCCommands();
*/ */
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request); std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
void EnsureWalletIsUnlocked(const CWallet *); void EnsureWalletIsUnlocked(const CWallet&);
WalletContext& EnsureWalletContext(const CoreContext& context); WalletContext& EnsureWalletContext(const CoreContext& context);
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false); LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create = false);

View File

@ -938,6 +938,14 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
if (!strCmd.empty()) if (!strCmd.empty())
{ {
ReplaceAll(strCmd, "%s", hash.GetHex()); ReplaceAll(strCmd, "%s", hash.GetHex());
if (confirm.status == CWalletTx::Status::CONFIRMED)
{
ReplaceAll(strCmd, "%b", confirm.hashBlock.GetHex());
ReplaceAll(strCmd, "%h", ToString(confirm.block_height));
} else {
ReplaceAll(strCmd, "%b", "unconfirmed");
ReplaceAll(strCmd, "%h", "-1");
}
#ifndef WIN32 #ifndef WIN32
// Substituting the wallet name isn't currently supported on windows // Substituting the wallet name isn't currently supported on windows
// because windows shell escaping has not been implemented yet: // because windows shell escaping has not been implemented yet:

View File

@ -8,10 +8,23 @@ Test that the CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height
1351. 1351.
""" """
from test_framework.blocktools import create_coinbase, create_block, create_transaction from test_framework.blocktools import (
from test_framework.messages import CTransaction, msg_block create_block,
create_coinbase,
create_transaction,
)
from test_framework.messages import (
CTransaction,
msg_block,
)
from test_framework.p2p import P2PInterface from test_framework.p2p import P2PInterface
from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP, CScriptNum from test_framework.script import (
CScript,
CScriptNum,
OP_1NEGATE,
OP_CHECKLOCKTIMEVERIFY,
OP_DROP,
)
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import ( from test_framework.util import (
assert_equal, assert_equal,
@ -24,32 +37,54 @@ from io import BytesIO
CLTV_HEIGHT = 1351 CLTV_HEIGHT = 1351
def cltv_invalidate(tx): # Helper function to modify a transaction by
'''Modify the signature in vin 0 of the tx to fail CLTV # 1) prepending a given script to the scriptSig of vin 0 and
# 2) (optionally) modify the nSequence of vin 0 and the tx's nLockTime
def cltv_modify_tx(node, tx, prepend_scriptsig, nsequence=None, nlocktime=None):
if nsequence is not None:
tx.vin[0].nSequence = nsequence
tx.nLockTime = nlocktime
Prepends -1 CLTV DROP in the scriptSig itself. # Need to re-sign, since nSequence and nLockTime changed
signed_result = node.signrawtransactionwithwallet(tx.serialize().hex())
new_tx = CTransaction()
new_tx.deserialize(BytesIO(hex_str_to_bytes(signed_result['hex'])))
else:
new_tx = tx
new_tx.vin[0].scriptSig = CScript(prepend_scriptsig + list(CScript(new_tx.vin[0].scriptSig)))
return new_tx
def cltv_invalidate(node, tx, failure_reason):
# Modify the signature in vin 0 and nSequence/nLockTime of the tx to fail CLTV
#
# According to BIP65, OP_CHECKLOCKTIMEVERIFY can fail due the following reasons:
# 1) the stack is empty
# 2) the top item on the stack is less than 0
# 3) the lock-time type (height vs. timestamp) of the top stack item and the
# nLockTime field are not the same
# 4) the top stack item is greater than the transaction's nLockTime field
# 5) the nSequence field of the txin is 0xffffffff
assert failure_reason in range(5)
scheme = [
# | Script to prepend to scriptSig | nSequence | nLockTime |
# +-------------------------------------------------+------------+--------------+
[[OP_CHECKLOCKTIMEVERIFY], None, None],
[[OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP], None, None],
[[CScriptNum(1000), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 1296688602], # timestamp of genesis block
[[CScriptNum(1000), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 500],
[[CScriptNum(500), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0xffffffff, 500],
][failure_reason]
return cltv_modify_tx(node, tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
TODO: test more ways that transactions using CLTV could be invalid (eg
locktime requirements fail, sequence time requirements fail, etc).
'''
tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
list(CScript(tx.vin[0].scriptSig)))
def cltv_validate(node, tx, height): def cltv_validate(node, tx, height):
'''Modify the signature in vin 0 of the tx to pass CLTV # Modify the signature in vin 0 and nSequence/nLockTime of the tx to pass CLTV
Prepends <height> CLTV DROP in the scriptSig, and sets scheme = [[CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, height]
the locktime to height'''
tx.vin[0].nSequence = 0
tx.nLockTime = height
# Need to re-sign, since nSequence and nLockTime changed return cltv_modify_tx(node, tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
signed_result = node.signrawtransactionwithwallet(tx.serialize().hex())
new_tx = CTransaction()
new_tx.deserialize(BytesIO(hex_str_to_bytes(signed_result['hex'])))
new_tx.vin[0].scriptSig = CScript([CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
list(CScript(new_tx.vin[0].scriptSig)))
return new_tx
class BIP65Test(BitcoinTestFramework): class BIP65Test(BitcoinTestFramework):
@ -66,8 +101,7 @@ class BIP65Test(BitcoinTestFramework):
self.rpc_timeout = 480 self.rpc_timeout = 480
def test_cltv_info(self, *, is_active): def test_cltv_info(self, *, is_active):
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'], assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'], {
{
"active": is_active, "active": is_active,
"height": CLTV_HEIGHT, "height": CLTV_HEIGHT,
"type": "buried", "type": "buried",
@ -86,18 +120,22 @@ class BIP65Test(BitcoinTestFramework):
self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(CLTV_HEIGHT - 2)] self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(CLTV_HEIGHT - 2)]
self.nodeaddress = self.nodes[0].getnewaddress() self.nodeaddress = self.nodes[0].getnewaddress()
self.log.info("Test that an invalid-according-to-CLTV transaction can still appear in a block") self.log.info("Test that invalid-according-to-CLTV transactions can still appear in a block")
spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0], # create one invalid tx per CLTV failure reason (5 in total) and collect them
self.nodeaddress, amount=1.0) invalid_ctlv_txs = []
cltv_invalidate(spendtx) for i in range(5):
spendtx.rehash() spendtx = create_transaction(self.nodes[0], self.coinbase_txids[i],
self.nodeaddress, amount=1.0)
spendtx = cltv_invalidate(self.nodes[0], spendtx, i)
spendtx.rehash()
invalid_ctlv_txs.append(spendtx)
tip = self.nodes[0].getbestblockhash() tip = self.nodes[0].getbestblockhash()
block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1 block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time) block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time)
block.nVersion = 3 block.nVersion = 3
block.vtx.append(spendtx) block.vtx.extend(invalid_ctlv_txs)
block.hashMerkleRoot = block.calc_merkle_root() block.hashMerkleRoot = block.calc_merkle_root()
block.solve() block.solve()
@ -118,27 +156,38 @@ class BIP65Test(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
peer.sync_with_ping() peer.sync_with_ping()
self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block") self.log.info("Test that invalid-according-to-CLTV transactions cannot appear in a block")
block.nVersion = 4 block.nVersion = 4
block.vtx.append(CTransaction()) # dummy tx after coinbase that will be replaced later
spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1], # create and test one invalid tx per CLTV failure reason (5 in total)
self.nodeaddress, amount=1.0) for i in range(5):
cltv_invalidate(spendtx) spendtx = create_transaction(self.nodes[0], self.coinbase_txids[10+i],
spendtx.rehash() self.nodeaddress, amount=1.0)
spendtx = cltv_invalidate(self.nodes[0], spendtx, i)
spendtx.rehash()
# First we show that this tx is valid except for CLTV by getting it expected_cltv_reject_reason = [
# rejected from the mempool for exactly that reason. "non-mandatory-script-verify-flag (Operation not valid with the current stack size)",
assert_raises_rpc_error(-26, 'non-mandatory-script-verify-flag (Negative locktime)', self.nodes[0].sendrawtransaction, spendtx.serialize().hex(), 0) "non-mandatory-script-verify-flag (Negative locktime)",
"non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
"non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
"non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
][i]
# First we show that this tx is valid except for CLTV by getting it
# rejected from the mempool for exactly that reason.
assert_raises_rpc_error(-26, expected_cltv_reject_reason, self.nodes[0].sendrawtransaction, spendtx.serialize().hex(), 0)
# Now we verify that a block with this transaction is also invalid. # Now we verify that a block with this transaction is also invalid.
block.vtx.append(spendtx) block.vtx[1] = spendtx
block.hashMerkleRoot = block.calc_merkle_root() block.hashMerkleRoot = block.calc_merkle_root()
block.solve() block.solve()
with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with non-mandatory-script-verify-flag (Negative locktime)'.format(block.vtx[-1].hash)]): with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputScripts on {} failed with {}'.format(
peer.send_and_ping(msg_block(block)) block.vtx[-1].hash, expected_cltv_reject_reason)]):
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) peer.send_and_ping(msg_block(block))
peer.sync_with_ping() assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
peer.sync_with_ping()
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted") self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1) spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)

View File

@ -19,7 +19,7 @@ from test_framework.util import (
FILE_CHAR_START = 32 if os.name == 'nt' else 1 FILE_CHAR_START = 32 if os.name == 'nt' else 1
FILE_CHAR_END = 128 FILE_CHAR_END = 128
FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if os.name == 'nt' else '/' FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if os.name == 'nt' else '/'
UNCONFIRMED_HASH_STRING = 'unconfirmed'
def notify_outputname(walletname, txid): def notify_outputname(walletname, txid):
return txid if os.name == 'nt' else '{}_{}'.format(walletname, txid) return txid if os.name == 'nt' else '{}_{}'.format(walletname, txid)
@ -46,7 +46,7 @@ class NotificationsTest(DashTestFramework):
self.extra_args[0].append("-alertnotify=echo > {}".format(os.path.join(self.alertnotify_dir, '%s'))) self.extra_args[0].append("-alertnotify=echo > {}".format(os.path.join(self.alertnotify_dir, '%s')))
self.extra_args[0].append("-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s'))) self.extra_args[0].append("-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s')))
self.extra_args[1].append("-rescan") self.extra_args[1].append("-rescan")
self.extra_args[1].append("-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s')))) self.extra_args[1].append("-walletnotify=echo %h_%b > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))))
# -chainlocknotify on node0, -instantsendnotify on node1 # -chainlocknotify on node0, -instantsendnotify on node1
self.extra_args[0].append("-chainlocknotify=echo > {}".format(os.path.join(self.chainlocknotify_dir, '%s'))) self.extra_args[0].append("-chainlocknotify=echo > {}".format(os.path.join(self.chainlocknotify_dir, '%s')))
@ -78,12 +78,9 @@ class NotificationsTest(DashTestFramework):
self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
# directory content should equal the generated transaction hashes # directory content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count))) tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count)))
assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir)))
self.stop_node(1) self.stop_node(1)
self.expect_wallet_notify(tx_details)
for tx_file in os.listdir(self.walletnotify_dir):
os.remove(os.path.join(self.walletnotify_dir, tx_file))
self.log.info("test -walletnotify after rescan") self.log.info("test -walletnotify after rescan")
# restart node to rescan to force wallet notifications # restart node to rescan to force wallet notifications
@ -94,10 +91,8 @@ class NotificationsTest(DashTestFramework):
self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
# directory content should equal the generated transaction hashes # directory content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: notify_outputname(self.wallet, t['txid']), self.nodes[1].listtransactions("*", block_count))) tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count)))
assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) self.expect_wallet_notify(tx_details)
for tx_file in os.listdir(self.walletnotify_dir):
os.remove(os.path.join(self.walletnotify_dir, tx_file))
self.log.info("test -chainlocknotify") self.log.info("test -chainlocknotify")
@ -145,6 +140,29 @@ class NotificationsTest(DashTestFramework):
# TODO: add test for `-alertnotify` large fork notifications # TODO: add test for `-alertnotify` large fork notifications
def expect_wallet_notify(self, tx_details):
self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_details), timeout=10)
# Should have no more and no less files than expected
assert_equal(sorted(notify_outputname(self.wallet, tx_id) for tx_id, _, _ in tx_details), sorted(os.listdir(self.walletnotify_dir)))
# Should now verify contents of each file
for tx_id, blockheight, blockhash in tx_details:
fname = os.path.join(self.walletnotify_dir, notify_outputname(self.wallet, tx_id))
# Wait for the cached writes to hit storage
self.wait_until(lambda: os.path.getsize(fname) > 0, timeout=10)
with open(fname, 'rt', encoding='utf-8') as f:
text = f.read()
# Universal newline ensures '\n' on 'nt'
assert_equal(text[-1], '\n')
text = text[:-1]
if os.name == 'nt':
# On Windows, echo as above will append a whitespace
assert_equal(text[-1], ' ')
text = text[:-1]
expected = str(blockheight) + '_' + blockhash
assert_equal(text, expected)
for tx_file in os.listdir(self.walletnotify_dir):
os.remove(os.path.join(self.walletnotify_dir, tx_file))
if __name__ == '__main__': if __name__ == '__main__':
NotificationsTest().main() NotificationsTest().main()