mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
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: utACK4731f7045f
PastaPastaPasta: utACK4731f7045f
Tree-SHA512: dcee684563e4e07e838e96df0bf5e49d59e8c85aea6beca3239782e7d90a24f13d828b1201073585b888b0b154b4ba8bb90f88a36e3f923ac03b46f595d75500
This commit is contained in:
commit
5211886fb4
336
doc/build-osx.md
336
doc/build-osx.md
@ -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).
|
|
||||||
|
6
doc/release-notes-21141.md
Normal file
6
doc/release-notes-21141.md
Normal 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).
|
@ -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();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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\"")
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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). "
|
||||||
|
@ -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 ) {
|
||||||
|
@ -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()) {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user