Merge branch 'master' into fetch-params-lock
This commit is contained in:
commit
4cd627a985
|
@ -50,6 +50,15 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.65"
|
||||
|
@ -964,6 +973,7 @@ dependencies = [
|
|||
"memuse",
|
||||
"metrics",
|
||||
"metrics-exporter-prometheus",
|
||||
"metrics-util",
|
||||
"nonempty",
|
||||
"orchard",
|
||||
"rand 0.8.5",
|
||||
|
@ -977,6 +987,7 @@ dependencies = [
|
|||
"subtle",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
"tracing-core",
|
||||
|
@ -1108,6 +1119,7 @@ version = "0.14.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d24dc2dbae22bff6f1f9326ffce828c9f07ef9cc1e8002e5279f845432a30a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"hashbrown",
|
||||
|
|
|
@ -77,6 +77,8 @@ rayon = "1.5"
|
|||
ipnet = "2"
|
||||
metrics = "0.20"
|
||||
metrics-exporter-prometheus = "0.11"
|
||||
metrics-util = { version = "0.14", default-features = false, features = ["layer-filter"] }
|
||||
tokio = { version = "1", features = ["rt", "net", "time"] }
|
||||
|
||||
# General tool dependencies
|
||||
gumdrop = "0.8"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Zcash 5.3.0
|
||||
Zcash 5.3.2
|
||||
<img align="right" width="120" height="80" src="doc/imgs/logo.png">
|
||||
===========
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
|
|||
AC_PREREQ([2.60])
|
||||
define(_CLIENT_VERSION_MAJOR, 5)
|
||||
define(_CLIENT_VERSION_MINOR, 3)
|
||||
define(_CLIENT_VERSION_REVISION, 0)
|
||||
define(_CLIENT_VERSION_REVISION, 2)
|
||||
define(_CLIENT_VERSION_BUILD, 50)
|
||||
define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50)))
|
||||
define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1)))
|
||||
|
|
|
@ -1,3 +1,21 @@
|
|||
zcash (5.3.2) stable; urgency=medium
|
||||
|
||||
* 5.3.2 release.
|
||||
|
||||
-- Electric Coin Company <team@electriccoin.co> Sat, 03 Dec 2022 19:58:44 +0000
|
||||
|
||||
zcash (5.3.1) stable; urgency=medium
|
||||
|
||||
* 5.3.1 release.
|
||||
|
||||
-- Electric Coin Company <team@electriccoin.co> Fri, 02 Dec 2022 02:46:42 +0000
|
||||
|
||||
zcash (5.3.1~rc1) stable; urgency=medium
|
||||
|
||||
* 5.3.1-rc1 release.
|
||||
|
||||
-- Electric Coin Company <team@electriccoin.co> Wed, 23 Nov 2022 22:18:55 -0700
|
||||
|
||||
zcash (5.3.0) stable; urgency=medium
|
||||
|
||||
* 5.3.0 release.
|
||||
|
|
|
@ -55,4 +55,5 @@ fi
|
|||
zcash-fetch-params
|
||||
touch .zcash/zcash.conf
|
||||
echo "Starting: ${ZCASHD_CMD}"
|
||||
# shellcheck disable=SC2294 # see #6297
|
||||
eval exec "${ZCASHD_CMD}" "${@}"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
name: "zcash-5.3.0"
|
||||
name: "zcash-5.3.2"
|
||||
enable_cache: true
|
||||
distro: "debian"
|
||||
suites:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
name: "zcash-5.3.0"
|
||||
name: "zcash-5.3.2"
|
||||
enable_cache: true
|
||||
distro: "debian"
|
||||
suites:
|
||||
|
|
|
@ -20,6 +20,7 @@ $(package)_download_path_freebsd=https://github.com/llvm/llvm-project/releases/d
|
|||
$(package)_download_file_freebsd=clang+llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz
|
||||
$(package)_file_name_freebsd=clang-llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz
|
||||
$(package)_sha256_hash_freebsd=b0a7b86dacb12afb8dd2ca99ea1b894d9cce84aab7711cb1964b3005dfb09af3
|
||||
$(package)_download_path_aarch64_linux=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version)
|
||||
$(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz
|
||||
$(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz
|
||||
$(package)_sha256_hash_aarch64_linux=1a81fda984f5e607584916fdf69cf41e5385b219b983544d2c1a14950d5a65cf
|
||||
|
|
|
@ -6,5 +6,6 @@ $(package)_download_file=v$($(package)_version).tar.gz
|
|||
$(package)_sha256_hash=8d6aa7d77ad0abb35bb6139cb9a33597ac4c5b33da6a004ae42429b8598c9605
|
||||
|
||||
define $(package)_stage_cmds
|
||||
cp -a ./source $($(package)_staging_dir)$(host_prefix)/include
|
||||
mkdir -p $($(package)_staging_dir)$(host_prefix)/include && \
|
||||
cp -a ./source $($(package)_staging_dir)$(host_prefix)/include/utf8cpp
|
||||
endef
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
Zcash Contributors
|
||||
==================
|
||||
|
||||
Jack Grigg (1243)
|
||||
Kris Nuttycombe (582)
|
||||
Jack Grigg (1244)
|
||||
Kris Nuttycombe (585)
|
||||
Simon Liu (460)
|
||||
Sean Bowe (389)
|
||||
Daira Hopwood (343)
|
||||
Daira Hopwood (369)
|
||||
Eirik Ogilvie-Wigley (216)
|
||||
Wladimir J. van der Laan (158)
|
||||
Pieter Wuille (143)
|
||||
|
@ -18,8 +18,9 @@ Jay Graber (89)
|
|||
Larry Ruane (88)
|
||||
Marco Falke (86)
|
||||
Cory Fields (78)
|
||||
sasha (62)
|
||||
Matt Corallo (60)
|
||||
sasha (59)
|
||||
Greg Pfeil (60)
|
||||
Nathan Wilcox (57)
|
||||
practicalswift (42)
|
||||
Kevin Gallagher (38)
|
||||
|
@ -30,7 +31,6 @@ Carl Dong (26)
|
|||
Gregory Maxwell (24)
|
||||
John Newbery (23)
|
||||
Jorge Timón (22)
|
||||
Greg Pfeil (20)
|
||||
furszy (18)
|
||||
Jonathan "Duke" Leto (18)
|
||||
syd (16)
|
||||
|
@ -38,13 +38,13 @@ Patick Strateman (16)
|
|||
Charlie O'Keefe (15)
|
||||
avnish (14)
|
||||
Per Grön (14)
|
||||
Suhas Daftuar (13)
|
||||
Benjamin Winston (13)
|
||||
Steven Smith (12)
|
||||
Pavel Janík (12)
|
||||
Patrick Strateman (12)
|
||||
Jeremy Rubin (12)
|
||||
Ariel Gabizon (12)
|
||||
Suhas Daftuar (11)
|
||||
Paige Peterson (11)
|
||||
Kaz Wesley (11)
|
||||
João Barbosa (11)
|
||||
|
@ -123,6 +123,7 @@ Pejvan (2)
|
|||
Pavol Rusnak (2)
|
||||
Pavel Vasin (2)
|
||||
Mustafa (2)
|
||||
Miodrag Popović (2)
|
||||
Matthew King (2)
|
||||
Mary Moore-Simmons (2)
|
||||
Marek (2)
|
||||
|
@ -159,6 +160,7 @@ kirkalx (1)
|
|||
kazcw (1)
|
||||
jeff-liang (1)
|
||||
jc (1)
|
||||
idm (1)
|
||||
glowang (1)
|
||||
ewillbefull@gmail.com (1)
|
||||
emilrus (1)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH ZCASH-CLI "1" "October 2022" "zcash-cli v5.3.0" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH ZCASH-CLI "1" "December 2022" "zcash-cli v5.3.2" "User Commands"
|
||||
.SH NAME
|
||||
zcash-cli \- manual page for zcash-cli v5.3.0
|
||||
zcash-cli \- manual page for zcash-cli v5.3.2
|
||||
.SH DESCRIPTION
|
||||
Zcash RPC client version v5.3.0
|
||||
Zcash RPC client version v5.3.2
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using Zcash,
|
||||
please see <https://z.cash/support/security/>.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH ZCASH-TX "1" "October 2022" "zcash-tx v5.3.0" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH ZCASH-TX "1" "December 2022" "zcash-tx v5.3.2" "User Commands"
|
||||
.SH NAME
|
||||
zcash-tx \- manual page for zcash-tx v5.3.0
|
||||
zcash-tx \- manual page for zcash-tx v5.3.2
|
||||
.SH DESCRIPTION
|
||||
Zcash zcash\-tx utility version v5.3.0
|
||||
Zcash zcash\-tx utility version v5.3.2
|
||||
.SS "Usage:"
|
||||
.TP
|
||||
zcash\-tx [options] <hex\-tx> [commands]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH ZCASHD-WALLET-TOOL "1" "October 2022" "zcashd-wallet-tool v5.3.0" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH ZCASHD-WALLET-TOOL "1" "December 2022" "zcashd-wallet-tool v5.3.2" "User Commands"
|
||||
.SH NAME
|
||||
zcashd-wallet-tool \- manual page for zcashd-wallet-tool v5.3.0
|
||||
zcashd-wallet-tool \- manual page for zcashd-wallet-tool v5.3.2
|
||||
.SH SYNOPSIS
|
||||
.B zcashd-wallet-tool
|
||||
[\fI\,OPTIONS\/\fR]
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.1.
|
||||
.TH ZCASHD "1" "October 2022" "zcashd v5.3.0" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH ZCASHD "1" "December 2022" "zcashd v5.3.2" "User Commands"
|
||||
.SH NAME
|
||||
zcashd \- manual page for zcashd v5.3.0
|
||||
zcashd \- manual page for zcashd v5.3.2
|
||||
.SH DESCRIPTION
|
||||
Zcash Daemon version v5.3.0
|
||||
Zcash Daemon version v5.3.2
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using Zcash,
|
||||
please see <https://z.cash/support/security/>.
|
||||
|
@ -94,7 +94,7 @@ Keep at most <n> unconnectable transactions in memory (default: 100)
|
|||
.HP
|
||||
\fB\-par=\fR<n>
|
||||
.IP
|
||||
Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 =
|
||||
Set the number of script verification threads (\fB\-6\fR to 16, 0 = auto, <0 =
|
||||
leave that many cores free, default: 0)
|
||||
.HP
|
||||
\fB\-pid=\fR<file>
|
||||
|
@ -416,6 +416,10 @@ Expose node metrics in the Prometheus exposition format. An HTTP
|
|||
listener will be started on <port>, which responds to GET requests on
|
||||
any request path. Use \fB\-metricsallowip\fR and \fB\-metricsbind\fR to control
|
||||
access.
|
||||
.HP
|
||||
\fB\-debugmetrics\fR
|
||||
.IP
|
||||
Include debug metrics in exposed node metrics.
|
||||
.PP
|
||||
Debugging/Testing options:
|
||||
.HP
|
||||
|
|
|
@ -4,8 +4,3 @@ release-notes at release time)
|
|||
Notable changes
|
||||
===============
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
This release fixes an error "Assertion `uResultHeight == rewindHeight` failed" (#5958)
|
||||
that could sometimes happen when restarting a node.
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
Notable changes
|
||||
===============
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
This release fixes an error "Assertion `uResultHeight == rewindHeight` failed" (#5958)
|
||||
that could sometimes happen when restarting a node.
|
||||
|
||||
Memory Usage Improvement
|
||||
------------------------
|
||||
|
||||
The memory usage of zcashd has been reduced by not keeping Equihash solutions for all
|
||||
block headers in memory.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Daira Hopwood (14):
|
||||
Always use a tuple as right argument of % in new Python code.
|
||||
Report the prevout for each transparent input as it is being checked
|
||||
Update authors of librustzcash to include Greg Pfeil.
|
||||
Ensure that the optimization of not scanning blocks prior to the wallet's birthday does not cause us to try to "rewind" the Orchard wallet to a height after its current checkpoint.
|
||||
Improve a comment about the wallet birthday scanning optimization.
|
||||
Add release notes for the fix to #5958.
|
||||
Fix a Markdown syntax error
|
||||
Error reporting improvements.
|
||||
Fix a dependency of the `show_help` RPC test on the number of cores, and an incompatibility with Python 3.9 in the test framework that affected the `receivedby` extended RPC test.
|
||||
Avoid storing the Equihash solution in a CBlockIndex object once it has been written to the leveldb database.
|
||||
Improve handling of database read errors.
|
||||
Add Prometheus metrics so we have more visibility into what is going on with the Equihash solution trimming:
|
||||
Declare `CBlockTreeDB::Read*` methods as `const` when they are trivially so.
|
||||
Update constants
|
||||
|
||||
Greg Pfeil (39):
|
||||
Fix display of binary name in error messages.
|
||||
Address review feedback and fixed test failures
|
||||
Check dependency updates on the correct branch
|
||||
updatecheck: fix GitHub auth
|
||||
updatecheck: simplify token handling
|
||||
updatecheck: support XDG-based token location
|
||||
`zcash --help` test improvements
|
||||
Remove the PR template
|
||||
Apply suggestions from code review
|
||||
Small formatting change
|
||||
Improve z_sendmany documentation
|
||||
Avoid inconsistent Python lookup
|
||||
Propagate asOfHeight to all relevant RPC calls
|
||||
Implement `asOfHeight`
|
||||
Add additional asOfHeight tests
|
||||
Don’t ignore asOfHeight in IsSpent calls
|
||||
Extract asOfHeight info from RPC calls
|
||||
Ignore mempool when asOfHeight is set
|
||||
Fix calls that should have specified asOfHeight
|
||||
GetUnconfirmedBalance should not take asOfHeight
|
||||
Require minconf > 0 when asOfHeight is provided
|
||||
Add error cases and default to `asOfHeight`
|
||||
Work around #6262 in wallet_listunspent
|
||||
Don’t trust mempool tx when using `asOfHeight`
|
||||
Apply suggestions from code review
|
||||
Add matured_at_height test helper
|
||||
Add FIXMEs to repair comments after #6262 is fixed
|
||||
Update src/rpc/server.cpp
|
||||
Apply suggestions from code review
|
||||
Fix small error in code review suggestions
|
||||
Revert change to getbalance minconf
|
||||
Revert getinfo support of asOfHeight
|
||||
Change asOfHeight to use -1 as default
|
||||
Change asOfHeight to preserve Bitcoin compat
|
||||
Apply suggestions from code review
|
||||
Simplify filtering AvailableCoins by destination
|
||||
Postpone dependency updates for v5.3.1
|
||||
make-release.py: Versioning changes for 5.3.1-rc1.
|
||||
make-release.py: Updated manpages for 5.3.1-rc1.
|
||||
|
||||
Jack Grigg (1):
|
||||
Place zcashd.debug.* metrics behind a -debugmetrics config option
|
||||
|
||||
Kris Nuttycombe (3):
|
||||
Add extra detail related to transparent inputs and outputs.
|
||||
Add `unspent_as_of` argument to `listunspent`
|
||||
Add RPC test for wallet_listunspent changes
|
||||
|
||||
Miodrag Popović (2):
|
||||
FindNextBlocksToDownload(): Fetch active consensus params to read nMinimumChainWork
|
||||
Headers sync timeout: Use EstimateNetHeight() for closer approximation of remaining headers to download
|
||||
|
||||
Suhas Daftuar (2):
|
||||
Delay parallel block download until chain has sufficient work
|
||||
Add timeout for headers sync
|
||||
|
||||
idm (1):
|
||||
fix aarch64 dependency native clang download URL
|
||||
|
||||
sasha (3):
|
||||
Update gitian-linux-parallel.yml
|
||||
Fix gitian version string issue by reverting the GIT_DIR backport commit
|
||||
Remove `git_check_in_repo` from genbuild.sh to fix gitian version string
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
Notable changes
|
||||
===============
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
This release fixes an error "Assertion `uResultHeight == rewindHeight` failed" (#5958)
|
||||
that could sometimes happen when restarting a node.
|
||||
|
||||
Memory Usage Improvement
|
||||
------------------------
|
||||
|
||||
The memory usage of zcashd has been reduced by not keeping Equihash solutions for all
|
||||
block headers in memory.
|
||||
|
||||
RPC changes
|
||||
-----------
|
||||
|
||||
The following RPC methods that query wallet state now support an optional `asOfHeight`
|
||||
parameter, to execute the query as if it were run when the blockchain was at the height
|
||||
specified by this argument:
|
||||
|
||||
* `getbalance`
|
||||
* `getreceivedbyaddress`
|
||||
* `gettransaction` (*)
|
||||
* `getwalletinfo`
|
||||
* `listaddressgroupings`
|
||||
* `listreceivedbyaddress` (*)
|
||||
* `listsinceblock` (*)
|
||||
* `listtransactions`
|
||||
* `listunspent` (*)
|
||||
* `z_getbalanceforaccount`
|
||||
* `z_getbalanceforviewingkey`
|
||||
* `z_getmigrationstatus`
|
||||
* `z_getnotescount`
|
||||
* `z_listreceivedbyaddress`
|
||||
* `z_listunspent`
|
||||
|
||||
(*) For these methods, additional parameters have been added to maintain
|
||||
compatibility of parameter lists with Bitcoin Core. Default values should be
|
||||
passed for these additional parameters in order to use `asOfHeight`. See the
|
||||
[RPC documentation](https://zcash.github.io/) for details.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Daira Hopwood (21):
|
||||
Always use a tuple as right argument of % in new Python code.
|
||||
Report the prevout for each transparent input as it is being checked
|
||||
Update authors of librustzcash to include Greg Pfeil.
|
||||
Ensure that the optimization of not scanning blocks prior to the wallet's birthday does not cause us to try to "rewind" the Orchard wallet to a height after its current checkpoint.
|
||||
Improve a comment about the wallet birthday scanning optimization.
|
||||
Add release notes for the fix to #5958.
|
||||
Fix a Markdown syntax error
|
||||
Error reporting improvements.
|
||||
Fix a dependency of the `show_help` RPC test on the number of cores, and an incompatibility with Python 3.9 in the test framework that affected the `receivedby` extended RPC test.
|
||||
Avoid storing the Equihash solution in a CBlockIndex object once it has been written to the leveldb database.
|
||||
Improve handling of database read errors.
|
||||
Add Prometheus metrics so we have more visibility into what is going on with the Equihash solution trimming:
|
||||
Declare `CBlockTreeDB::Read*` methods as `const` when they are trivially so.
|
||||
Update constants
|
||||
Add release notes for #6122 (`asOfHeight` RPC parameters).
|
||||
Update release notes: z_getnotescount already had the minconf parameter
|
||||
Add release notes for #6122 (`asOfHeight` RPC parameters).
|
||||
Update release notes: z_getnotescount already had the minconf parameter
|
||||
Bump timestamps and add libcxx/native_clang 15.0.6 in `postponed-updates.txt`.
|
||||
make-release.py: Versioning changes for 5.3.1.
|
||||
make-release.py: Updated manpages for 5.3.1.
|
||||
|
||||
Greg Pfeil (40):
|
||||
Fix display of binary name in error messages.
|
||||
Address review feedback and fixed test failures
|
||||
Check dependency updates on the correct branch
|
||||
updatecheck: fix GitHub auth
|
||||
updatecheck: simplify token handling
|
||||
updatecheck: support XDG-based token location
|
||||
`zcash --help` test improvements
|
||||
Remove the PR template
|
||||
Apply suggestions from code review
|
||||
Small formatting change
|
||||
Improve z_sendmany documentation
|
||||
Avoid inconsistent Python lookup
|
||||
Propagate asOfHeight to all relevant RPC calls
|
||||
Implement `asOfHeight`
|
||||
Add additional asOfHeight tests
|
||||
Don’t ignore asOfHeight in IsSpent calls
|
||||
Extract asOfHeight info from RPC calls
|
||||
Ignore mempool when asOfHeight is set
|
||||
Fix calls that should have specified asOfHeight
|
||||
GetUnconfirmedBalance should not take asOfHeight
|
||||
Require minconf > 0 when asOfHeight is provided
|
||||
Add error cases and default to `asOfHeight`
|
||||
Work around #6262 in wallet_listunspent
|
||||
Don’t trust mempool tx when using `asOfHeight`
|
||||
Apply suggestions from code review
|
||||
Add matured_at_height test helper
|
||||
Add FIXMEs to repair comments after #6262 is fixed
|
||||
Update src/rpc/server.cpp
|
||||
Apply suggestions from code review
|
||||
Fix small error in code review suggestions
|
||||
Revert change to getbalance minconf
|
||||
Revert getinfo support of asOfHeight
|
||||
Change asOfHeight to use -1 as default
|
||||
Change asOfHeight to preserve Bitcoin compat
|
||||
Apply suggestions from code review
|
||||
Simplify filtering AvailableCoins by destination
|
||||
Postpone dependency updates for v5.3.1
|
||||
make-release.py: Versioning changes for 5.3.1-rc1.
|
||||
make-release.py: Updated manpages for 5.3.1-rc1.
|
||||
make-release.py: Updated release notes and changelog for 5.3.1-rc1.
|
||||
|
||||
Jack Grigg (1):
|
||||
Place zcashd.debug.* metrics behind a -debugmetrics config option
|
||||
|
||||
Kris Nuttycombe (3):
|
||||
Add extra detail related to transparent inputs and outputs.
|
||||
Add `unspent_as_of` argument to `listunspent`
|
||||
Add RPC test for wallet_listunspent changes
|
||||
|
||||
Miodrag Popović (2):
|
||||
FindNextBlocksToDownload(): Fetch active consensus params to read nMinimumChainWork
|
||||
Headers sync timeout: Use EstimateNetHeight() for closer approximation of remaining headers to download
|
||||
|
||||
Suhas Daftuar (2):
|
||||
Delay parallel block download until chain has sufficient work
|
||||
Add timeout for headers sync
|
||||
|
||||
idm (1):
|
||||
fix aarch64 dependency native clang download URL
|
||||
|
||||
sasha (3):
|
||||
Update gitian-linux-parallel.yml
|
||||
Fix gitian version string issue by reverting the GIT_DIR backport commit
|
||||
Remove `git_check_in_repo` from genbuild.sh to fix gitian version string
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
Notable changes
|
||||
===============
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
This is a hotfix release that fixes a regression in memory usage during
|
||||
Initial Block Download. The regression was indirectly caused by a change
|
||||
to prioritize downloading headers (PR #6231), introduced in release 5.3.1.
|
||||
It caused memory usage for new nodes to spike to roughly 11 GiB about an
|
||||
hour after starting Initial Block Download.
|
||||
|
||||
The issue fixed by this release does not affect nodes that start from
|
||||
a fully synced chain, or that had sufficient memory available to get
|
||||
past the memory usage spike.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Daira Hopwood (5):
|
||||
Revert "Headers-first fix"
|
||||
Add release notes for the IBD memory spike issue.
|
||||
Postpone updates.
|
||||
make-release.py: Versioning changes for 5.3.2.
|
||||
make-release.py: Updated manpages for 5.3.2.
|
||||
|
|
@ -62,6 +62,7 @@ BASE_SCRIPTS= [
|
|||
'wallet_overwintertx.py',
|
||||
'wallet_persistence.py',
|
||||
'wallet_listnotes.py',
|
||||
'wallet_listunspent.py',
|
||||
# vv Tests less than 60s vv
|
||||
'orchard_reorg.py',
|
||||
'fundrawtransaction.py',
|
||||
|
|
|
@ -7,9 +7,17 @@
|
|||
# Exercise the listtransactions API
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
def count_array_matches(object_array, to_match):
|
||||
num_matched = 0
|
||||
for item in object_array:
|
||||
if all((item[key] == value for key,value in to_match.items())):
|
||||
num_matched = num_matched+1
|
||||
return num_matched
|
||||
|
||||
def check_array_result(object_array, to_match, expected):
|
||||
"""
|
||||
Pass in array of JSON objects, a dictionary with key/value pairs
|
||||
|
@ -27,7 +35,7 @@ def check_array_result(object_array, to_match, expected):
|
|||
for key,value in expected.items():
|
||||
if item[key] != value:
|
||||
raise AssertionError("%s : expected %s=%s"%(str(item), str(key), str(value)))
|
||||
num_matched = num_matched+1
|
||||
num_matched = num_matched+1
|
||||
if num_matched == 0:
|
||||
raise AssertionError("No objects matched %s"%(str(to_match)))
|
||||
|
||||
|
@ -45,6 +53,7 @@ class ListTransactionsTest(BitcoinTestFramework):
|
|||
{"category":"receive","amount":Decimal("0.1"),"amountZat":10000000,"confirmations":0})
|
||||
|
||||
# mine a block, confirmations should change:
|
||||
old_block = self.nodes[0].getblockcount()
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
check_array_result(self.nodes[0].listtransactions(),
|
||||
|
@ -53,6 +62,16 @@ class ListTransactionsTest(BitcoinTestFramework):
|
|||
check_array_result(self.nodes[1].listtransactions(),
|
||||
{"txid":txid},
|
||||
{"category":"receive","amount":Decimal("0.1"),"amountZat":10000000,"confirmations":1})
|
||||
# Confirmations here are -1 instead of 0 because while we only went back
|
||||
# 1 block, “0” means the tx is in the mempool, but the tx has already
|
||||
# been mined and `asOfHeight` ignores the mempool regardless.
|
||||
check_array_result(self.nodes[0].listtransactions("*", 10, 0, False, old_block),
|
||||
{"txid":txid},
|
||||
{"category":"send","amount":Decimal("-0.1"),"amountZat":-10000000,"confirmations":-1})
|
||||
# And while we can still see the tx we sent before the block where they
|
||||
# got mined, we don’t have access to ones we’ll receive after the
|
||||
# specified block.
|
||||
assert_equal(0, count_array_matches(self.nodes[1].listtransactions("*", 10, 0, False, old_block), {"txid":txid}))
|
||||
|
||||
# send-to-self:
|
||||
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
|
||||
|
|
|
@ -14,7 +14,7 @@ from decimal import Decimal
|
|||
def get_sub_array_from_array(object_array, to_match):
|
||||
'''
|
||||
Finds and returns a sub array from an array of arrays.
|
||||
to_match should be a unique idetifier of a sub array
|
||||
to_match should be a unique identifier of a sub array
|
||||
'''
|
||||
for item in object_array:
|
||||
all_match = True
|
||||
|
|
|
@ -13,7 +13,7 @@ from test_framework.util import assert_equal, assert_true, zcashd_binary
|
|||
import subprocess
|
||||
import tempfile
|
||||
|
||||
help_message = """
|
||||
help_message_1 = """
|
||||
In order to ensure you are adequately protecting your privacy when using Zcash,
|
||||
please see <https://z.cash/support/security/>.
|
||||
|
||||
|
@ -85,7 +85,8 @@ Options:
|
|||
Keep at most <n> unconnectable transactions in memory (default: 100)
|
||||
|
||||
-par=<n>
|
||||
Set the number of script verification threads (-8 to 16, 0 = auto, <0 =
|
||||
Set the number of script verification threads (""" # nondeterministic part here
|
||||
help_message_2 = """, 0 = auto, <0 =
|
||||
leave that many cores free, default: 0)
|
||||
|
||||
-pid=<file>
|
||||
|
@ -342,6 +343,9 @@ Monitoring options:
|
|||
any request path. Use -metricsallowip and -metricsbind to control
|
||||
access.
|
||||
|
||||
-debugmetrics
|
||||
Include debug metrics in exposed node metrics.
|
||||
|
||||
Debugging/Testing options:
|
||||
|
||||
-debug=<category>
|
||||
|
@ -492,7 +496,8 @@ class ShowHelpTest(BitcoinTestFramework):
|
|||
assert_equal(process.returncode, 0)
|
||||
log_stdout.seek(0)
|
||||
stdout = log_stdout.read().decode('utf-8')
|
||||
assert_true(help_message in stdout)
|
||||
assert_true(help_message_1 in stdout)
|
||||
assert_true(help_message_2 in stdout)
|
||||
|
||||
def run_test(self):
|
||||
self.show_help()
|
||||
|
|
|
@ -107,7 +107,7 @@ def all_interfaces():
|
|||
max_possible *= 2
|
||||
else:
|
||||
break
|
||||
namestr = names.tostring()
|
||||
namestr = names.tobytes()
|
||||
return [(namestr[i:i+16].split(b'\0', 1)[0],
|
||||
socket.inet_ntoa(namestr[i+20:i+24]))
|
||||
for i in range(0, outbytes, struct_size)]
|
||||
|
|
|
@ -56,17 +56,18 @@ class BitcoinTestFramework(object):
|
|||
# Connect the nodes as a "chain". This allows us
|
||||
# to split the network between nodes 1 and 2 to get
|
||||
# two halves that can work on competing chains.
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
|
||||
# If we joined network halves, connect the nodes from the joint
|
||||
# on outward. This ensures that chains are properly reorganised.
|
||||
if not split:
|
||||
connect_nodes_bi(self.nodes, 1, 2)
|
||||
sync_blocks(self.nodes[1:3])
|
||||
if do_mempool_sync:
|
||||
sync_mempools(self.nodes[1:3])
|
||||
if len(self.nodes) >= 4:
|
||||
connect_nodes_bi(self.nodes, 2, 3)
|
||||
if not split:
|
||||
connect_nodes_bi(self.nodes, 1, 2)
|
||||
sync_blocks(self.nodes[1:3])
|
||||
if do_mempool_sync:
|
||||
sync_mempools(self.nodes[1:3])
|
||||
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
connect_nodes_bi(self.nodes, 2, 3)
|
||||
self.is_network_split = split
|
||||
self.sync_all(do_mempool_sync)
|
||||
|
||||
|
|
|
@ -214,7 +214,7 @@ def wait_for_bitcoind_start(process, url, i):
|
|||
'''
|
||||
while True:
|
||||
if process.poll() is not None:
|
||||
raise Exception('%s exited with status %i during initialization' % (zcashd_binary(), process.returncode))
|
||||
raise Exception('%s node %d exited with status %i during initialization' % (zcashd_binary(), i, process.returncode))
|
||||
try:
|
||||
rpc = get_rpc_proxy(url, i)
|
||||
rpc.getblockcount()
|
||||
|
@ -307,15 +307,15 @@ def initialize_chain(test_dir, num_nodes, cachedir, cache_behavior='current'):
|
|||
wait_bitcoinds()
|
||||
for i in range(MAX_NODES):
|
||||
# record the system time at which the cache was regenerated
|
||||
with open(log_filename(cachedir, i, 'cache_config.json'), "w", encoding="utf8") as cache_conf_file:
|
||||
with open(node_file(cachedir, i, 'cache_config.json'), "w", encoding="utf8") as cache_conf_file:
|
||||
cache_config = { "cache_time": time.time() }
|
||||
cache_conf_json = json.dumps(cache_config, indent=4)
|
||||
cache_conf_file.write(cache_conf_json)
|
||||
|
||||
os.remove(log_filename(cachedir, i, "debug.log"))
|
||||
os.remove(log_filename(cachedir, i, "db.log"))
|
||||
os.remove(log_filename(cachedir, i, "peers.dat"))
|
||||
os.remove(log_filename(cachedir, i, "fee_estimates.dat"))
|
||||
os.remove(node_file(cachedir, i, "debug.log"))
|
||||
os.remove(node_file(cachedir, i, "db.log"))
|
||||
os.remove(node_file(cachedir, i, "peers.dat"))
|
||||
os.remove(node_file(cachedir, i, "fee_estimates.dat"))
|
||||
|
||||
def init_from_cache():
|
||||
for i in range(num_nodes):
|
||||
|
@ -351,7 +351,7 @@ def initialize_chain(test_dir, num_nodes, cachedir, cache_behavior='current'):
|
|||
for i in range(MAX_NODES):
|
||||
node_path = os.path.join(cachedir, 'node'+str(i))
|
||||
if os.path.isdir(node_path):
|
||||
if not os.path.isfile(log_filename(cachedir, i, 'cache_config.json')):
|
||||
if not os.path.isfile(node_file(cachedir, i, 'cache_config.json')):
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
@ -433,7 +433,7 @@ def assert_start_raises_init_error(i, dirname, extra_args=None, expected_msg=Non
|
|||
node = start_node(i, dirname, extra_args, stderr=log_stderr)
|
||||
stop_node(node, i)
|
||||
except Exception as e:
|
||||
assert ("%s exited" % (zcashd_binary(),)) in str(e) #node must have shutdown
|
||||
assert ("%s node %d exited" % (zcashd_binary(), i)) in str(e) # node must have shutdown
|
||||
if expected_msg is not None:
|
||||
log_stderr.seek(0)
|
||||
stderr = log_stderr.read().decode('utf-8')
|
||||
|
@ -461,8 +461,8 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None):
|
|||
raise
|
||||
return rpcs
|
||||
|
||||
def log_filename(dirname, n_node, logname):
|
||||
return os.path.join(dirname, "node"+str(n_node), "regtest", logname)
|
||||
def node_file(dirname, n_node, filename):
|
||||
return os.path.join(dirname, "node"+str(n_node), "regtest", filename)
|
||||
|
||||
def check_node(i):
|
||||
bitcoind_processes[i].poll()
|
||||
|
|
|
@ -126,7 +126,7 @@ class WalletAccountsTest(BitcoinTestFramework):
|
|||
# The UA contains the expected receiver kinds.
|
||||
self.check_receiver_types(ua0, ['p2pkh', 'sapling', 'orchard'])
|
||||
self.check_receiver_types(ua0_2, ['p2pkh', 'sapling', 'orchard'])
|
||||
self.check_receiver_types(ua0_3, [ 'sapling', 'orchard'])
|
||||
self.check_receiver_types(ua0_3, [ 'sapling', 'orchard'])
|
||||
self.check_receiver_types(ua0_4, ['p2pkh', 'orchard'])
|
||||
self.check_receiver_types(ua1, ['p2pkh', 'sapling', 'orchard'])
|
||||
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2022 The Zcash developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
get_coinbase_address,
|
||||
nuparams,
|
||||
start_nodes,
|
||||
wait_and_assert_operationid_status,
|
||||
NU5_BRANCH_ID
|
||||
)
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
def unspent_total(unspent):
|
||||
return sum((item['amount'] for item in unspent))
|
||||
|
||||
class WalletListUnspent(BitcoinTestFramework):
|
||||
def setup_nodes(self):
|
||||
return start_nodes(4, self.options.tmpdir, [[
|
||||
nuparams(NU5_BRANCH_ID, 201),
|
||||
]] * 4)
|
||||
|
||||
def matured_at_height(self, height):
|
||||
return unspent_total(self.nodes[0].listunspent(1, 999999, [], False, {}, height))
|
||||
|
||||
def run_test(self):
|
||||
def expected_matured_at_height(height):
|
||||
return (height-200)*10 + 250
|
||||
|
||||
assert_equal(self.nodes[0].getbalance(), expected_matured_at_height(200))
|
||||
assert_equal(self.nodes[1].getbalance(), expected_matured_at_height(200))
|
||||
|
||||
# Activate NU5
|
||||
self.nodes[1].generate(1) # height 201
|
||||
self.sync_all()
|
||||
assert_equal(self.nodes[0].getbalance(), expected_matured_at_height(201))
|
||||
# check balances from before the latest tx
|
||||
assert_equal(self.nodes[0].getbalance("", 1, False, False, 200), expected_matured_at_height(200))
|
||||
assert_equal(self.matured_at_height(200), expected_matured_at_height(200))
|
||||
|
||||
# Shield some coinbase funds so that they become spendable
|
||||
n1acct = self.nodes[1].z_getnewaccount()['account']
|
||||
n1uaddr = self.nodes[1].z_getaddressforaccount(n1acct)['address']
|
||||
opid = self.nodes[0].z_sendmany(
|
||||
get_coinbase_address(self.nodes[0]),
|
||||
[{'address': n1uaddr, 'amount': 10}],
|
||||
1, 0, 'AllowRevealedSenders')
|
||||
wait_and_assert_operationid_status(self.nodes[0], opid)
|
||||
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(2)
|
||||
self.sync_all() # height 203
|
||||
assert_equal(self.nodes[0].getbalance(), expected_matured_at_height(203) - 10)
|
||||
|
||||
assert_equal(
|
||||
self.nodes[1].z_getbalanceforaccount(n1acct, 1)['pools']['orchard']['valueZat'],
|
||||
Decimal('1000000000'))
|
||||
|
||||
# Get a bare legacy transparent address for node 0
|
||||
n0addr = self.nodes[0].getnewaddress()
|
||||
|
||||
# Send funds to the node 0 address so we have transparent funds to spend.
|
||||
opid = self.nodes[1].z_sendmany(
|
||||
n1uaddr,
|
||||
[{'address': n0addr, 'amount': 10}],
|
||||
1, 0, 'AllowRevealedRecipients')
|
||||
wait_and_assert_operationid_status(self.nodes[1], opid)
|
||||
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(2)
|
||||
self.sync_all() # height 205
|
||||
assert_equal(self.nodes[0].getbalance(), expected_matured_at_height(205) - 10 + 10)
|
||||
|
||||
# We will then perform several spends, and then check the list of
|
||||
# unspent notes as of various heights.
|
||||
|
||||
opid = self.nodes[0].z_sendmany(
|
||||
'ANY_TADDR',
|
||||
# FIXME: #6262 The amount here _should_ be 2, but because of a
|
||||
# bug in the selector for `ANY_TADDR`, it’s selecting
|
||||
# transparent coinbase, which also means we can’t have
|
||||
# change. When that bug is fixed, the test should fail
|
||||
# here, and we can switch it back to 2 (and cascade the
|
||||
# corrected amounts mentioned below.
|
||||
[{'address': n1uaddr, 'amount': 10}],
|
||||
1, 0, 'AllowRevealedSenders')
|
||||
wait_and_assert_operationid_status(self.nodes[0], opid)
|
||||
|
||||
self.nodes[0].generate(2)
|
||||
self.sync_all() # height 207
|
||||
# FIXME: #6262, should be `expected_matured_at_height(207) - 10 + 10 - 2`
|
||||
assert_equal(self.nodes[0].getbalance(), expected_matured_at_height(207) - 10 + 10 - 10)
|
||||
|
||||
opid = self.nodes[0].z_sendmany(
|
||||
'ANY_TADDR',
|
||||
# FIXME: Should be 3 (see above)
|
||||
[{'address': n1uaddr, 'amount': 10}],
|
||||
1, 0, 'AllowRevealedSenders')
|
||||
wait_and_assert_operationid_status(self.nodes[0], opid)
|
||||
|
||||
self.nodes[0].generate(2)
|
||||
self.sync_all() # height 209
|
||||
# FIXME: #6262, should be `expected_matured_at_height(209) - 10 + 10 - 2 - 3`
|
||||
assert_equal(self.nodes[0].getbalance(), expected_matured_at_height(209) - 10 + 10 - 10 - 10)
|
||||
|
||||
opid = self.nodes[0].z_sendmany(
|
||||
'ANY_TADDR',
|
||||
# FIXME: Should be 5 (see above)
|
||||
[{'address': n1uaddr, 'amount': 10}],
|
||||
1, 0, 'AllowRevealedSenders')
|
||||
wait_and_assert_operationid_status(self.nodes[0], opid)
|
||||
|
||||
self.nodes[0].generate(2)
|
||||
self.sync_all() # height 211
|
||||
# FIXME: #6262, should be `expected_matured_at_height(211) - 10 + 10 - 2 - 3 - 5`
|
||||
assert_equal(self.nodes[0].getbalance(), expected_matured_at_height(211) - 10 + 10 - 10 - 10 - 10)
|
||||
|
||||
# check balances at various past points in the chain
|
||||
# FIXME: #6262, change the comparison amounts when the above changes are made.
|
||||
assert_equal(self.matured_at_height(205), expected_matured_at_height(205) - 10 + 10)
|
||||
assert_equal(self.matured_at_height(207), expected_matured_at_height(207) - 10 + 10 - 10)
|
||||
assert_equal(self.matured_at_height(209), expected_matured_at_height(209) - 10 + 10 - 10 - 10)
|
||||
assert_equal(self.matured_at_height(211), expected_matured_at_height(211) - 10 + 10 - 10 - 10 - 10)
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletListUnspent().main()
|
|
@ -51,6 +51,10 @@ criteria = "safe-to-deploy"
|
|||
version = "0.7.6"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.aho-corasick]]
|
||||
version = "0.7.19"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.anyhow]]
|
||||
version = "1.0.56"
|
||||
criteria = "safe-to-deploy"
|
||||
|
|
|
@ -137,12 +137,8 @@ def ensure_no_dot_so_in_depends():
|
|||
return exit_code == 0
|
||||
|
||||
def util_test():
|
||||
python = []
|
||||
if os.path.isfile('/usr/local/bin/python3'):
|
||||
python = ['/usr/local/bin/python3']
|
||||
|
||||
return subprocess.call(
|
||||
python + [repofile('src/test/bitcoin-util-test.py')],
|
||||
[sys.executable, repofile('src/test/bitcoin-util-test.py')],
|
||||
cwd=repofile('src'),
|
||||
env={'PYTHONPATH': repofile('src/test'), 'srcdir': repofile('src')}
|
||||
) == 0
|
||||
|
|
|
@ -4,38 +4,59 @@
|
|||
# bdb 18.1.40 2020-09-01
|
||||
#
|
||||
|
||||
native_cxxbridge 1.0.80 2022-12-10
|
||||
native_cxxbridge 1.0.81 2022-12-10
|
||||
native_cxxbridge 1.0.82 2022-12-10
|
||||
native_cxxbridge 1.0.83 2022-12-10
|
||||
native_rust 1.65.0 2022-12-10
|
||||
rustcxx 1.0.80 2022-12-10
|
||||
rustcxx 1.0.81 2022-12-10
|
||||
rustcxx 1.0.82 2022-12-10
|
||||
rustcxx 1.0.83 2022-12-10
|
||||
utfcpp 3.2.2 2022-12-10
|
||||
|
||||
# Ccache 4.0 requires adding CMake to the depends system.
|
||||
native_ccache 4.0 2022-11-01
|
||||
native_ccache 4.1 2022-11-01
|
||||
native_ccache 4.2 2022-11-01
|
||||
native_ccache 4.2.1 2022-11-01
|
||||
native_ccache 4.3 2022-11-01
|
||||
native_ccache 4.4 2022-11-01
|
||||
native_ccache 4.4.1 2022-11-01
|
||||
native_ccache 4.4.2 2022-11-01
|
||||
native_ccache 4.5 2022-11-01
|
||||
native_ccache 4.5.1 2022-11-01
|
||||
native_ccache 4.6 2022-11-01
|
||||
native_ccache 4.6.1 2022-11-01
|
||||
native_ccache 4.6.2 2022-11-01
|
||||
native_ccache 4.6.3 2022-11-01
|
||||
native_ccache 4.7 2022-11-01
|
||||
native_ccache 4.0 2022-12-10
|
||||
native_ccache 4.1 2022-12-10
|
||||
native_ccache 4.2 2022-12-10
|
||||
native_ccache 4.2.1 2022-12-10
|
||||
native_ccache 4.3 2022-12-10
|
||||
native_ccache 4.4 2022-12-10
|
||||
native_ccache 4.4.1 2022-12-10
|
||||
native_ccache 4.4.2 2022-12-10
|
||||
native_ccache 4.5 2022-12-10
|
||||
native_ccache 4.5.1 2022-12-10
|
||||
native_ccache 4.6 2022-12-10
|
||||
native_ccache 4.6.1 2022-12-10
|
||||
native_ccache 4.6.2 2022-12-10
|
||||
native_ccache 4.6.3 2022-12-10
|
||||
native_ccache 4.7 2022-12-10
|
||||
native_ccache 4.7.1 2022-12-10
|
||||
native_ccache 4.7.2 2022-12-10
|
||||
native_ccache 4.7.3 2022-12-10
|
||||
native_ccache 4.7.4 2022-12-10
|
||||
|
||||
# Clang and Rust are currently pinned to LLVM 14
|
||||
libcxx 15.0.0 2022-11-01
|
||||
libcxx 15.0.1 2022-11-01
|
||||
libcxx 15.0.2 2022-11-01
|
||||
libcxx 15.0.3 2022-11-01
|
||||
native_clang 15.0.0 2022-11-01
|
||||
native_clang 15.0.1 2022-11-01
|
||||
native_clang 15.0.2 2022-11-01
|
||||
native_clang 15.0.3 2022-11-01
|
||||
libcxx 15.0.0 2022-12-10
|
||||
libcxx 15.0.1 2022-12-10
|
||||
libcxx 15.0.2 2022-12-10
|
||||
libcxx 15.0.3 2022-12-10
|
||||
libcxx 15.0.4 2022-12-10
|
||||
libcxx 15.0.5 2022-12-10
|
||||
libcxx 15.0.6 2022-12-10
|
||||
native_clang 15.0.0 2022-12-10
|
||||
native_clang 15.0.1 2022-12-10
|
||||
native_clang 15.0.2 2022-12-10
|
||||
native_clang 15.0.3 2022-12-10
|
||||
native_clang 15.0.4 2022-12-10
|
||||
native_clang 15.0.5 2022-12-10
|
||||
native_clang 15.0.6 2022-12-10
|
||||
|
||||
# We're never updating to this version
|
||||
bdb 18.1.40 2024-02-01
|
||||
|
||||
# Google Test 1.10.0 requires adding CMake to the depends system.
|
||||
googletest 1.10.0 2022-11-01
|
||||
googletest 1.11.0 2022-11-01
|
||||
googletest 1.12.0 2022-11-01
|
||||
googletest 1.12.1 2022-11-01
|
||||
googletest 1.10.0 2022-12-10
|
||||
googletest 1.11.0 2022-12-10
|
||||
googletest 1.12.0 2022-12-10
|
||||
googletest 1.12.1 2022-12-10
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
#include "chain.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "txdb.h"
|
||||
|
||||
#include <rust/metrics.h>
|
||||
|
||||
/**
|
||||
* CChain implementation
|
||||
*/
|
||||
|
@ -58,6 +63,50 @@ const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
|
|||
return pindex;
|
||||
}
|
||||
|
||||
void CBlockIndex::TrimSolution()
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
// We can correctly trim a solution as soon as the block index entry has been added
|
||||
// to leveldb. Updates to the block index entry (to update validity status) will be
|
||||
// handled by re-reading the solution from the existing db entry. It does not help to
|
||||
// try to avoid these reads by gating trimming on the validity status: the re-reads are
|
||||
// efficient anyway because of caching in leveldb, and most of them are unavoidable.
|
||||
if (HasSolution()) {
|
||||
MetricsIncrementCounter("zcashd.debug.memory.trimmed_equihash_solutions");
|
||||
std::vector<unsigned char> empty;
|
||||
nSolution.swap(empty);
|
||||
}
|
||||
}
|
||||
|
||||
CBlockHeader CBlockIndex::GetBlockHeader() const
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
CBlockHeader header;
|
||||
header.nVersion = nVersion;
|
||||
if (pprev) {
|
||||
header.hashPrevBlock = pprev->GetBlockHash();
|
||||
}
|
||||
header.hashMerkleRoot = hashMerkleRoot;
|
||||
header.hashBlockCommitments = hashBlockCommitments;
|
||||
header.nTime = nTime;
|
||||
header.nBits = nBits;
|
||||
header.nNonce = nNonce;
|
||||
if (HasSolution()) {
|
||||
header.nSolution = nSolution;
|
||||
} else {
|
||||
MetricsIncrementCounter("zcashd.debug.blocktree.trimmed_equihash_read_dbindex");
|
||||
CDiskBlockIndex dbindex;
|
||||
if (!pblocktree->ReadDiskBlockIndex(GetBlockHash(), dbindex)) {
|
||||
LogPrintf("%s: Failed to read index entry", __func__);
|
||||
throw std::runtime_error("Failed to read index entry");
|
||||
}
|
||||
header.nSolution = dbindex.GetSolution();
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
|
||||
int static inline InvertLowestOne(int n) { return n & (n - 1); }
|
||||
|
||||
|
|
85
src/chain.h
85
src/chain.h
|
@ -12,10 +12,13 @@
|
|||
#include "pow.h"
|
||||
#include "tinyformat.h"
|
||||
#include "uint256.h"
|
||||
#include "util/strencodings.h"
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <rust/metrics.h>
|
||||
|
||||
static const int SPROUT_VALUE_VERSION = 1001400;
|
||||
static const int SAPLING_VALUE_VERSION = 1010100;
|
||||
static const int CHAIN_HISTORY_ROOT_VERSION = 2010200;
|
||||
|
@ -308,8 +311,13 @@ public:
|
|||
unsigned int nTime;
|
||||
unsigned int nBits;
|
||||
uint256 nNonce;
|
||||
protected:
|
||||
// The Equihash solution, if it is stored. Once we know that the block index
|
||||
// entry is present in leveldb, this field can be cleared via the TrimSolution
|
||||
// method to save memory.
|
||||
std::vector<unsigned char> nSolution;
|
||||
|
||||
public:
|
||||
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
|
||||
uint32_t nSequenceId;
|
||||
|
||||
|
@ -366,6 +374,7 @@ public:
|
|||
nBits = block.nBits;
|
||||
nNonce = block.nNonce;
|
||||
nSolution = block.nSolution;
|
||||
MetricsIncrementCounter("zcashd.debug.memory.allocated_equihash_solutions");
|
||||
}
|
||||
|
||||
CDiskBlockPos GetBlockPos() const {
|
||||
|
@ -386,23 +395,15 @@ public:
|
|||
return ret;
|
||||
}
|
||||
|
||||
CBlockHeader GetBlockHeader() const
|
||||
{
|
||||
CBlockHeader block;
|
||||
block.nVersion = nVersion;
|
||||
if (pprev)
|
||||
block.hashPrevBlock = pprev->GetBlockHash();
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashBlockCommitments = hashBlockCommitments;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
block.nSolution = nSolution;
|
||||
return block;
|
||||
}
|
||||
//! Get the block header for this block index. Requires cs_main.
|
||||
CBlockHeader GetBlockHeader() const;
|
||||
|
||||
//! Clear the Equihash solution to save memory. Requires cs_main.
|
||||
void TrimSolution();
|
||||
|
||||
uint256 GetBlockHash() const
|
||||
{
|
||||
assert(phashBlock);
|
||||
return *phashBlock;
|
||||
}
|
||||
|
||||
|
@ -429,10 +430,11 @@ public:
|
|||
|
||||
std::string ToString() const
|
||||
{
|
||||
return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
|
||||
return strprintf("CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s, HasSolution=%s)",
|
||||
pprev, nHeight,
|
||||
hashMerkleRoot.ToString(),
|
||||
GetBlockHash().ToString());
|
||||
phashBlock ? GetBlockHash().ToString() : "(nil)",
|
||||
HasSolution());
|
||||
}
|
||||
|
||||
//! Check whether this block index entry is valid up to the passed validity level.
|
||||
|
@ -444,6 +446,12 @@ public:
|
|||
return ((nStatus & BLOCK_VALID_MASK) >= nUpTo);
|
||||
}
|
||||
|
||||
//! Is the Equihash solution stored?
|
||||
bool HasSolution() const
|
||||
{
|
||||
return !nSolution.empty();
|
||||
}
|
||||
|
||||
//! Raise the validity level of this block index entry.
|
||||
//! Returns true if the validity was changed.
|
||||
bool RaiseValidity(enum BlockStatus nUpTo)
|
||||
|
@ -482,8 +490,11 @@ public:
|
|||
hashPrev = uint256();
|
||||
}
|
||||
|
||||
explicit CDiskBlockIndex(const CBlockIndex* pindex) : CBlockIndex(*pindex) {
|
||||
explicit CDiskBlockIndex(const CBlockIndex* pindex, std::function<std::vector<unsigned char>()> getSolution) : CBlockIndex(*pindex) {
|
||||
hashPrev = (pprev ? pprev->GetBlockHash() : uint256());
|
||||
if (!HasSolution()) {
|
||||
nSolution = getSolution();
|
||||
}
|
||||
}
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
@ -564,20 +575,31 @@ public:
|
|||
// them to CBlockTreeDB::LoadBlockIndexGuts() in txdb.cpp :)
|
||||
}
|
||||
|
||||
uint256 GetBlockHash() const
|
||||
//! Get the block header for this block index.
|
||||
CBlockHeader GetBlockHeader() const
|
||||
{
|
||||
CBlockHeader block;
|
||||
block.nVersion = nVersion;
|
||||
block.hashPrevBlock = hashPrev;
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashBlockCommitments = hashBlockCommitments;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
block.nSolution = nSolution;
|
||||
return block.GetHash();
|
||||
CBlockHeader header;
|
||||
header.nVersion = nVersion;
|
||||
header.hashPrevBlock = hashPrev;
|
||||
header.hashMerkleRoot = hashMerkleRoot;
|
||||
header.hashBlockCommitments = hashBlockCommitments;
|
||||
header.nTime = nTime;
|
||||
header.nBits = nBits;
|
||||
header.nNonce = nNonce;
|
||||
header.nSolution = nSolution;
|
||||
return header;
|
||||
}
|
||||
|
||||
uint256 GetBlockHash() const
|
||||
{
|
||||
return GetBlockHeader().GetHash();
|
||||
}
|
||||
|
||||
std::vector<unsigned char> GetSolution() const
|
||||
{
|
||||
assert(HasSolution());
|
||||
return nSolution;
|
||||
}
|
||||
|
||||
std::string ToString() const
|
||||
{
|
||||
|
@ -588,6 +610,13 @@ public:
|
|||
hashPrev.ToString());
|
||||
return str;
|
||||
}
|
||||
|
||||
private:
|
||||
//! This method should not be called on a CDiskBlockIndex.
|
||||
void TrimSolution()
|
||||
{
|
||||
assert(!"called CDiskBlockIndex::TrimSolution");
|
||||
}
|
||||
};
|
||||
|
||||
/** An in-memory indexed chain of blocks. */
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
|
||||
#define CLIENT_VERSION_MAJOR 5
|
||||
#define CLIENT_VERSION_MINOR 3
|
||||
#define CLIENT_VERSION_REVISION 0
|
||||
#define CLIENT_VERSION_REVISION 2
|
||||
#define CLIENT_VERSION_BUILD 50
|
||||
|
||||
//! Set to true for release, false for prerelease or test build
|
||||
|
|
|
@ -281,7 +281,7 @@ private:
|
|||
*
|
||||
* First, epoch_check decrements and checks the cheap heuristic, and then does
|
||||
* a more expensive scan if the cheap heuristic runs out. If the expensive
|
||||
* scan suceeds, the epochs are aged and old elements are allow_erased. The
|
||||
* scan succeeds, the epochs are aged and old elements are allow_erased. The
|
||||
* cheap heuristic is reset to retrigger after the worst case growth of the
|
||||
* current epoch's elements would exceed the epoch_size.
|
||||
*/
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
// Per https://zips.z.cash/zip-0200
|
||||
// Shut down nodes running this version of code, 16 weeks' worth of blocks after the estimated
|
||||
// release block height. A warning is shown during the 14 days' worth of blocks prior to shut down.
|
||||
static const int APPROX_RELEASE_HEIGHT = 1849900;
|
||||
static const int APPROX_RELEASE_HEIGHT = 1900500;
|
||||
static const int RELEASE_TO_DEPRECATION_WEEKS = 16;
|
||||
static const int EXPECTED_BLOCKS_PER_HOUR = 3600 / Consensus::POST_BLOSSOM_POW_TARGET_SPACING;
|
||||
static_assert(EXPECTED_BLOCKS_PER_HOUR == 48, "The value of Consensus::POST_BLOSSOM_POW_TARGET_SPACING was chosen such that this assertion holds.");
|
||||
|
|
|
@ -438,6 +438,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
|||
strUsage += HelpMessageOpt("-prometheusport=<port>", _("Expose node metrics in the Prometheus exposition format. "
|
||||
"An HTTP listener will be started on <port>, which responds to GET requests on any request path. "
|
||||
"Use -metricsallowip and -metricsbind to control access."));
|
||||
strUsage += HelpMessageOpt("-debugmetrics", _("Include debug metrics in exposed node metrics."));
|
||||
|
||||
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
|
||||
if (showDebug)
|
||||
|
@ -1471,10 +1472,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||
metricsBindCstr = metricsBind.c_str();
|
||||
}
|
||||
|
||||
bool debugMetrics = GetBoolArg("-debugmetrics", false);
|
||||
|
||||
// Start up the metrics runtime. This spins off a Rust thread that runs
|
||||
// the Prometheus exporter. We just let this thread die at process end.
|
||||
LogPrintf("metrics thread start");
|
||||
if (!metrics_run(metricsBindCstr, vAllowCstr.data(), vAllowCstr.size(), prometheusPort)) {
|
||||
if (!metrics_run(metricsBindCstr, vAllowCstr.data(), vAllowCstr.size(), prometheusPort, debugMetrics)) {
|
||||
return InitError(strprintf(_("Failed to start Prometheus metrics exporter")));
|
||||
}
|
||||
}
|
||||
|
|
14
src/main.cpp
14
src/main.cpp
|
@ -3715,7 +3715,7 @@ bool static FlushStateToDisk(
|
|||
vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it]));
|
||||
it = setDirtyFileInfo.erase(it);
|
||||
}
|
||||
std::vector<const CBlockIndex*> vBlocks;
|
||||
std::vector<CBlockIndex*> vBlocks;
|
||||
vBlocks.reserve(setDirtyBlockIndex.size());
|
||||
for (set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) {
|
||||
vBlocks.push_back(*it);
|
||||
|
@ -3724,6 +3724,12 @@ bool static FlushStateToDisk(
|
|||
if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) {
|
||||
return AbortNode(state, "Files to write to block index database");
|
||||
}
|
||||
// Now that we have written the block indices to the database, we do not
|
||||
// need to store solutions for these CBlockIndex objects in memory.
|
||||
// cs_main must be held here.
|
||||
for (CBlockIndex *pblockindex : vBlocks) {
|
||||
pblockindex->TrimSolution();
|
||||
}
|
||||
}
|
||||
// Finally remove any pruned files
|
||||
if (fFlushForPrune)
|
||||
|
@ -6128,7 +6134,11 @@ void static CheckBlockIndex(const Consensus::Params& consensusParams)
|
|||
}
|
||||
}
|
||||
}
|
||||
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow
|
||||
// try {
|
||||
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow
|
||||
// } catch (const runtime_error&) {
|
||||
// assert(!"Failed to read index entry");
|
||||
// }
|
||||
// End: actual consistency checks.
|
||||
|
||||
// Try descending into the first subnode.
|
||||
|
|
14
src/rest.cpp
14
src/rest.cpp
|
@ -141,6 +141,7 @@ static bool rest_headers(HTTPRequest* req,
|
|||
|
||||
std::vector<const CBlockIndex *> headers;
|
||||
headers.reserve(count);
|
||||
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
|
||||
{
|
||||
LOCK(cs_main);
|
||||
BlockMap::const_iterator it = mapBlockIndex.find(hash);
|
||||
|
@ -151,11 +152,16 @@ static bool rest_headers(HTTPRequest* req,
|
|||
break;
|
||||
pindex = chainActive.Next(pindex);
|
||||
}
|
||||
}
|
||||
|
||||
CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
|
||||
for (const CBlockIndex *pindex : headers) {
|
||||
ssHeader << pindex->GetBlockHeader();
|
||||
if (rf == RF_BINARY || rf == RF_HEX) {
|
||||
try {
|
||||
for (const CBlockIndex *pindex : headers) {
|
||||
ssHeader << pindex->GetBlockHeader();
|
||||
}
|
||||
} catch (const std::runtime_error&) {
|
||||
return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Failed to read index entry");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (rf) {
|
||||
|
|
|
@ -118,7 +118,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex)
|
|||
result.pushKV("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex());
|
||||
result.pushKV("time", (int64_t)blockindex->nTime);
|
||||
result.pushKV("nonce", blockindex->nNonce.GetHex());
|
||||
result.pushKV("solution", HexStr(blockindex->nSolution));
|
||||
result.pushKV("solution", HexStr(blockindex->GetBlockHeader().nSolution));
|
||||
result.pushKV("bits", strprintf("%08x", blockindex->nBits));
|
||||
result.pushKV("difficulty", GetDifficulty(blockindex));
|
||||
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
|
||||
|
@ -684,15 +684,18 @@ UniValue getblockheader(const UniValue& params, bool fHelp)
|
|||
|
||||
CBlockIndex* pblockindex = mapBlockIndex[hash];
|
||||
|
||||
if (!fVerbose)
|
||||
{
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssBlock << pblockindex->GetBlockHeader();
|
||||
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
|
||||
return strHex;
|
||||
try {
|
||||
if (!fVerbose) {
|
||||
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssBlock << pblockindex->GetBlockHeader();
|
||||
std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
|
||||
return strHex;
|
||||
} else {
|
||||
return blockheaderToJSON(pblockindex);
|
||||
}
|
||||
} catch (const runtime_error&) {
|
||||
throw JSONRPCError(RPC_DATABASE_ERROR, "Failed to read index entry");
|
||||
}
|
||||
|
||||
return blockheaderToJSON(pblockindex);
|
||||
}
|
||||
|
||||
UniValue getblock(const UniValue& params, bool fHelp)
|
||||
|
|
|
@ -93,7 +93,7 @@ UniValue getinfo(const UniValue& params, bool fHelp)
|
|||
#ifdef ENABLE_WALLET
|
||||
if (pwalletMain) {
|
||||
obj.pushKV("walletversion", pwalletMain->GetVersion());
|
||||
obj.pushKV("balance", ValueFromAmount(pwalletMain->GetBalance()));
|
||||
obj.pushKV("balance", ValueFromAmount(pwalletMain->GetBalance(std::nullopt)));
|
||||
}
|
||||
#endif
|
||||
obj.pushKV("blocks", (int)chainActive.Height());
|
||||
|
|
|
@ -550,6 +550,55 @@ std::string experimentalDisabledHelpMsg(const std::string& rpc, const std::vecto
|
|||
+ config;
|
||||
}
|
||||
|
||||
std::string asOfHeightMessage(bool hasMinconf) {
|
||||
std::string minconfInteraction = hasMinconf
|
||||
? " `minconf` must be at least 1 when `asOfHeight` is provided.\n"
|
||||
: "";
|
||||
return
|
||||
"asOfHeight (numeric, optional, default=-1) Execute the query as if it\n"
|
||||
" were run when the blockchain was at the height specified by\n"
|
||||
" this argument. The default is to use the entire blockchain\n"
|
||||
" that the node is aware of. -1 can be used as in other RPC\n"
|
||||
" calls to indicate the current height (including the\n"
|
||||
" mempool), but this does not support negative values in\n"
|
||||
" general. A “future” height will fall back to the current\n"
|
||||
" height. Any explicit value will cause the mempool to be\n"
|
||||
" ignored, meaning no unconfirmed tx will be considered.\n"
|
||||
+ minconfInteraction;
|
||||
}
|
||||
|
||||
std::optional<int> parseAsOfHeight(const UniValue& params, int index) {
|
||||
std::optional<int> asOfHeight;
|
||||
if (params.size() > index) {
|
||||
auto requestedHeight = params[index].get_int();
|
||||
if (requestedHeight == -1) {
|
||||
// the default, do nothing
|
||||
} else if (requestedHeight < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Can not perform the query as of a negative block height");
|
||||
} else if (requestedHeight == 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Can not perform the query as of the genesis block");
|
||||
} else {
|
||||
asOfHeight = requestedHeight;
|
||||
}
|
||||
}
|
||||
return asOfHeight;
|
||||
}
|
||||
|
||||
int parseMinconf(int defaultValue, const UniValue& params, int index, const std::optional<int>& asOfHeight) {
|
||||
int nMinDepth = defaultValue;
|
||||
if (params.size() > index) {
|
||||
auto requestedDepth = params[index].get_int();
|
||||
if (requestedDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
} else if (requestedDepth == 0 && asOfHeight.has_value()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Require a minimum of 1 confirmation when `asOfHeight` is provided");
|
||||
} else {
|
||||
nMinDepth = requestedDepth;
|
||||
}
|
||||
}
|
||||
return nMinDepth;
|
||||
}
|
||||
|
||||
void RPCRegisterTimerInterface(RPCTimerInterface *iface)
|
||||
{
|
||||
timerInterfaces.push_back(iface);
|
||||
|
|
|
@ -192,6 +192,10 @@ std::string JSONRPCExecBatch(const UniValue& vReq);
|
|||
|
||||
extern std::string experimentalDisabledHelpMsg(const std::string& rpc, const std::vector<std::string>& enableArgs);
|
||||
|
||||
std::string asOfHeightMessage(bool hasMinconf);
|
||||
std::optional<int> parseAsOfHeight(const UniValue& params, int index);
|
||||
int parseMinconf(int defaultValue, const UniValue& params, int index, const std::optional<int>& asOfHeight);
|
||||
|
||||
extern int interpretHeightArg(int nHeight, int currentHeight);
|
||||
extern int parseHeightArg(const std::string& strHeight, int currentHeight);
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ bool metrics_run(
|
|||
const char* bind_address,
|
||||
const char* const* allow_ips,
|
||||
size_t allow_ips_len,
|
||||
uint16_t prometheus_port);
|
||||
uint16_t prometheus_port,
|
||||
bool debug_metrics);
|
||||
|
||||
struct MetricsCallsite;
|
||||
typedef struct MetricsCallsite MetricsCallsite;
|
||||
|
|
|
@ -1,18 +1,48 @@
|
|||
use libc::{c_char, c_double};
|
||||
use metrics::{try_recorder, Key, Label};
|
||||
use metrics_exporter_prometheus::PrometheusBuilder;
|
||||
use metrics_exporter_prometheus::{BuildError, PrometheusBuilder};
|
||||
use metrics_util::layers::{FilterLayer, Stack};
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::thread;
|
||||
|
||||
use tracing::error;
|
||||
|
||||
/// Builds the recorder and exporter, applies the given filters to the recorder, and
|
||||
/// installs both of them globally.
|
||||
fn metrics_install(builder: PrometheusBuilder, filters: &[&str]) -> Result<(), BuildError> {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.map_err(|e| BuildError::FailedToCreateRuntime(e.to_string()))?;
|
||||
|
||||
let (recorder, exporter) = {
|
||||
let _g = runtime.enter();
|
||||
builder.build()?
|
||||
};
|
||||
|
||||
thread::Builder::new()
|
||||
.name("zc-metrics".to_string())
|
||||
.spawn(move || runtime.block_on(exporter))
|
||||
.map_err(|e| BuildError::FailedToCreateRuntime(e.to_string()))?;
|
||||
|
||||
Stack::new(recorder)
|
||||
.push(FilterLayer::from_patterns(filters))
|
||||
.install()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_run(
|
||||
bind_address: *const c_char,
|
||||
allow_ips: *const *const c_char,
|
||||
allow_ips_len: usize,
|
||||
prometheus_port: u16,
|
||||
debug_metrics: bool,
|
||||
) -> bool {
|
||||
// Convert the C string IPs to Rust strings.
|
||||
let allow_ips = unsafe { slice::from_raw_parts(allow_ips, allow_ips_len) };
|
||||
|
@ -54,6 +84,13 @@ pub extern "C" fn metrics_run(
|
|||
prometheus_port,
|
||||
);
|
||||
|
||||
// Metrics matching any of these filters will be discarded.
|
||||
let filters = if debug_metrics {
|
||||
&[][..]
|
||||
} else {
|
||||
&["zcashd.debug."]
|
||||
};
|
||||
|
||||
allow_ips
|
||||
.into_iter()
|
||||
.try_fold(
|
||||
|
@ -66,7 +103,7 @@ pub extern "C" fn metrics_run(
|
|||
},
|
||||
)
|
||||
.and_then(|builder| {
|
||||
builder.install().map_err(|e| {
|
||||
metrics_install(builder, filters).map_err(|e| {
|
||||
error!("Could not install Prometheus exporter: {}", e);
|
||||
e
|
||||
})
|
||||
|
|
|
@ -235,7 +235,7 @@ BOOST_AUTO_TEST_CASE(merkle_test)
|
|||
// If no mutation was done (once for every ntx value), try up to 16 branches.
|
||||
if (mutate == 0) {
|
||||
for (int loop = 0; loop < std::min(ntx, 16); loop++) {
|
||||
// If ntx <= 16, try all branches. Otherise, try 16 random ones.
|
||||
// If ntx <= 16, try all branches. Otherwise, try 16 random ones.
|
||||
int mtx = loop;
|
||||
if (ntx > 16) {
|
||||
mtx = InsecureRandRange(ntx);
|
||||
|
|
59
src/txdb.cpp
59
src/txdb.cpp
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <rust/metrics.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// NOTE: Per issue #3277, do not use the prefix 'X' or 'x' as they were
|
||||
|
@ -316,7 +318,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
|
|||
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
|
||||
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) const {
|
||||
return Read(make_pair(DB_BLOCK_FILES, nFile), info);
|
||||
}
|
||||
|
||||
|
@ -327,12 +329,12 @@ bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
|
|||
return Erase(DB_REINDEX_FLAG);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadReindexing(bool &fReindexing) {
|
||||
bool CBlockTreeDB::ReadReindexing(bool &fReindexing) const {
|
||||
fReindexing = Exists(DB_REINDEX_FLAG);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
|
||||
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) const {
|
||||
return Read(DB_LAST_BLOCK, nFile);
|
||||
}
|
||||
|
||||
|
@ -382,14 +384,31 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) {
|
||||
bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<CBlockIndex*>& blockinfo) {
|
||||
MetricsIncrementCounter("zcashd.debug.blocktree.write_batch");
|
||||
CDBBatch batch(*this);
|
||||
for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
|
||||
batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second);
|
||||
}
|
||||
batch.Write(DB_LAST_BLOCK, nLastFile);
|
||||
for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
|
||||
batch.Write(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it));
|
||||
std::pair<char, uint256> key = make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash());
|
||||
try {
|
||||
CDiskBlockIndex dbindex {*it, [this, &key]() {
|
||||
MetricsIncrementCounter("zcashd.debug.blocktree.write_batch_read_dbindex");
|
||||
// It can happen that the index entry is written, then the Equihash solution is cleared from memory,
|
||||
// then the index entry is rewritten. In that case we must read the solution from the old entry.
|
||||
CDiskBlockIndex dbindex_old;
|
||||
if (!Read(key, dbindex_old)) {
|
||||
LogPrintf("%s: Failed to read index entry", __func__);
|
||||
throw runtime_error("Failed to read index entry");
|
||||
}
|
||||
return dbindex_old.GetSolution();
|
||||
}};
|
||||
batch.Write(key, dbindex);
|
||||
} catch (const runtime_error&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return WriteBatch(batch, true);
|
||||
}
|
||||
|
@ -402,7 +421,11 @@ bool CBlockTreeDB::EraseBatchSync(const std::vector<const CBlockIndex*>& blockin
|
|||
return WriteBatch(batch, true);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
|
||||
bool CBlockTreeDB::ReadDiskBlockIndex(const uint256 &blockhash, CDiskBlockIndex &dbindex) const {
|
||||
return Read(make_pair(DB_BLOCK_INDEX, blockhash), dbindex);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) const {
|
||||
return Read(make_pair(DB_TXINDEX, txid), pos);
|
||||
}
|
||||
|
||||
|
@ -491,7 +514,7 @@ bool CBlockTreeDB::ReadAddressIndex(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) {
|
||||
bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) const {
|
||||
return Read(make_pair(DB_SPENTINDEX, key), value);
|
||||
}
|
||||
|
||||
|
@ -547,7 +570,7 @@ bool CBlockTreeDB::WriteTimestampBlockIndex(const CTimestampBlockIndexKey &block
|
|||
return WriteBatch(batch);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp)
|
||||
bool CBlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp) const
|
||||
{
|
||||
CTimestampBlockIndexValue(lts);
|
||||
if (!Read(std::make_pair(DB_BLOCKHASHINDEX, hash), lts))
|
||||
|
@ -562,7 +585,7 @@ bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
|||
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
|
||||
bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) const {
|
||||
char ch;
|
||||
if (!Read(std::make_pair(DB_FLAG, name), ch))
|
||||
return false;
|
||||
|
@ -599,7 +622,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(
|
|||
pindexNew->nTime = diskindex.nTime;
|
||||
pindexNew->nBits = diskindex.nBits;
|
||||
pindexNew->nNonce = diskindex.nNonce;
|
||||
pindexNew->nSolution = diskindex.nSolution;
|
||||
// the Equihash solution will be loaded lazily from the dbindex entry
|
||||
pindexNew->nStatus = diskindex.nStatus;
|
||||
pindexNew->nCachedBranchId = diskindex.nCachedBranchId;
|
||||
pindexNew->nTx = diskindex.nTx;
|
||||
|
@ -612,10 +635,22 @@ bool CBlockTreeDB::LoadBlockIndexGuts(
|
|||
pindexNew->hashAuthDataRoot = diskindex.hashAuthDataRoot;
|
||||
|
||||
// Consistency checks
|
||||
auto header = pindexNew->GetBlockHeader();
|
||||
CBlockHeader header;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
try {
|
||||
header = pindexNew->GetBlockHeader();
|
||||
} catch (const runtime_error&) {
|
||||
return error("LoadBlockIndex(): failed to read index entry: diskindex hash = %s",
|
||||
diskindex.GetBlockHash().ToString());
|
||||
}
|
||||
}
|
||||
if (header.GetHash() != diskindex.GetBlockHash())
|
||||
return error("LoadBlockIndex(): inconsistent header vs diskindex hash: header hash = %s, diskindex hash = %s",
|
||||
header.GetHash().ToString(), diskindex.GetBlockHash().ToString());
|
||||
if (header.GetHash() != pindexNew->GetBlockHash())
|
||||
return error("LoadBlockIndex(): block header inconsistency detected: on-disk = %s, in-memory = %s",
|
||||
diskindex.ToString(), pindexNew->ToString());
|
||||
diskindex.ToString(), pindexNew->ToString());
|
||||
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus()))
|
||||
return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());
|
||||
|
||||
|
|
17
src/txdb.h
17
src/txdb.h
|
@ -117,13 +117,14 @@ private:
|
|||
CBlockTreeDB(const CBlockTreeDB&);
|
||||
void operator=(const CBlockTreeDB&);
|
||||
public:
|
||||
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo);
|
||||
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<CBlockIndex*>& blockinfo);
|
||||
bool EraseBatchSync(const std::vector<const CBlockIndex*>& blockinfo);
|
||||
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info);
|
||||
bool ReadLastBlockFile(int &nFile);
|
||||
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info) const;
|
||||
bool ReadLastBlockFile(int &nFile) const;
|
||||
bool WriteReindexing(bool fReindexing);
|
||||
bool ReadReindexing(bool &fReindexing);
|
||||
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
|
||||
bool ReadReindexing(bool &fReindexing) const;
|
||||
bool ReadDiskBlockIndex(const uint256 &blockhash, CDiskBlockIndex &dbindex) const;
|
||||
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) const;
|
||||
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &vect);
|
||||
|
||||
// START insightexplorer
|
||||
|
@ -132,18 +133,18 @@ public:
|
|||
bool WriteAddressIndex(const std::vector<CAddressIndexDbEntry> &vect);
|
||||
bool EraseAddressIndex(const std::vector<CAddressIndexDbEntry> &vect);
|
||||
bool ReadAddressIndex(uint160 addressHash, int type, std::vector<CAddressIndexDbEntry> &addressIndex, int start = 0, int end = 0);
|
||||
bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value);
|
||||
bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) const;
|
||||
bool UpdateSpentIndex(const std::vector<CSpentIndexDbEntry> &vect);
|
||||
bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex);
|
||||
bool ReadTimestampIndex(unsigned int high, unsigned int low,
|
||||
const bool fActiveOnly, std::vector<std::pair<uint256, unsigned int> > &vect);
|
||||
bool WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex,
|
||||
const CTimestampBlockIndexValue &logicalts);
|
||||
bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS);
|
||||
bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS) const;
|
||||
// END insightexplorer
|
||||
|
||||
bool WriteFlag(const std::string &name, bool fValue);
|
||||
bool ReadFlag(const std::string &name, bool &fValue);
|
||||
bool ReadFlag(const std::string &name, bool &fValue) const;
|
||||
bool LoadBlockIndexGuts(
|
||||
std::function<CBlockIndex*(const uint256&)> insertBlockIndex,
|
||||
const CChainParams& chainParams);
|
||||
|
|
|
@ -639,7 +639,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
|
|||
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString()));
|
||||
}
|
||||
wtxHeight = mapBlockIndex[wtx.hashBlock]->nHeight;
|
||||
wtxDepth = wtx.GetDepthInMainChain();
|
||||
wtxDepth = wtx.GetDepthInMainChain(std::nullopt);
|
||||
}
|
||||
LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vJoinSplit=%d, jsoutindex=%d, amount=%s, height=%d, confirmations=%d)\n",
|
||||
getId(),
|
||||
|
|
|
@ -86,7 +86,7 @@ bool AsyncRPCOperation_saplingmigration::main_impl() {
|
|||
// We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying
|
||||
// an anchor at height N-10 for each Sprout JoinSplit description
|
||||
// Consider, should notes be sorted?
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 11);
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 11);
|
||||
}
|
||||
CAmount availableFunds = 0;
|
||||
for (const SproutNoteEntry& sproutEntry : sproutEntries) {
|
||||
|
|
|
@ -192,7 +192,7 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
|||
SpendableInputs spendable;
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
spendable = pwalletMain->FindSpendableInputs(ztxoSelector_, allowTransparentCoinbase, mindepth_);
|
||||
spendable = pwalletMain->FindSpendableInputs(ztxoSelector_, allowTransparentCoinbase, mindepth_, std::nullopt);
|
||||
}
|
||||
if (!spendable.LimitToAmount(targetAmount, dustThreshold, recipientPools_)) {
|
||||
CAmount changeAmount{spendable.Total() - targetAmount};
|
||||
|
|
|
@ -214,18 +214,18 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
|
||||
wtx.SetSproutNoteData(noteData);
|
||||
wallet.LoadWalletTx(wtx);
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
// We currently have an unspent and unconfirmed note in the wallet (depth of -1)
|
||||
std::vector<SproutNoteEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
std::vector<OrchardNoteMetadata> orchardEntries;
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 0);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 0);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
orchardEntries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, -1);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, -1);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
|
@ -244,21 +244,21 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
|
||||
wtx.SetMerkleBranch(block);
|
||||
wallet.LoadWalletTx(wtx);
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
|
||||
// We now have an unspent and confirmed note in the wallet (depth of 1)
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 0);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 0);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
orchardEntries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 1);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 1);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
orchardEntries.clear();
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 2);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 2);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
|
@ -268,7 +268,7 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
// Let's spend the note.
|
||||
auto wtx2 = GetValidSproutSpend(sk, note, 5);
|
||||
wallet.LoadWalletTx(wtx2);
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
// Fake-mine a spend transaction
|
||||
EXPECT_EQ(0, chainActive.Height());
|
||||
|
@ -286,22 +286,22 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
|
||||
wtx2.SetMerkleBranch(block2);
|
||||
wallet.LoadWalletTx(wtx2);
|
||||
EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_TRUE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
// The note has been spent. By default, GetFilteredNotes() ignores spent notes.
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 0);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 0);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
orchardEntries.clear();
|
||||
// Let's include spent notes to retrieve it.
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 0, INT_MAX, false);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 0, INT_MAX, false);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
orchardEntries.clear();
|
||||
// The spent note has two confirmations.
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 2, INT_MAX, false);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 2, INT_MAX, false);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
|
@ -328,7 +328,7 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
|
||||
wtx.SetSproutNoteData(noteData);
|
||||
wallet.LoadWalletTx(wtx);
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
wtx3 = wtx;
|
||||
}
|
||||
|
@ -351,25 +351,25 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
wallet.LoadWalletTx(wtx3);
|
||||
|
||||
// We now have an unspent note which has one confirmation, in addition to our spent note.
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 1);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 1);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
orchardEntries.clear();
|
||||
// Let's return the spent note too.
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 1, INT_MAX, false);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 1, INT_MAX, false);
|
||||
EXPECT_EQ(2, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
orchardEntries.clear();
|
||||
// Increasing number of confirmations will exclude our new unspent note.
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 2, INT_MAX, false);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 2, INT_MAX, false);
|
||||
EXPECT_EQ(1, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
orchardEntries.clear();
|
||||
// If we also ignore spent notes at this depth, we won't find any notes.
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 2, INT_MAX, true);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, 2, INT_MAX, true);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
sproutEntries.clear();
|
||||
saplingEntries.clear();
|
||||
|
@ -877,7 +877,7 @@ TEST(WalletTests, GetConflictedOrchardNotes) {
|
|||
std::vector<SproutNoteEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
std::vector<OrchardNoteMetadata> orchardEntries;
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, -1);
|
||||
wallet.GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, std::nullopt, -1);
|
||||
EXPECT_EQ(0, sproutEntries.size());
|
||||
EXPECT_EQ(0, saplingEntries.size());
|
||||
EXPECT_EQ(1, orchardEntries.size());
|
||||
|
@ -957,14 +957,14 @@ TEST(WalletTests, SproutNullifierIsSpent) {
|
|||
auto note = GetSproutNote(sk, wtx, 0, 1);
|
||||
auto nullifier = note.nullifier(sk);
|
||||
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
wallet.LoadWalletTx(wtx);
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
auto wtx2 = GetValidSproutSpend(sk, note, 5);
|
||||
wallet.LoadWalletTx(wtx2);
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
// Fake-mine the transaction
|
||||
EXPECT_EQ(-1, chainActive.Height());
|
||||
|
@ -980,7 +980,7 @@ TEST(WalletTests, SproutNullifierIsSpent) {
|
|||
|
||||
wtx2.SetMerkleBranch(block);
|
||||
wallet.LoadWalletTx(wtx2);
|
||||
EXPECT_TRUE(wallet.IsSproutSpent(nullifier));
|
||||
EXPECT_TRUE(wallet.IsSproutSpent(nullifier, std::nullopt));
|
||||
|
||||
// Tear down
|
||||
chainActive.SetTip(NULL);
|
||||
|
@ -1018,7 +1018,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) {
|
|||
uint256 nullifier = nf.value();
|
||||
|
||||
// Verify note has not been spent
|
||||
EXPECT_FALSE(wallet.IsSaplingSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSaplingSpent(nullifier, std::nullopt));
|
||||
|
||||
// Fake-mine the transaction
|
||||
EXPECT_EQ(-1, chainActive.Height());
|
||||
|
@ -1036,7 +1036,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) {
|
|||
wallet.LoadWalletTx(wtx);
|
||||
|
||||
// Verify note has been spent
|
||||
EXPECT_TRUE(wallet.IsSaplingSpent(nullifier));
|
||||
EXPECT_TRUE(wallet.IsSaplingSpent(nullifier, std::nullopt));
|
||||
|
||||
// Tear down
|
||||
chainActive.SetTip(NULL);
|
||||
|
@ -1107,7 +1107,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
|
|||
MerkleFrontiers frontiers = { .sapling = testNote.tree };
|
||||
|
||||
// Verify dummy note is unspent
|
||||
EXPECT_FALSE(wallet.IsSaplingSpent(nullifier));
|
||||
EXPECT_FALSE(wallet.IsSaplingSpent(nullifier, std::nullopt));
|
||||
|
||||
// Fake-mine the transaction
|
||||
EXPECT_EQ(-1, chainActive.Height());
|
||||
|
@ -1129,7 +1129,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
|
|||
wallet.LoadWalletTx(wtx);
|
||||
|
||||
// Verify dummy note is now spent, as AddToWallet invokes AddToSpends()
|
||||
EXPECT_TRUE(wallet.IsSaplingSpent(nullifier));
|
||||
EXPECT_TRUE(wallet.IsSaplingSpent(nullifier, std::nullopt));
|
||||
|
||||
// Test invariant: no witnesses means no nullifier.
|
||||
EXPECT_EQ(0, wallet.mapSaplingNullifiersToNotes.size());
|
||||
|
@ -1334,7 +1334,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
|
|||
wallet.LoadWalletTx(wtx2);
|
||||
|
||||
// Verify note B is spent. LoadWalletTx invokes AddToSpends which updates mapTxSaplingNullifiers
|
||||
EXPECT_TRUE(wallet.IsSaplingSpent(nullifier2));
|
||||
EXPECT_TRUE(wallet.IsSaplingSpent(nullifier2, std::nullopt));
|
||||
|
||||
// Verify note B belongs to wallet.
|
||||
EXPECT_TRUE(wallet.IsFromMe(wtx2));
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
#include <boost/assign/list_of.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <utf8.h>
|
||||
#include <utf8cpp/utf8.h>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
|
@ -115,9 +115,9 @@ void ThrowIfInitialBlockDownload()
|
|||
}
|
||||
}
|
||||
|
||||
void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
|
||||
void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry, const std::optional<int>& asOfHeight)
|
||||
{
|
||||
int confirms = wtx.GetDepthInMainChain();
|
||||
int confirms = wtx.GetDepthInMainChain(asOfHeight);
|
||||
std::string status = "waiting";
|
||||
|
||||
entry.pushKV("confirmations", confirms);
|
||||
|
@ -264,7 +264,7 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp)
|
|||
|
||||
static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew)
|
||||
{
|
||||
CAmount curBalance = pwalletMain->GetBalance();
|
||||
CAmount curBalance = pwalletMain->GetBalance(std::nullopt);
|
||||
|
||||
// Check amount
|
||||
if (nValue <= 0)
|
||||
|
@ -285,7 +285,7 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr
|
|||
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
|
||||
vecSend.push_back(recipient);
|
||||
if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) {
|
||||
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance())
|
||||
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance(std::nullopt))
|
||||
strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||
}
|
||||
|
@ -484,7 +484,7 @@ UniValue listaddresses(const UniValue& params, bool fHelp)
|
|||
// with respect to the entries in the address book for addresses generated by this wallet,
|
||||
// there is not a guarantee that an externally generated address (such as one associated with
|
||||
// a future unified incoming viewing key) will have been added to the address book.
|
||||
for (const std::pair<CTxDestination, CAmount>& item : pwalletMain->GetAddressBalances()) {
|
||||
for (const std::pair<CTxDestination, CAmount>& item : pwalletMain->GetAddressBalances(std::nullopt)) {
|
||||
if (t_generated_dests.count(item.first) == 0 &&
|
||||
t_mnemonic_dests.count(item.first) == 0 &&
|
||||
t_imported_dests.count(item.first) == 0 &&
|
||||
|
@ -841,11 +841,13 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp)
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"listaddressgroupings\n"
|
||||
"listaddressgroupings ( asOfHeight )\n"
|
||||
"\nLists groups of transparent addresses which have had their common ownership\n"
|
||||
"made public by common use as inputs or as the resulting change in past transactions.\n"
|
||||
"\nArguments:\n"
|
||||
"1. " + asOfHeightMessage(false) +
|
||||
"\nResult:\n"
|
||||
"[\n"
|
||||
" [\n"
|
||||
|
@ -857,6 +859,8 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp)
|
|||
" ]\n"
|
||||
" ,...\n"
|
||||
"]\n"
|
||||
"\nBitcoin compatibility:\n"
|
||||
"The zero-argument form is compatible."
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("listaddressgroupings", "")
|
||||
+ HelpExampleRpc("listaddressgroupings", "")
|
||||
|
@ -864,9 +868,11 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp)
|
|||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
auto asOfHeight = parseAsOfHeight(params, 0);
|
||||
|
||||
KeyIO keyIO(Params());
|
||||
UniValue jsonGroupings(UniValue::VARR);
|
||||
std::map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances();
|
||||
std::map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances(asOfHeight);
|
||||
for (const std::set<CTxDestination>& grouping : pwalletMain->GetAddressGroupings()) {
|
||||
UniValue jsonGrouping(UniValue::VARR);
|
||||
for (const CTxDestination& address : grouping)
|
||||
|
@ -952,16 +958,19 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() < 1 || params.size() > 3)
|
||||
if (fHelp || params.size() < 1 || params.size() > 4)
|
||||
throw runtime_error(
|
||||
"getreceivedbyaddress \"zcashaddress\" ( minconf ) ( inZat )\n"
|
||||
"getreceivedbyaddress \"zcashaddress\" ( minconf inZat asOfHeight )\n"
|
||||
"\nReturns the total amount received by the given transparent Zcash address in transactions with at least minconf confirmations.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"zcashaddress\" (string, required) The Zcash address for transactions.\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
"3. inZat (bool, optional, default=false) Get the result amount in " + MINOR_CURRENCY_UNIT + " (as an integer).\n"
|
||||
"4. " + asOfHeightMessage(true) +
|
||||
"\nResult:\n"
|
||||
"amount (numeric) The total amount in " + CURRENCY_UNIT + "(or " + MINOR_CURRENCY_UNIT + " if inZat is true) received at this address.\n"
|
||||
"\nBitcoin compatibility:\n"
|
||||
"Compatible with up to two arguments."
|
||||
"\nExamples:\n"
|
||||
"\nThe amount from transactions with at least 1 confirmation\n"
|
||||
+ HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") +
|
||||
|
@ -987,10 +996,10 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
return ValueFromAmount(0);
|
||||
}
|
||||
|
||||
auto asOfHeight = parseAsOfHeight(params, 3);
|
||||
|
||||
// Minimum confirmations
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 1)
|
||||
nMinDepth = params[1].get_int();
|
||||
int nMinDepth = parseMinconf(1, params, 1, asOfHeight);
|
||||
|
||||
// Tally
|
||||
CAmount nAmount = 0;
|
||||
|
@ -1002,7 +1011,7 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
|
||||
for (const CTxOut& txout : wtx.vout)
|
||||
if (txout.scriptPubKey == scriptPubKey)
|
||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
||||
if (wtx.GetDepthInMainChain(asOfHeight) >= nMinDepth)
|
||||
nAmount += txout.nValue;
|
||||
}
|
||||
|
||||
|
@ -1019,19 +1028,22 @@ UniValue getbalance(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 4)
|
||||
if (fHelp || params.size() > 5)
|
||||
throw runtime_error(
|
||||
"getbalance ( \"(dummy)\" minconf includeWatchonly inZat )\n"
|
||||
"getbalance ( \"(dummy)\" minconf includeWatchonly inZat asOfHeight )\n"
|
||||
"\nReturns the wallet's available transparent balance. This total\n"
|
||||
"currently includes transparent balances associated with unified\n"
|
||||
"accounts. Prefer to use `z_getbalanceforaccount` instead.\n"
|
||||
"\nArguments:\n"
|
||||
"1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\" or \"\".\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
"2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n"
|
||||
"3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n"
|
||||
"4. inZat (bool, optional, default=false) Get the result amount in " + MINOR_CURRENCY_UNIT + " (as an integer).\n"
|
||||
"5. " + asOfHeightMessage(true) +
|
||||
"\nResult:\n"
|
||||
"amount (numeric) The total amount in " + CURRENCY_UNIT + "(or " + MINOR_CURRENCY_UNIT + " if inZat is true) received.\n"
|
||||
"\nBitcoin compatibility:\n"
|
||||
"Compatible with up to three arguments."
|
||||
"\nExamples:\n"
|
||||
"\nThe total amount in the wallet\n"
|
||||
+ HelpExampleCli("getbalance", "*") +
|
||||
|
@ -1048,17 +1060,16 @@ UniValue getbalance(const UniValue& params, bool fHelp)
|
|||
throw JSONRPCError(RPC_INVALID_PARAMETER, "dummy first argument must be excluded or set to \"*\" or \"\".");
|
||||
}
|
||||
|
||||
int min_depth = 0;
|
||||
if (!params[1].isNull()) {
|
||||
min_depth = params[1].get_int();
|
||||
}
|
||||
auto asOfHeight = parseAsOfHeight(params, 4);
|
||||
|
||||
int min_depth = parseMinconf(0, params, 1, asOfHeight);
|
||||
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
if (!params[2].isNull() && params[2].get_bool()) {
|
||||
filter = filter | ISMINE_WATCH_ONLY;
|
||||
}
|
||||
|
||||
CAmount nBalance = pwalletMain->GetBalance(filter, min_depth);
|
||||
CAmount nBalance = pwalletMain->GetBalance(asOfHeight, filter, min_depth);
|
||||
if (!params[3].isNull() && params[3].get_bool()) {
|
||||
return nBalance;
|
||||
} else {
|
||||
|
@ -1073,12 +1084,12 @@ UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp)
|
|||
|
||||
if (fHelp || params.size() > 0)
|
||||
throw runtime_error(
|
||||
"getunconfirmedbalance\n"
|
||||
"Returns the server's total unconfirmed transparent balance\n");
|
||||
"getunconfirmedbalance\n"
|
||||
"Returns the server's total unconfirmed transparent balance\n");
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
return ValueFromAmount(pwalletMain->GetUnconfirmedBalance());
|
||||
return ValueFromAmount(pwalletMain->GetUnconfirmedTransparentBalance());
|
||||
}
|
||||
|
||||
|
||||
|
@ -1134,9 +1145,7 @@ UniValue sendmany(const UniValue& params, bool fHelp)
|
|||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
|
||||
}
|
||||
UniValue sendTo = params[1].get_obj();
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 2)
|
||||
nMinDepth = params[2].get_int();
|
||||
int nMinDepth = parseMinconf(1, params, 2, std::nullopt);
|
||||
|
||||
CWalletTx wtx;
|
||||
if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty())
|
||||
|
@ -1274,10 +1283,18 @@ struct tallyitem
|
|||
|
||||
UniValue ListReceived(const UniValue& params)
|
||||
{
|
||||
if (params.size() > 3 && params[3].get_str() != "") {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "addressFilter must be set to \"\"");
|
||||
}
|
||||
|
||||
if (params.size() > 4 && params[4].get_bool() != false) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "includeImmatureCoinbase must be set to false");
|
||||
}
|
||||
|
||||
auto asOfHeight = parseAsOfHeight(params, 5);
|
||||
|
||||
// Minimum confirmations
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 0)
|
||||
nMinDepth = params[0].get_int();
|
||||
int nMinDepth = parseMinconf(1, params, 0, asOfHeight);
|
||||
|
||||
// Whether to include empty accounts
|
||||
bool fIncludeEmpty = false;
|
||||
|
@ -1297,7 +1314,7 @@ UniValue ListReceived(const UniValue& params)
|
|||
if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
|
||||
continue;
|
||||
|
||||
int nDepth = wtx.GetDepthInMainChain();
|
||||
int nDepth = wtx.GetDepthInMainChain(asOfHeight);
|
||||
if (nDepth < nMinDepth)
|
||||
continue;
|
||||
|
||||
|
@ -1367,9 +1384,9 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 3)
|
||||
if (fHelp || params.size() > 6)
|
||||
throw runtime_error(
|
||||
"listreceivedbyaddress ( minconf includeempty includeWatchonly)\n"
|
||||
"listreceivedbyaddress ( minconf includeempty includeWatchonly addressFilter includeImmatureCoinbase asOfHeight )\n"
|
||||
"\nList balances by transparent receiving address. This API does not provide\n"
|
||||
"any information for associated with shielded addresses and should only be used\n"
|
||||
"in circumstances where it is necessary to interoperate with legacy Bitcoin\n"
|
||||
|
@ -1378,7 +1395,9 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
|
||||
"2. includeempty (numeric, optional, default=false) Whether to include addresses that haven't received any payments.\n"
|
||||
"3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
|
||||
|
||||
"4. addressFilter (string, optional, default=\"\") If present and non-empty, only return information on this address. Currently, only the default value is supported.\n"
|
||||
"5. includeImmatureCoinbase (bool, optional, default=false) Include immature coinbase transactions. Currently, only the default value is supported.\n"
|
||||
"6. " + asOfHeightMessage(true) +
|
||||
"\nResult:\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
|
@ -1390,7 +1409,8 @@ UniValue listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
" }\n"
|
||||
" ,...\n"
|
||||
"]\n"
|
||||
|
||||
"\nBitcoin compatibility:\n"
|
||||
"Compatible up to five arguments, but can only use the default value for `addressFilter` and `includeImmatureCoinbase`."
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("listreceivedbyaddress", "")
|
||||
+ HelpExampleCli("listreceivedbyaddress", "6 true")
|
||||
|
@ -1418,8 +1438,9 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
|
|||
* @param fLong Whether to include the JSON version of the transaction.
|
||||
* @param ret The UniValue into which the result is stored.
|
||||
* @param filter The "is mine" filter flags.
|
||||
* @param asOfHeight The last block to look at.
|
||||
*/
|
||||
void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
|
||||
void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter, const std::optional<int>& asOfHeight)
|
||||
{
|
||||
CAmount nFee;
|
||||
std::list<COutputEntry> listReceived;
|
||||
|
@ -1445,14 +1466,14 @@ void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue&
|
|||
entry.pushKV("vout", s.vout);
|
||||
entry.pushKV("fee", ValueFromAmount(-nFee));
|
||||
if (fLong)
|
||||
WalletTxToJSON(wtx, entry);
|
||||
WalletTxToJSON(wtx, entry, asOfHeight);
|
||||
entry.pushKV("size", static_cast<uint64_t>(GetSerializeSize(static_cast<CTransaction>(wtx), SER_NETWORK, PROTOCOL_VERSION)));
|
||||
ret.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Received
|
||||
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
|
||||
if (listReceived.size() > 0 && wtx.GetDepthInMainChain(asOfHeight) >= nMinDepth)
|
||||
{
|
||||
for (const COutputEntry& r : listReceived)
|
||||
{
|
||||
|
@ -1467,9 +1488,9 @@ void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue&
|
|||
MaybePushAddress(entry, r.destination);
|
||||
if (wtx.IsCoinBase())
|
||||
{
|
||||
if (wtx.GetDepthInMainChain() < 1)
|
||||
if (wtx.GetDepthInMainChain(asOfHeight) < 1)
|
||||
entry.pushKV("category", "orphan");
|
||||
else if (wtx.GetBlocksToMaturity() > 0)
|
||||
else if (wtx.GetBlocksToMaturity(asOfHeight) > 0)
|
||||
entry.pushKV("category", "immature");
|
||||
else
|
||||
entry.pushKV("category", "generate");
|
||||
|
@ -1482,7 +1503,7 @@ void ListTransactions(const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue&
|
|||
entry.pushKV("amountZat", r.amount);
|
||||
entry.pushKV("vout", r.vout);
|
||||
if (fLong)
|
||||
WalletTxToJSON(wtx, entry);
|
||||
WalletTxToJSON(wtx, entry, asOfHeight);
|
||||
entry.pushKV("size", static_cast<uint64_t>(GetSerializeSize(static_cast<CTransaction>(wtx), SER_NETWORK, PROTOCOL_VERSION)));
|
||||
ret.push_back(entry);
|
||||
}
|
||||
|
@ -1494,9 +1515,9 @@ UniValue listtransactions(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 4)
|
||||
if (fHelp || params.size() > 5)
|
||||
throw runtime_error(
|
||||
"listtransactions ( \"dummy\" count from includeWatchonly)\n"
|
||||
"listtransactions ( \"dummy\" count from includeWatchonly asOfHeight)\n"
|
||||
"\nReturns up to 'count' of the most recent transactions associated with legacy transparent\n"
|
||||
"addresses of this wallet, skipping the first 'from' transactions.\n"
|
||||
"\nThis API does not provide any information about transactions containing shielded inputs\n"
|
||||
|
@ -1508,6 +1529,7 @@ UniValue listtransactions(const UniValue& params, bool fHelp)
|
|||
"2. count (numeric, optional, default=10) The number of transactions to return\n"
|
||||
"3. from (numeric, optional, default=0) The number of transactions to skip\n"
|
||||
"4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n"
|
||||
"5. " + asOfHeightMessage(false) +
|
||||
"\nResult:\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
|
@ -1562,6 +1584,8 @@ UniValue listtransactions(const UniValue& params, bool fHelp)
|
|||
if(params[3].get_bool())
|
||||
filter = filter | ISMINE_WATCH_ONLY;
|
||||
|
||||
auto asOfHeight = parseAsOfHeight(params, 4);
|
||||
|
||||
if (nCount < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
|
||||
if (nFrom < 0)
|
||||
|
@ -1577,7 +1601,7 @@ UniValue listtransactions(const UniValue& params, bool fHelp)
|
|||
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
||||
{
|
||||
CWalletTx *const pwtx = (*it).second;
|
||||
ListTransactions(*pwtx, 0, true, ret, filter);
|
||||
ListTransactions(*pwtx, 0, true, ret, filter, asOfHeight);
|
||||
if ((int)ret.size() >= (nCount+nFrom)) break;
|
||||
}
|
||||
}
|
||||
|
@ -1614,14 +1638,17 @@ UniValue listsinceblock(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp)
|
||||
if (fHelp || params.size() > 6)
|
||||
throw runtime_error(
|
||||
"listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n"
|
||||
"listsinceblock ( \"blockhash\" target-confirmations includeWatchonly includeRemoved includeChange asOfHeight )\n"
|
||||
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
|
||||
"2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
|
||||
"3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')"
|
||||
"4. includeRemoved (bool, optional, default=true) Show transactions that were removed due to a reorg in the \"removed\" array (not guaranteed to work on pruned nodes)\n"
|
||||
"5. includeChange (bool, optional, default=false) Also add entries for change outputs. Currently, only the default value is supported.\n"
|
||||
"6. " + asOfHeightMessage(false) +
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"transactions\": [\n"
|
||||
|
@ -1643,9 +1670,13 @@ UniValue listsinceblock(const UniValue& params, bool fHelp)
|
|||
" \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n"
|
||||
" \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
|
||||
" \"to\": \"...\", (string) If a comment to is associated with the transaction.\n"
|
||||
" ],\n"
|
||||
" ],\n"
|
||||
" \"removed\": [...] (array of objects, optional) structure is the same as \"transactions\" above, only present if includeRemoved=true\n"
|
||||
" Note: currently this only returns an empty array.\n"
|
||||
" \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n"
|
||||
"}\n"
|
||||
"\nBitcoin compatibility:\n"
|
||||
"Compatible up to five arguments, but can only use the default value for `includeChange`, and only returns an empty array for \"removed\"."
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("listsinceblock", "")
|
||||
+ HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6")
|
||||
|
@ -1680,6 +1711,17 @@ UniValue listsinceblock(const UniValue& params, bool fHelp)
|
|||
if(params[2].get_bool())
|
||||
filter = filter | ISMINE_WATCH_ONLY;
|
||||
|
||||
bool includeRemoved = true;
|
||||
if (params.size() > 3) {
|
||||
includeRemoved = params[3].get_bool();
|
||||
}
|
||||
|
||||
if (params.size() > 4 && params[4].get_bool() != false) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "includeChange must be set to false");
|
||||
}
|
||||
|
||||
auto asOfHeight = parseAsOfHeight(params, 5);
|
||||
|
||||
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
|
||||
|
||||
UniValue transactions(UniValue::VARR);
|
||||
|
@ -1687,8 +1729,8 @@ UniValue listsinceblock(const UniValue& params, bool fHelp)
|
|||
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwalletMain->mapWallet) {
|
||||
CWalletTx tx = pairWtx.second;
|
||||
|
||||
if (depth == -1 || tx.GetDepthInMainChain() < depth) {
|
||||
ListTransactions(tx, 0, true, transactions, filter);
|
||||
if (depth == -1 || tx.GetDepthInMainChain(std::nullopt) < depth) {
|
||||
ListTransactions(tx, 0, true, transactions, filter, asOfHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1697,6 +1739,9 @@ UniValue listsinceblock(const UniValue& params, bool fHelp)
|
|||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("transactions", transactions);
|
||||
if (includeRemoved) {
|
||||
ret.pushKV("removed", UniValue(UniValue::VARR));
|
||||
}
|
||||
ret.pushKV("lastblock", lastblock.GetHex());
|
||||
|
||||
return ret;
|
||||
|
@ -1707,15 +1752,17 @@ UniValue gettransaction(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
if (fHelp || params.size() < 1 || params.size() > 4)
|
||||
throw runtime_error(
|
||||
"gettransaction \"txid\" ( includeWatchonly )\n"
|
||||
"gettransaction \"txid\" ( includeWatchonly verbose asOfHeight )\n"
|
||||
"\nReturns detailed information about in-wallet transaction <txid>. This does not\n"
|
||||
"include complete information about shielded components of the transaction; to obtain\n"
|
||||
"details about shielded components of the transaction use `z_viewtransaction`.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"txid\" (string, required) The transaction id\n"
|
||||
"2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n"
|
||||
"2. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n"
|
||||
"3. verbose (bool, optional, default=false) Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction). Currently, only the default value is supported.\n"
|
||||
"4. " + asOfHeightMessage(false) +
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"status\" : \"mined|waiting|expiringsoon|expired\", (string) The transaction status, can be 'mined', 'waiting', 'expiringsoon' or 'expired'\n"
|
||||
|
@ -1752,7 +1799,8 @@ UniValue gettransaction(const UniValue& params, bool fHelp)
|
|||
" ],\n"
|
||||
" \"hex\" : \"data\" (string) Raw data for transaction\n"
|
||||
"}\n"
|
||||
|
||||
"\nBitcoin compatibility:\n"
|
||||
"Compatible up to three arguments, but can only use the default value for `verbose`."
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
|
||||
+ HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true")
|
||||
|
@ -1769,12 +1817,18 @@ UniValue gettransaction(const UniValue& params, bool fHelp)
|
|||
if(params[1].get_bool())
|
||||
filter = filter | ISMINE_WATCH_ONLY;
|
||||
|
||||
if (params.size() > 2 && params[2].get_bool() != false) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "verbose must be set to false");
|
||||
}
|
||||
|
||||
auto asOfHeight = parseAsOfHeight(params, 3);
|
||||
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
if (!pwalletMain->mapWallet.count(hash))
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
|
||||
const CWalletTx& wtx = pwalletMain->mapWallet[hash];
|
||||
|
||||
CAmount nCredit = wtx.GetCredit(filter);
|
||||
CAmount nCredit = wtx.GetCredit(asOfHeight, filter);
|
||||
CAmount nDebit = wtx.GetDebit(filter);
|
||||
CAmount nNet = nCredit - nDebit;
|
||||
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
|
||||
|
@ -1785,10 +1839,10 @@ UniValue gettransaction(const UniValue& params, bool fHelp)
|
|||
if (wtx.IsFromMe(filter))
|
||||
entry.pushKV("fee", ValueFromAmount(nFee));
|
||||
|
||||
WalletTxToJSON(wtx, entry);
|
||||
WalletTxToJSON(wtx, entry, asOfHeight);
|
||||
|
||||
UniValue details(UniValue::VARR);
|
||||
ListTransactions(wtx, 0, false, details, filter);
|
||||
ListTransactions(wtx, 0, false, details, filter, asOfHeight);
|
||||
entry.pushKV("details", details);
|
||||
|
||||
string strHex = EncodeHexTx(static_cast<CTransaction>(wtx));
|
||||
|
@ -2303,25 +2357,29 @@ UniValue settxfee(const UniValue& params, bool fHelp)
|
|||
return true;
|
||||
}
|
||||
|
||||
CAmount getBalanceZaddr(std::optional<libzcash::PaymentAddress> address, int minDepth = 1, int maxDepth = INT_MAX, bool ignoreUnspendable=true);
|
||||
CAmount getBalanceZaddr(std::optional<libzcash::PaymentAddress> address, const std::optional<int>& asOfHeight, int minDepth = 1, int maxDepth = INT_MAX, bool ignoreUnspendable=true);
|
||||
|
||||
UniValue getwalletinfo(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() != 0)
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"getwalletinfo\n"
|
||||
"getwalletinfo ( asOfHeight )\n"
|
||||
"Returns wallet state information.\n"
|
||||
"\nArguments:\n"
|
||||
"1. " + asOfHeightMessage(false) +
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
|
||||
" \"balance\": xxxxxxx, (numeric) the total confirmed transparent balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed transparent balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"unconfirmed_balance\": xxx, (numeric, optional) the total unconfirmed transparent balance of the wallet in " + CURRENCY_UNIT + ".\n"
|
||||
" Not included if `asOfHeight` is specified.\n"
|
||||
" \"immature_balance\": xxxxxx, (numeric) the total immature transparent balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"shielded_balance\": xxxxxxx, (numeric) the total confirmed shielded balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"shielded_unconfirmed_balance\": xxx, (numeric) the total unconfirmed shielded balance of the wallet in " + CURRENCY_UNIT + "\n"
|
||||
" \"shielded_unconfirmed_balance\": xxx, (numeric, optional) the total unconfirmed shielded balance of the wallet in " + CURRENCY_UNIT + ".\n"
|
||||
" Not included if `asOfHeight` is specified.\n"
|
||||
" \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
|
||||
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
|
||||
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
|
||||
|
@ -2337,15 +2395,21 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
|
|||
+ HelpExampleRpc("getwalletinfo", "")
|
||||
);
|
||||
|
||||
auto asOfHeight = parseAsOfHeight(params, 0);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("walletversion", pwalletMain->GetVersion());
|
||||
obj.pushKV("balance", ValueFromAmount(pwalletMain->GetBalance()));
|
||||
obj.pushKV("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()));
|
||||
obj.pushKV("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance()));
|
||||
obj.pushKV("shielded_balance", FormatMoney(getBalanceZaddr(std::nullopt, 1, INT_MAX)));
|
||||
obj.pushKV("shielded_unconfirmed_balance", FormatMoney(getBalanceZaddr(std::nullopt, 0, 0)));
|
||||
obj.pushKV("balance", ValueFromAmount(pwalletMain->GetBalance(asOfHeight)));
|
||||
if (!asOfHeight.has_value()) {
|
||||
obj.pushKV("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedTransparentBalance()));
|
||||
}
|
||||
obj.pushKV("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance(asOfHeight)));
|
||||
obj.pushKV("shielded_balance", FormatMoney(getBalanceZaddr(std::nullopt, asOfHeight, 1, INT_MAX)));
|
||||
if (!asOfHeight.has_value()) {
|
||||
obj.pushKV("shielded_unconfirmed_balance", FormatMoney(getBalanceZaddr(std::nullopt, asOfHeight, 0, 0)));
|
||||
}
|
||||
obj.pushKV("txcount", (int)pwalletMain->mapWallet.size());
|
||||
obj.pushKV("keypoololdest", pwalletMain->GetOldestKeyPoolTime());
|
||||
obj.pushKV("keypoolsize", (int)pwalletMain->GetKeyPoolSize());
|
||||
|
@ -2394,9 +2458,9 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 3)
|
||||
if (fHelp || params.size() > 6)
|
||||
throw runtime_error(
|
||||
"listunspent ( minconf maxconf [\"address\",...] )\n"
|
||||
"listunspent ( minconf maxconf [\"address\",...] includeUnsafe queryOptions asOfHeight )\n"
|
||||
"\nReturns array of unspent transparent transaction outputs with between minconf and\n"
|
||||
"maxconf (inclusive) confirmations. Use `z_listunspent` instead to see information\n"
|
||||
"related to unspent shielded notes. Results may be optionally filtered to only include\n"
|
||||
|
@ -2409,6 +2473,9 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
|||
" \"address\" (string) Zcash address\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"4. includeUnsafe (bool, optional, default=true) Include outputs that are not safe to spend. Currently, only the default value is supported.\n"
|
||||
"5. queryOptions (object, optional, default={}) JSON with query options. Currently, only the default value is supported.\n"
|
||||
"6. " + asOfHeightMessage(true) +
|
||||
"\nResult\n"
|
||||
"[ (array of json object)\n"
|
||||
" {\n"
|
||||
|
@ -2425,7 +2492,8 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
|||
" }\n"
|
||||
" ,...\n"
|
||||
"]\n"
|
||||
|
||||
"\nBitcoin compatibility:\n"
|
||||
"Compatible up to five arguments, but can only use the default value for `includeUnsafe` and `queryOptions`."
|
||||
"\nExamples\n"
|
||||
+ HelpExampleCli("listunspent", "")
|
||||
+ HelpExampleCli("listunspent", "6 9999999 \"[\\\"t1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"t1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
|
||||
|
@ -2434,9 +2502,9 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
|||
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR));
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 0)
|
||||
nMinDepth = params[0].get_int();
|
||||
auto asOfHeight = parseAsOfHeight(params, 5);
|
||||
|
||||
int nMinDepth = parseMinconf(1, params, 0, asOfHeight);
|
||||
|
||||
int nMaxDepth = 9999999;
|
||||
if (params.size() > 1)
|
||||
|
@ -2458,10 +2526,28 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
|||
}
|
||||
}
|
||||
|
||||
if (params.size() > 3 && params[3].get_bool() != false) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "includeUnsafe must be set to false");
|
||||
}
|
||||
|
||||
if (params.size() > 4 && !params[4].get_obj().empty()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "queryOptions must be set to {}");
|
||||
}
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
UniValue results(UniValue::VARR);
|
||||
vector<COutput> vecOutputs;
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
pwalletMain->AvailableCoins(vecOutputs, false, NULL, true);
|
||||
pwalletMain->AvailableCoins(
|
||||
vecOutputs,
|
||||
asOfHeight,
|
||||
false, // fOnlyConfirmed
|
||||
nullptr, // coinControl
|
||||
true, // fIncludeZeroValue
|
||||
true, // fIncludeCoinBase
|
||||
false, // fOnlySpendable
|
||||
nMinDepth,
|
||||
destinations);
|
||||
for (const COutput& out : vecOutputs) {
|
||||
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
|
||||
continue;
|
||||
|
@ -2470,9 +2556,6 @@ UniValue listunspent(const UniValue& params, bool fHelp)
|
|||
const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey;
|
||||
bool fValidAddress = ExtractDestination(scriptPubKey, address);
|
||||
|
||||
if (destinations.size() && (!fValidAddress || !destinations.count(address)))
|
||||
continue;
|
||||
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("txid", out.tx->GetHash().GetHex());
|
||||
entry.pushKV("vout", out.i);
|
||||
|
@ -2506,9 +2589,9 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 4)
|
||||
if (fHelp || params.size() > 5)
|
||||
throw runtime_error(
|
||||
"z_listunspent ( minconf maxconf includeWatchonly [\"zaddr\",...] )\n"
|
||||
"z_listunspent ( minconf maxconf includeWatchonly [\"zaddr\",...] asOfHeight )\n"
|
||||
"\nReturns an array of unspent shielded notes with between minconf and maxconf (inclusive)\n"
|
||||
"confirmations. Results may be optionally filtered to only include notes sent to specified\n"
|
||||
"addresses.\n"
|
||||
|
@ -2523,6 +2606,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
|
|||
" \"address\" (string) Sprout, Sapling, or Unified address\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"5. " + asOfHeightMessage(true) +
|
||||
"\nResult (output indices for only one value pool will be present):\n"
|
||||
"[ (array of json object)\n"
|
||||
" {\n"
|
||||
|
@ -2550,13 +2634,9 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
|
|||
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VBOOL)(UniValue::VARR));
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 0) {
|
||||
nMinDepth = params[0].get_int();
|
||||
}
|
||||
if (nMinDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
auto asOfHeight = parseAsOfHeight(params, 4);
|
||||
|
||||
int nMinDepth = parseMinconf(1, params, 0, asOfHeight);
|
||||
|
||||
int nMaxDepth = 9999999;
|
||||
if (params.size() > 1) {
|
||||
|
@ -2626,7 +2706,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
|
|||
std::vector<SproutNoteEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
std::vector<OrchardNoteMetadata> orchardEntries;
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false);
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, asOfHeight, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false);
|
||||
|
||||
for (auto & entry : sproutEntries) {
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
|
@ -3759,13 +3839,13 @@ UniValue z_listunifiedreceivers(const UniValue& params, bool fHelp)
|
|||
return result;
|
||||
}
|
||||
|
||||
CAmount getBalanceTaddr(const std::optional<CTxDestination>& taddr, int minDepth=1, bool ignoreUnspendable=true) {
|
||||
CAmount getBalanceTaddr(const std::optional<CTxDestination>& taddr, const std::optional<int>& asOfHeight, int minDepth=1, bool ignoreUnspendable=true) {
|
||||
vector<COutput> vecOutputs;
|
||||
CAmount balance = 0;
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
pwalletMain->AvailableCoins(vecOutputs, false, NULL, true);
|
||||
pwalletMain->AvailableCoins(vecOutputs, asOfHeight, false, NULL, true);
|
||||
for (const COutput& out : vecOutputs) {
|
||||
if (out.nDepth < minDepth) {
|
||||
continue;
|
||||
|
@ -3792,7 +3872,7 @@ CAmount getBalanceTaddr(const std::optional<CTxDestination>& taddr, int minDepth
|
|||
return balance;
|
||||
}
|
||||
|
||||
CAmount getBalanceZaddr(std::optional<libzcash::PaymentAddress> address, int minDepth, int maxDepth, bool ignoreUnspendable) {
|
||||
CAmount getBalanceZaddr(std::optional<libzcash::PaymentAddress> address, const std::optional<int>& asOfHeight, int minDepth, int maxDepth, bool ignoreUnspendable) {
|
||||
CAmount balance = 0;
|
||||
std::vector<SproutNoteEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
|
@ -3804,7 +3884,7 @@ CAmount getBalanceZaddr(std::optional<libzcash::PaymentAddress> address, int min
|
|||
noteFilter = NoteFilter::ForPaymentAddresses(std::vector({address.value()}));
|
||||
}
|
||||
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, minDepth, maxDepth, true, ignoreUnspendable);
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, asOfHeight, minDepth, maxDepth, true, ignoreUnspendable);
|
||||
for (auto & entry : sproutEntries) {
|
||||
balance += CAmount(entry.note.value());
|
||||
}
|
||||
|
@ -3840,13 +3920,14 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size()==0 || params.size() >2)
|
||||
if (fHelp || params.size() == 0 || params.size() > 3)
|
||||
throw runtime_error(
|
||||
"z_listreceivedbyaddress \"address\" ( minconf )\n"
|
||||
"z_listreceivedbyaddress \"address\" ( minconf asOfHeight )\n"
|
||||
"\nReturn a list of amounts received by a zaddr belonging to the node's wallet.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string) The shielded address.\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
"3. " + asOfHeightMessage(true) +
|
||||
"\nResult (output indices for only one value pool will be present):\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
|
@ -3873,13 +3954,10 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 1) {
|
||||
nMinDepth = params[1].get_int();
|
||||
}
|
||||
if (nMinDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
auto asOfHeight = parseAsOfHeight(params, 2);
|
||||
|
||||
int nMinDepth = parseMinconf(1, params, 1, asOfHeight);
|
||||
|
||||
UniValue result(UniValue::VARR);
|
||||
|
||||
// Check that the from address is valid.
|
||||
|
@ -3925,7 +4003,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
std::vector<OrchardNoteMetadata> orchardEntries;
|
||||
|
||||
auto noteFilter = NoteFilter::ForPaymentAddresses(std::vector({decoded.value()}));
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, nMinDepth, INT_MAX, false, false);
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, asOfHeight, nMinDepth, INT_MAX, false, false);
|
||||
|
||||
auto push_transparent_result = [&](const CTxDestination& dest) -> void {
|
||||
const CScript scriptPubKey{GetScriptForDestination(dest)};
|
||||
|
@ -3933,7 +4011,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
|||
if (!CheckFinalTx(wtx))
|
||||
continue;
|
||||
|
||||
int nDepth = wtx.GetDepthInMainChain();
|
||||
int nDepth = wtx.GetDepthInMainChain(asOfHeight);
|
||||
if (nDepth < nMinDepth) continue;
|
||||
for (size_t i = 0; i < wtx.vout.size(); ++i) {
|
||||
const CTxOut& txout{wtx.vout[i]};
|
||||
|
@ -4115,13 +4193,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
|||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 1) {
|
||||
nMinDepth = params[1].get_int();
|
||||
}
|
||||
if (nMinDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
int nMinDepth = parseMinconf(1, params, 1, std::nullopt);
|
||||
|
||||
KeyIO keyIO(Params());
|
||||
// Check that the from address is valid.
|
||||
|
@ -4138,16 +4210,16 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
|||
CAmount nBalance = 0;
|
||||
std::visit(match {
|
||||
[&](const CKeyID& addr) {
|
||||
nBalance = getBalanceTaddr(addr, nMinDepth, false);
|
||||
nBalance = getBalanceTaddr(addr, std::nullopt, nMinDepth, false);
|
||||
},
|
||||
[&](const CScriptID& addr) {
|
||||
nBalance = getBalanceTaddr(addr, nMinDepth, false);
|
||||
nBalance = getBalanceTaddr(addr, std::nullopt, nMinDepth, false);
|
||||
},
|
||||
[&](const libzcash::SproutPaymentAddress& addr) {
|
||||
nBalance = getBalanceZaddr(addr, nMinDepth, INT_MAX, false);
|
||||
nBalance = getBalanceZaddr(addr, std::nullopt, nMinDepth, INT_MAX, false);
|
||||
},
|
||||
[&](const libzcash::SaplingPaymentAddress& addr) {
|
||||
nBalance = getBalanceZaddr(addr, nMinDepth, INT_MAX, false);
|
||||
nBalance = getBalanceZaddr(addr, std::nullopt, nMinDepth, INT_MAX, false);
|
||||
},
|
||||
[&](const libzcash::UnifiedAddress& addr) {
|
||||
auto selector = pwalletMain->ZTXOSelectorForAddress(addr, true, false);
|
||||
|
@ -4156,7 +4228,7 @@ UniValue z_getbalance(const UniValue& params, bool fHelp)
|
|||
RPC_INVALID_ADDRESS_OR_KEY,
|
||||
"Unified address does not correspond to an account in the wallet");
|
||||
}
|
||||
auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, nMinDepth);
|
||||
auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, nMinDepth, std::nullopt);
|
||||
|
||||
for (const auto& t : spendableInputs.utxos) {
|
||||
nBalance += t.Value();
|
||||
|
@ -4183,15 +4255,16 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
if (fHelp || params.size() < 1 || params.size() > 3)
|
||||
throw runtime_error(
|
||||
"z_getbalanceforviewingkey \"fvk\" ( minconf )\n"
|
||||
"z_getbalanceforviewingkey \"fvk\" ( minconf asOfHeight )\n"
|
||||
"\nReturns the balance viewable by a full viewing key known to the node's wallet"
|
||||
"\nfor each value pool. Sprout viewing keys may be used only if the wallet controls"
|
||||
"\nthe corresponding spending key."
|
||||
"\nArguments:\n"
|
||||
"1. \"fvk\" (string) The selected full viewing key.\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
"3. " + asOfHeightMessage(true) +
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"pools\": {\n"
|
||||
|
@ -4228,13 +4301,9 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp)
|
|||
}
|
||||
auto fvk = decoded.value();
|
||||
|
||||
int minconf = 1;
|
||||
if (params.size() > 1) {
|
||||
minconf = params[1].get_int();
|
||||
if (minconf < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
}
|
||||
auto asOfHeight = parseAsOfHeight(params, 2);
|
||||
|
||||
int minconf = parseMinconf(1, params, 1, asOfHeight);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
|
@ -4251,7 +4320,7 @@ UniValue z_getbalanceforviewingkey(const UniValue& params, bool fHelp)
|
|||
"Error: the wallet does not recognize the specified viewing key.");
|
||||
}
|
||||
|
||||
auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, minconf);
|
||||
auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, minconf, asOfHeight);
|
||||
|
||||
CAmount transparentBalance = 0;
|
||||
CAmount sproutBalance = 0;
|
||||
|
@ -4295,13 +4364,14 @@ UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
if (fHelp || params.size() < 1 || params.size() > 3)
|
||||
throw runtime_error(
|
||||
"z_getbalanceforaccount account ( minconf )\n"
|
||||
"z_getbalanceforaccount account ( minconf asOfHeight )\n"
|
||||
"\nReturns the account's spendable balance for each value pool (\"transparent\", \"sapling\", and \"orchard\")."
|
||||
"\nArguments:\n"
|
||||
"1. account (numeric) The account number.\n"
|
||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||
"3. " + asOfHeightMessage(true) +
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"pools\": {\n"
|
||||
|
@ -4334,13 +4404,9 @@ UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp)
|
|||
}
|
||||
libzcash::AccountId account = accountInt;
|
||||
|
||||
int minconf = 1;
|
||||
if (params.size() > 1) {
|
||||
minconf = params[1].get_int();
|
||||
if (minconf < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
}
|
||||
auto asOfHeight = parseAsOfHeight(params, 2);
|
||||
|
||||
int minconf = parseMinconf(1, params, 1, asOfHeight);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
|
@ -4352,7 +4418,7 @@ UniValue z_getbalanceforaccount(const UniValue& params, bool fHelp)
|
|||
tfm::format("Error: account %d has not been generated by z_getnewaccount.", account));
|
||||
}
|
||||
|
||||
auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, minconf);
|
||||
auto spendableInputs = pwalletMain->FindSpendableInputs(selector.value(), true, minconf, asOfHeight);
|
||||
// Accounts never contain Sprout notes.
|
||||
assert(spendableInputs.sproutNoteEntries.empty());
|
||||
|
||||
|
@ -4428,13 +4494,7 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
|
|||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 0) {
|
||||
nMinDepth = params[0].get_int();
|
||||
}
|
||||
if (nMinDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
int nMinDepth = parseMinconf(1, params, 0, std::nullopt);
|
||||
|
||||
bool fIncludeWatchonly = false;
|
||||
if (params.size() > 1) {
|
||||
|
@ -4445,8 +4505,8 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp)
|
|||
// but they don't because wtx.GetAmounts() does not handle tx where there are no outputs
|
||||
// pwalletMain->GetBalance() does not accept min depth parameter
|
||||
// so we use our own method to get balance of utxos.
|
||||
CAmount nBalance = getBalanceTaddr(std::nullopt, nMinDepth, !fIncludeWatchonly);
|
||||
CAmount nPrivateBalance = getBalanceZaddr(std::nullopt, nMinDepth, INT_MAX, !fIncludeWatchonly);
|
||||
CAmount nBalance = getBalanceTaddr(std::nullopt, std::nullopt, nMinDepth, !fIncludeWatchonly);
|
||||
CAmount nPrivateBalance = getBalanceZaddr(std::nullopt, std::nullopt, nMinDepth, INT_MAX, !fIncludeWatchonly);
|
||||
CAmount nTotalBalance = nBalance + nPrivateBalance;
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("transparent", FormatMoney(nBalance));
|
||||
|
@ -5004,11 +5064,11 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
|||
if (fHelp || params.size() < 2 || params.size() > 5)
|
||||
throw runtime_error(
|
||||
"z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee ) ( privacyPolicy )\n"
|
||||
"\nSend multiple times. Amounts are decimal numbers with at most 8 digits of precision."
|
||||
"\nChange generated from one or more transparent addresses flows to a new transparent"
|
||||
"\naddress, while change generated from a legacy Sapling address returns to itself."
|
||||
"\nWhen sending from a unified address, change is returned to the internal-only address"
|
||||
"\nfor the associated unified account."
|
||||
"\nSend a transaction with multiple recipients. Amounts are decimal numbers with at"
|
||||
"\nmost 8 digits of precision. Change generated from one or more transparent"
|
||||
"\naddresses flows to a new transparent address, while change generated from a"
|
||||
"\nlegacy Sapling address returns to itself. When sending from a unified address,"
|
||||
"\nchange is returned to the internal-only address for the associated unified account."
|
||||
"\nWhen spending coinbase UTXOs, only shielded recipients are permitted and change is not allowed;"
|
||||
"\nthe entire value of the coinbase UTXO(s) must be consumed."
|
||||
+ HelpRequiringPassphrase() + "\n"
|
||||
|
@ -5258,13 +5318,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
|||
}
|
||||
|
||||
// Minimum confirmations
|
||||
int nMinDepth = DEFAULT_NOTE_CONFIRMATIONS;
|
||||
if (params.size() > 2) {
|
||||
nMinDepth = params[2].get_int();
|
||||
}
|
||||
if (nMinDepth < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0");
|
||||
}
|
||||
int nMinDepth = parseMinconf(DEFAULT_NOTE_CONFIRMATIONS, params, 2, std::nullopt);
|
||||
|
||||
// Fee in Zatoshis, not currency format)
|
||||
CAmount nFee = DEFAULT_FEE;
|
||||
|
@ -5354,13 +5408,15 @@ UniValue z_setmigration(const UniValue& params, bool fHelp) {
|
|||
UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) {
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
if (fHelp || params.size() != 0)
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"z_getmigrationstatus\n"
|
||||
"z_getmigrationstatus ( asOfHeight )\n"
|
||||
"Returns information about the status of the Sprout to Sapling migration.\n"
|
||||
"Note: A transaction is defined as finalized if it has at least ten confirmations.\n"
|
||||
"Also, it is possible that manually created transactions involving this wallet\n"
|
||||
"will be included in the result.\n"
|
||||
"\nArguments:\n"
|
||||
"1. " + asOfHeightMessage(false) +
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"enabled\": true|false, (boolean) Whether or not migration is enabled\n"
|
||||
|
@ -5374,6 +5430,9 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) {
|
|||
"}\n"
|
||||
);
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
auto asOfHeight = parseAsOfHeight(params, 0);
|
||||
|
||||
UniValue migrationStatus(UniValue::VOBJ);
|
||||
migrationStatus.pushKV("enabled", pwalletMain->fSaplingMigrationEnabled);
|
||||
// The "destination_address" field MAY be omitted if the "-migrationdestaddress"
|
||||
|
@ -5391,7 +5450,7 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) {
|
|||
std::vector<OrchardNoteMetadata> orchardEntries;
|
||||
// Here we are looking for any and all Sprout notes for which we have the spending key, including those
|
||||
// which are locked and/or only exist in the mempool, as they should be included in the unmigrated amount.
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, 0, INT_MAX, true, true, false);
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, std::nullopt, asOfHeight, 0, INT_MAX, true, true, false);
|
||||
CAmount unmigratedAmount = 0;
|
||||
for (const auto& sproutEntry : sproutEntries) {
|
||||
unmigratedAmount += sproutEntry.note.value();
|
||||
|
@ -5426,7 +5485,7 @@ UniValue z_getmigrationstatus(const UniValue& params, bool fHelp) {
|
|||
migrationTxids.push_back(txPair.first.ToString());
|
||||
// A transaction is "finalized" iff it has at least 10 confirmations.
|
||||
// TODO: subject to change, if the recommended number of confirmations changes.
|
||||
if (tx.GetDepthInMainChain() >= 10) {
|
||||
if (tx.GetDepthInMainChain(asOfHeight) >= 10) {
|
||||
finalizedMigratedAmount -= tx.GetValueBalanceSapling();
|
||||
++numFinalizedMigrationTxs;
|
||||
} else {
|
||||
|
@ -5603,7 +5662,7 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
|
|||
|
||||
// Get available utxos
|
||||
vector<COutput> vecOutputs;
|
||||
pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, true);
|
||||
pwalletMain->AvailableCoins(vecOutputs, std::nullopt, true, NULL, false, true);
|
||||
|
||||
// Find unspent coinbase utxos and update estimated size
|
||||
for (const COutput& out : vecOutputs) {
|
||||
|
@ -5965,7 +6024,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
|||
if (useAnyUTXO || taddrs.size() > 0) {
|
||||
// Get available utxos
|
||||
vector<COutput> vecOutputs;
|
||||
pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false);
|
||||
pwalletMain->AvailableCoins(vecOutputs, std::nullopt, true, NULL, false, false);
|
||||
|
||||
// Find unspent utxos and update estimated size
|
||||
for (const COutput& out : vecOutputs) {
|
||||
|
@ -6016,7 +6075,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
|||
useAnySprout || useAnySapling ?
|
||||
std::nullopt :
|
||||
std::optional(NoteFilter::ForPaymentAddresses(zaddrs));
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, nAnchorConfirmations);
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, orchardEntries, noteFilter, std::nullopt, nAnchorConfirmations);
|
||||
|
||||
// If Sapling is not active, do not allow sending from a sapling addresses.
|
||||
if (!saplingActive && saplingEntries.size() > 0) {
|
||||
|
@ -6241,12 +6300,13 @@ UniValue z_getnotescount(const UniValue& params, bool fHelp)
|
|||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() > 1)
|
||||
if (fHelp || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"z_getnotescount\n"
|
||||
"z_getnotescount ( minconf asOfHeight )\n"
|
||||
"\nReturns the number of notes available in the wallet for each shielded value pool.\n"
|
||||
"\nArguments:\n"
|
||||
"1. minconf (numeric, optional, default=1) Only include notes in transactions confirmed at least this many times.\n"
|
||||
"\nReturns the number of notes available in the wallet for each shielded value pool.\n"
|
||||
"2. " + asOfHeightMessage(true) +
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"sprout\" (numeric) the number of Sprout notes in the wallet\n"
|
||||
|
@ -6260,15 +6320,15 @@ UniValue z_getnotescount(const UniValue& params, bool fHelp)
|
|||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 0)
|
||||
nMinDepth = params[0].get_int();
|
||||
auto asOfHeight = parseAsOfHeight(params, 1);
|
||||
|
||||
int nMinDepth = parseMinconf(1, params, 0, asOfHeight);
|
||||
|
||||
int sprout = 0;
|
||||
int sapling = 0;
|
||||
int orchard = 0;
|
||||
for (auto& wtx : pwalletMain->mapWallet) {
|
||||
if (wtx.second.GetDepthInMainChain() >= nMinDepth) {
|
||||
if (wtx.second.GetDepthInMainChain(asOfHeight) >= nMinDepth) {
|
||||
sprout += wtx.second.mapSproutNoteData.size();
|
||||
sapling += wtx.second.mapSaplingNoteData.size();
|
||||
orchard += wtx.second.orchardTxMeta.GetMyActionIVKs().size();
|
||||
|
|
|
@ -876,7 +876,7 @@ std::pair<PaymentAddress, RecipientType> CWallet::GetPaymentAddressForRecipient(
|
|||
auto wtxPtr = mapWallet.find(txid);
|
||||
if (wtxPtr != mapWallet.end()) {
|
||||
const CBlockIndex* pTxIndex{nullptr};
|
||||
if (wtxPtr->second.GetDepthInMainChain(pTxIndex) > 0) {
|
||||
if (wtxPtr->second.GetDepthInMainChain(pTxIndex, std::nullopt) > 0) {
|
||||
nHeight = pTxIndex->nHeight;
|
||||
}
|
||||
}
|
||||
|
@ -2206,7 +2206,8 @@ std::optional<RecipientAddress> CWallet::GenerateChangeAddressForAccount(
|
|||
SpendableInputs CWallet::FindSpendableInputs(
|
||||
ZTXOSelector selector,
|
||||
bool allowTransparentCoinbase,
|
||||
uint32_t minDepth) const {
|
||||
uint32_t minDepth,
|
||||
const std::optional<int>& asOfHeight) const {
|
||||
AssertLockHeld(cs_main);
|
||||
AssertLockHeld(cs_wallet);
|
||||
|
||||
|
@ -2220,7 +2221,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
SpendableInputs unspent;
|
||||
for (auto const& [wtxid, wtx] : mapWallet) {
|
||||
bool isCoinbase = wtx.IsCoinBase();
|
||||
auto nDepth = wtx.GetDepthInMainChain();
|
||||
auto nDepth = wtx.GetDepthInMainChain(asOfHeight);
|
||||
|
||||
// Filter the transactions before checking for coins
|
||||
if (!CheckFinalTx(wtx)) continue;
|
||||
|
@ -2228,14 +2229,14 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
|
||||
if (selectTransparent &&
|
||||
// skip transparent utxo selection if coinbase spend restrictions are not met
|
||||
(!isCoinbase || (allowTransparentCoinbase && wtx.GetBlocksToMaturity() <= 0))) {
|
||||
(!isCoinbase || (allowTransparentCoinbase && wtx.GetBlocksToMaturity(asOfHeight) <= 0))) {
|
||||
|
||||
for (int i = 0; i < wtx.vout.size(); i++) {
|
||||
const auto& output = wtx.vout[i];
|
||||
isminetype mine = IsMine(output);
|
||||
|
||||
// skip spent utxos
|
||||
if (IsSpent(wtxid, i)) continue;
|
||||
if (IsSpent(wtxid, i, asOfHeight)) continue;
|
||||
// skip utxos that don't belong to the wallet
|
||||
if (mine == ISMINE_NO) continue;
|
||||
// skip utxos that for which we don't have the spending keys, if
|
||||
|
@ -2263,7 +2264,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
SproutPaymentAddress pa = nd.address;
|
||||
|
||||
// skip note which has been spent
|
||||
if (nd.nullifier.has_value() && IsSproutSpent(nd.nullifier.value())) continue;
|
||||
if (nd.nullifier.has_value() && IsSproutSpent(nd.nullifier.value(), asOfHeight)) continue;
|
||||
// skip notes which don't match the source
|
||||
if (!this->SelectorMatchesAddress(selector, pa)) continue;
|
||||
// skip notes for which we don't have the spending key
|
||||
|
@ -2297,7 +2298,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
(unsigned char) j);
|
||||
|
||||
unspent.sproutNoteEntries.push_back(SproutNoteEntry {
|
||||
jsop, pa, plaintext.note(pa), plaintext.memo(), wtx.GetDepthInMainChain() });
|
||||
jsop, pa, plaintext.note(pa), plaintext.memo(), nDepth });
|
||||
|
||||
} catch (const note_decryption_failed &err) {
|
||||
// Couldn't decrypt with this spending key
|
||||
|
@ -2327,7 +2328,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
auto pa = maybe_pa.value();
|
||||
|
||||
// skip notes which have been spent
|
||||
if (nd.nullifier.has_value() && IsSaplingSpent(nd.nullifier.value())) continue;
|
||||
if (nd.nullifier.has_value() && IsSaplingSpent(nd.nullifier.value(), asOfHeight)) continue;
|
||||
// skip notes which do not match the source
|
||||
if (!this->SelectorMatchesAddress(selector, pa)) continue;
|
||||
// skip notes if we don't have the spending key
|
||||
|
@ -2337,7 +2338,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
|
||||
auto note = notePt.note(nd.ivk).value();
|
||||
unspent.saplingNoteEntries.push_back(SaplingNoteEntry {
|
||||
op, pa, note, notePt.memo(), wtx.GetDepthInMainChain() });
|
||||
op, pa, note, notePt.memo(), nDepth });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2386,7 +2387,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
orchardWallet.GetFilteredNotes(incomingNotes, ivk, true, true);
|
||||
|
||||
for (auto& noteMeta : incomingNotes) {
|
||||
if (IsOrchardSpent(noteMeta.GetOutPoint())) {
|
||||
if (IsOrchardSpent(noteMeta.GetOutPoint(), asOfHeight)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2396,7 +2397,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
// the transaction does not exist in the main wallet.
|
||||
assert(mit != mapWallet.end());
|
||||
|
||||
int confirmations = mit->second.GetDepthInMainChain();
|
||||
int confirmations = mit->second.GetDepthInMainChain(asOfHeight);
|
||||
if (confirmations < 0) continue;
|
||||
if (confirmations >= minDepth) {
|
||||
noteMeta.SetConfirmations(confirmations);
|
||||
|
@ -2413,7 +2414,7 @@ SpendableInputs CWallet::FindSpendableInputs(
|
|||
* Outpoint is spent if any non-conflicted transaction
|
||||
* spends it:
|
||||
*/
|
||||
bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
|
||||
bool CWallet::IsSpent(const uint256& hash, unsigned int n, const std::optional<int>& asOfHeight) const
|
||||
{
|
||||
const COutPoint outpoint(hash, n);
|
||||
pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
|
||||
|
@ -2423,8 +2424,9 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
|
|||
{
|
||||
const uint256& wtxid = it->second;
|
||||
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
|
||||
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0)
|
||||
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain(asOfHeight) >= 0) {
|
||||
return true; // Spent
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -2433,7 +2435,7 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
|
|||
* Note is spent if any non-conflicted transaction
|
||||
* spends it:
|
||||
*/
|
||||
bool CWallet::IsSproutSpent(const uint256& nullifier) const {
|
||||
bool CWallet::IsSproutSpent(const uint256& nullifier, const std::optional<int>& asOfHeight) const {
|
||||
LOCK(cs_main);
|
||||
pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range;
|
||||
range = mapTxSproutNullifiers.equal_range(nullifier);
|
||||
|
@ -2441,14 +2443,14 @@ bool CWallet::IsSproutSpent(const uint256& nullifier) const {
|
|||
for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) {
|
||||
const uint256& wtxid = it->second;
|
||||
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
|
||||
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) {
|
||||
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain(asOfHeight) >= 0) {
|
||||
return true; // Spent
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CWallet::IsSaplingSpent(const uint256& nullifier) const {
|
||||
bool CWallet::IsSaplingSpent(const uint256& nullifier, const std::optional<int>& asOfHeight) const {
|
||||
LOCK(cs_main);
|
||||
pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range;
|
||||
range = mapTxSaplingNullifiers.equal_range(nullifier);
|
||||
|
@ -2456,17 +2458,17 @@ bool CWallet::IsSaplingSpent(const uint256& nullifier) const {
|
|||
for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) {
|
||||
const uint256& wtxid = it->second;
|
||||
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
|
||||
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) {
|
||||
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain(asOfHeight) >= 0) {
|
||||
return true; // Spent
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CWallet::IsOrchardSpent(const OrchardOutPoint& outpoint) const {
|
||||
bool CWallet::IsOrchardSpent(const OrchardOutPoint& outpoint, const std::optional<int>& asOfHeight) const {
|
||||
for (const auto& txid : orchardWallet.GetPotentialSpends(outpoint)) {
|
||||
std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(txid);
|
||||
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) {
|
||||
if (mit != mapWallet.end() && mit->second.GetDepthInMainChain(asOfHeight) >= 0) {
|
||||
return true; // Spent
|
||||
}
|
||||
}
|
||||
|
@ -4834,7 +4836,7 @@ void CWallet::ReacceptWalletTransactions()
|
|||
CWalletTx& wtx = item.second;
|
||||
assert(wtx.GetHash() == wtxid);
|
||||
|
||||
int nDepth = wtx.GetDepthInMainChain();
|
||||
int nDepth = wtx.GetDepthInMainChain(std::nullopt);
|
||||
|
||||
if (!wtx.IsCoinBase() && nDepth < 0) {
|
||||
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
|
||||
|
@ -4854,7 +4856,7 @@ bool CWalletTx::RelayWalletTransaction()
|
|||
assert(pwallet->GetBroadcastTransactions());
|
||||
if (!IsCoinBase())
|
||||
{
|
||||
if (GetDepthInMainChain() == 0) {
|
||||
if (GetDepthInMainChain(std::nullopt) == 0) {
|
||||
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
|
||||
RelayTransaction((CTransaction)*this);
|
||||
return true;
|
||||
|
@ -4906,10 +4908,10 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
|
|||
return debit;
|
||||
}
|
||||
|
||||
CAmount CWalletTx::GetCredit(const isminefilter& filter) const
|
||||
CAmount CWalletTx::GetCredit(const std::optional<int>& asOfHeight, const isminefilter& filter) const
|
||||
{
|
||||
// Must wait until coinbase is safely deep enough in the chain before valuing it
|
||||
if (IsCoinBase() && GetBlocksToMaturity() > 0)
|
||||
if (IsCoinBase() && GetBlocksToMaturity(asOfHeight) > 0)
|
||||
return 0;
|
||||
|
||||
int64_t credit = 0;
|
||||
|
@ -4939,9 +4941,9 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const
|
|||
return credit;
|
||||
}
|
||||
|
||||
CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
|
||||
CAmount CWalletTx::GetImmatureCredit(const std::optional<int>& asOfHeight, bool fUseCache) const
|
||||
{
|
||||
if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
|
||||
if (IsCoinBase() && GetBlocksToMaturity(asOfHeight) > 0 && IsInMainChain(asOfHeight))
|
||||
{
|
||||
if (fUseCache && fImmatureCreditCached)
|
||||
return nImmatureCreditCached;
|
||||
|
@ -4953,13 +4955,13 @@ CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const
|
|||
return 0;
|
||||
}
|
||||
|
||||
CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const
|
||||
CAmount CWalletTx::GetAvailableCredit(const std::optional<int>& asOfHeight, bool fUseCache, const isminefilter& filter) const
|
||||
{
|
||||
if (pwallet == nullptr)
|
||||
return 0;
|
||||
|
||||
// Must wait until coinbase is safely deep enough in the chain before valuing it
|
||||
if (IsCoinBase() && GetBlocksToMaturity() > 0)
|
||||
if (IsCoinBase() && GetBlocksToMaturity(asOfHeight) > 0)
|
||||
return 0;
|
||||
|
||||
CAmount* cache = nullptr;
|
||||
|
@ -4981,7 +4983,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
|
|||
uint256 hashTx = GetHash();
|
||||
for (unsigned int i = 0; i < vout.size(); i++)
|
||||
{
|
||||
if (!pwallet->IsSpent(hashTx, i))
|
||||
if (!pwallet->IsSpent(hashTx, i, asOfHeight))
|
||||
{
|
||||
const CTxOut &txout = vout[i];
|
||||
nCredit += pwallet->GetCredit(txout, filter);
|
||||
|
@ -4997,9 +4999,9 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
|
|||
return nCredit;
|
||||
}
|
||||
|
||||
CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const
|
||||
CAmount CWalletTx::GetImmatureWatchOnlyCredit(const std::optional<int>& asOfHeight, const bool fUseCache) const
|
||||
{
|
||||
if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())
|
||||
if (IsCoinBase() && GetBlocksToMaturity(asOfHeight) > 0 && IsInMainChain(asOfHeight))
|
||||
{
|
||||
if (fUseCache && fImmatureWatchCreditCached)
|
||||
return nImmatureWatchCreditCached;
|
||||
|
@ -5040,14 +5042,17 @@ bool CWalletTx::IsFromMe(const isminefilter& filter) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CWalletTx::IsTrusted() const
|
||||
bool CWalletTx::IsTrusted(const std::optional<int>& asOfHeight) const
|
||||
{
|
||||
// Quick answer in most cases
|
||||
if (!CheckFinalTx(*this))
|
||||
return false;
|
||||
int nDepth = GetDepthInMainChain();
|
||||
int nDepth = GetDepthInMainChain(asOfHeight);
|
||||
if (nDepth >= 1)
|
||||
return true;
|
||||
if (asOfHeight.has_value() && nDepth == 0)
|
||||
// don’t trust mempool tx if using `asOfHeight`
|
||||
return false;
|
||||
if (nDepth < 0)
|
||||
return false;
|
||||
if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit
|
||||
|
@ -5125,7 +5130,7 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime)
|
|||
*/
|
||||
|
||||
|
||||
CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) const
|
||||
CAmount CWallet::GetBalance(const std::optional<int>& asOfHeight, const isminefilter& filter, const int min_depth) const
|
||||
{
|
||||
CAmount nTotal = 0;
|
||||
{
|
||||
|
@ -5133,8 +5138,8 @@ CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) con
|
|||
for (const auto& entry : mapWallet)
|
||||
{
|
||||
const CWalletTx* pcoin = &entry.second;
|
||||
if (pcoin->IsTrusted() && pcoin->GetDepthInMainChain() >= min_depth) {
|
||||
nTotal += pcoin->GetAvailableCredit(true, filter);
|
||||
if (pcoin->IsTrusted(asOfHeight) && pcoin->GetDepthInMainChain(asOfHeight) >= min_depth) {
|
||||
nTotal += pcoin->GetAvailableCredit(asOfHeight, true, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5142,7 +5147,7 @@ CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) con
|
|||
return nTotal;
|
||||
}
|
||||
|
||||
CAmount CWallet::GetUnconfirmedBalance() const
|
||||
CAmount CWallet::GetUnconfirmedTransparentBalance() const
|
||||
{
|
||||
CAmount nTotal = 0;
|
||||
{
|
||||
|
@ -5150,14 +5155,14 @@ CAmount CWallet::GetUnconfirmedBalance() const
|
|||
for (const auto& entry : mapWallet)
|
||||
{
|
||||
const CWalletTx* pcoin = &entry.second;
|
||||
if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
|
||||
nTotal += pcoin->GetAvailableCredit();
|
||||
if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted(std::nullopt) && pcoin->GetDepthInMainChain(std::nullopt) == 0))
|
||||
nTotal += pcoin->GetAvailableCredit(std::nullopt);
|
||||
}
|
||||
}
|
||||
return nTotal;
|
||||
}
|
||||
|
||||
CAmount CWallet::GetImmatureBalance() const
|
||||
CAmount CWallet::GetImmatureBalance(const std::optional<int>& asOfHeight) const
|
||||
{
|
||||
CAmount nTotal = 0;
|
||||
{
|
||||
|
@ -5165,36 +5170,7 @@ CAmount CWallet::GetImmatureBalance() const
|
|||
for (const auto& entry : mapWallet)
|
||||
{
|
||||
const CWalletTx* pcoin = &entry.second;
|
||||
nTotal += pcoin->GetImmatureCredit();
|
||||
}
|
||||
}
|
||||
return nTotal;
|
||||
}
|
||||
|
||||
CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
|
||||
{
|
||||
CAmount nTotal = 0;
|
||||
{
|
||||
LOCK2(cs_main, cs_wallet);
|
||||
for (const auto& entry : mapWallet)
|
||||
{
|
||||
const CWalletTx* pcoin = &entry.second;
|
||||
if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
|
||||
nTotal += pcoin->GetAvailableCredit(true, ISMINE_WATCH_ONLY);
|
||||
}
|
||||
}
|
||||
return nTotal;
|
||||
}
|
||||
|
||||
CAmount CWallet::GetImmatureWatchOnlyBalance() const
|
||||
{
|
||||
CAmount nTotal = 0;
|
||||
{
|
||||
LOCK2(cs_main, cs_wallet);
|
||||
for (const auto& entry : mapWallet)
|
||||
{
|
||||
const CWalletTx* pcoin = &entry.second;
|
||||
nTotal += pcoin->GetImmatureWatchOnlyCredit();
|
||||
nTotal += pcoin->GetImmatureCredit(asOfHeight);
|
||||
}
|
||||
}
|
||||
return nTotal;
|
||||
|
@ -5213,8 +5189,8 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) cons
|
|||
CAmount balance = 0;
|
||||
for (const auto& entry : mapWallet) {
|
||||
const CWalletTx& wtx = entry.second;
|
||||
const int depth = wtx.GetDepthInMainChain();
|
||||
if (depth < 0 || !CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0) {
|
||||
const int depth = wtx.GetDepthInMainChain(std::nullopt);
|
||||
if (depth < 0 || !CheckFinalTx(wtx) || wtx.GetBlocksToMaturity(std::nullopt) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -5240,45 +5216,43 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) cons
|
|||
}
|
||||
|
||||
void CWallet::AvailableCoins(vector<COutput>& vCoins,
|
||||
const std::optional<int>& asOfHeight,
|
||||
bool fOnlyConfirmed,
|
||||
const CCoinControl *coinControl,
|
||||
bool fIncludeZeroValue,
|
||||
bool fIncludeCoinBase,
|
||||
bool fOnlySpendable,
|
||||
int nMinDepth,
|
||||
std::set<CTxDestination>* onlyFilterByDests) const
|
||||
const std::set<CTxDestination>& onlyFilterByDests) const
|
||||
{
|
||||
assert(nMinDepth >= 0);
|
||||
assert(!asOfHeight.has_value() || nMinDepth > 0);
|
||||
AssertLockHeld(cs_main);
|
||||
AssertLockHeld(cs_wallet);
|
||||
|
||||
vCoins.clear();
|
||||
|
||||
{
|
||||
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
||||
{
|
||||
const uint256& wtxid = it->first;
|
||||
const CWalletTx* pcoin = &(*it).second;
|
||||
|
||||
if (!CheckFinalTx(*pcoin))
|
||||
for (const auto& [wtxid, pcoin] : mapWallet) {
|
||||
if (!CheckFinalTx(pcoin))
|
||||
continue;
|
||||
|
||||
if (fOnlyConfirmed && !pcoin->IsTrusted())
|
||||
if (fOnlyConfirmed && !pcoin.IsTrusted(asOfHeight))
|
||||
continue;
|
||||
|
||||
if (pcoin->IsCoinBase() && !fIncludeCoinBase)
|
||||
if (pcoin.IsCoinBase() && !fIncludeCoinBase)
|
||||
continue;
|
||||
|
||||
bool isCoinbase = pcoin->IsCoinBase();
|
||||
if (isCoinbase && pcoin->GetBlocksToMaturity() > 0)
|
||||
bool isCoinbase = pcoin.IsCoinBase();
|
||||
if (isCoinbase && pcoin.GetBlocksToMaturity(asOfHeight) > 0)
|
||||
continue;
|
||||
|
||||
int nDepth = pcoin->GetDepthInMainChain();
|
||||
int nDepth = pcoin.GetDepthInMainChain(asOfHeight);
|
||||
if (nDepth < nMinDepth)
|
||||
continue;
|
||||
|
||||
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
|
||||
const auto& output = pcoin->vout[i];
|
||||
for (unsigned int i = 0; i < pcoin.vout.size(); i++) {
|
||||
const auto& output = pcoin.vout[i];
|
||||
isminetype mine = IsMine(output);
|
||||
|
||||
bool isSpendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
|
||||
|
@ -5288,17 +5262,17 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins,
|
|||
continue;
|
||||
|
||||
// Filter by specific destinations if needed
|
||||
if (onlyFilterByDests && !onlyFilterByDests->empty()) {
|
||||
if (!onlyFilterByDests.empty()) {
|
||||
CTxDestination address;
|
||||
if (!ExtractDestination(output.scriptPubKey, address) || onlyFilterByDests->count(address) == 0) {
|
||||
if (!ExtractDestination(output.scriptPubKey, address) || onlyFilterByDests.count(address) == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
|
||||
!IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) &&
|
||||
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected((*it).first, i)))
|
||||
vCoins.push_back(COutput(pcoin, i, nDepth, isSpendable, isCoinbase));
|
||||
if (!(IsSpent(wtxid, i, asOfHeight)) && mine != ISMINE_NO &&
|
||||
!IsLockedCoin(wtxid, i) && (pcoin.vout[i].nValue > 0 || fIncludeZeroValue) &&
|
||||
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(wtxid, i)))
|
||||
vCoins.push_back(COutput(&pcoin, i, nDepth, isSpendable, isCoinbase));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5455,8 +5429,8 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
|
|||
{
|
||||
// Output parameter fOnlyCoinbaseCoinsRet is set to true when the only available coins are coinbase utxos.
|
||||
vector<COutput> vCoinsNoCoinbase, vCoinsWithCoinbase;
|
||||
AvailableCoins(vCoinsNoCoinbase, true, coinControl, false, false);
|
||||
AvailableCoins(vCoinsWithCoinbase, true, coinControl, false, true);
|
||||
AvailableCoins(vCoinsNoCoinbase, std::nullopt, true, coinControl, false, false);
|
||||
AvailableCoins(vCoinsWithCoinbase, std::nullopt, true, coinControl, false, true);
|
||||
fOnlyCoinbaseCoinsRet = vCoinsNoCoinbase.size() == 0 && vCoinsWithCoinbase.size() > 0;
|
||||
|
||||
// If coinbase utxos can only be sent to zaddrs, exclude any coinbase utxos from coin selection.
|
||||
|
@ -5725,7 +5699,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
|||
//reflecting an assumption the user would accept a bit more delay for
|
||||
//a chance at a free transaction.
|
||||
//But mempool inputs might still be in the mempool, so their age stays 0
|
||||
int age = pcoin.first->GetDepthInMainChain();
|
||||
int age = pcoin.first->GetDepthInMainChain(std::nullopt);
|
||||
if (age != 0)
|
||||
age += 1;
|
||||
dPriority += (double)nCredit * age;
|
||||
|
@ -6220,7 +6194,7 @@ int64_t CWallet::GetOldestKeyPoolTime()
|
|||
return keypool.nTime;
|
||||
}
|
||||
|
||||
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
|
||||
std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(const std::optional<int>& asOfHeight)
|
||||
{
|
||||
map<CTxDestination, CAmount> balances;
|
||||
|
||||
|
@ -6230,13 +6204,13 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
|
|||
{
|
||||
CWalletTx *pcoin = &walletEntry.second;
|
||||
|
||||
if (!CheckFinalTx(*pcoin) || !pcoin->IsTrusted())
|
||||
if (!CheckFinalTx(*pcoin) || !pcoin->IsTrusted(asOfHeight))
|
||||
continue;
|
||||
|
||||
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
|
||||
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity(asOfHeight) > 0)
|
||||
continue;
|
||||
|
||||
int nDepth = pcoin->GetDepthInMainChain();
|
||||
int nDepth = pcoin->GetDepthInMainChain(asOfHeight);
|
||||
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
|
||||
continue;
|
||||
|
||||
|
@ -6248,7 +6222,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
|
|||
if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
|
||||
continue;
|
||||
|
||||
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue;
|
||||
CAmount n = IsSpent(walletEntry.first, i, asOfHeight) ? 0 : pcoin->vout[i].nValue;
|
||||
|
||||
if (!balances.count(addr))
|
||||
balances[addr] = 0;
|
||||
|
@ -6984,39 +6958,43 @@ void CMerkleTx::SetMerkleBranch(const CBlock& block)
|
|||
}
|
||||
}
|
||||
|
||||
int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const
|
||||
int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet, const std::optional<int>& asOfHeight) const
|
||||
{
|
||||
if (hashBlock.IsNull() || nIndex == -1)
|
||||
return 0;
|
||||
AssertLockHeld(cs_main);
|
||||
int effectiveChainHeight = min(chainActive.Height(), asOfHeight.value_or(chainActive.Height()));
|
||||
|
||||
// Find the block it claims to be in
|
||||
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
|
||||
if (mi == mapBlockIndex.end())
|
||||
return 0;
|
||||
CBlockIndex* pindex = (*mi).second;
|
||||
if (!pindex || !chainActive.Contains(pindex))
|
||||
if (!pindex ||
|
||||
!chainActive.Contains(pindex) ||
|
||||
pindex->nHeight > effectiveChainHeight) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pindexRet = pindex;
|
||||
return chainActive.Height() - pindex->nHeight + 1;
|
||||
return effectiveChainHeight - pindex->nHeight + 1;
|
||||
}
|
||||
|
||||
int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
|
||||
int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet, const std::optional<int>& asOfHeight) const
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
int nResult = GetDepthInMainChainINTERNAL(pindexRet);
|
||||
if (nResult == 0 && !mempool.exists(GetHash()))
|
||||
int nResult = GetDepthInMainChainINTERNAL(pindexRet, asOfHeight);
|
||||
if (nResult == 0 && (asOfHeight.has_value() || !mempool.exists(GetHash())))
|
||||
return -1; // Not in chain, not in mempool
|
||||
|
||||
return nResult;
|
||||
}
|
||||
|
||||
int CMerkleTx::GetBlocksToMaturity() const
|
||||
int CMerkleTx::GetBlocksToMaturity(const std::optional<int>& asOfHeight) const
|
||||
{
|
||||
if (!IsCoinBase())
|
||||
return 0;
|
||||
return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
|
||||
return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain(asOfHeight));
|
||||
}
|
||||
|
||||
|
||||
|
@ -7098,6 +7076,7 @@ void CWallet::GetFilteredNotes(
|
|||
std::vector<SaplingNoteEntry>& saplingEntriesRet,
|
||||
std::vector<OrchardNoteMetadata>& orchardNotesRet,
|
||||
const std::optional<NoteFilter>& noteFilter,
|
||||
const std::optional<int>& asOfHeight,
|
||||
int minDepth,
|
||||
int maxDepth,
|
||||
bool ignoreSpent,
|
||||
|
@ -7116,8 +7095,8 @@ void CWallet::GetFilteredNotes(
|
|||
|
||||
// Filter the transactions before checking for notes
|
||||
if (!CheckFinalTx(wtx) ||
|
||||
wtx.GetDepthInMainChain() < minDepth ||
|
||||
wtx.GetDepthInMainChain() > maxDepth) {
|
||||
wtx.GetDepthInMainChain(asOfHeight) < minDepth ||
|
||||
wtx.GetDepthInMainChain(asOfHeight) > maxDepth) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -7137,7 +7116,7 @@ void CWallet::GetFilteredNotes(
|
|||
}
|
||||
|
||||
// skip note which has been spent
|
||||
if (ignoreSpent && nd.nullifier && IsSproutSpent(*nd.nullifier)) {
|
||||
if (ignoreSpent && nd.nullifier && IsSproutSpent(*nd.nullifier, asOfHeight)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -7175,7 +7154,7 @@ void CWallet::GetFilteredNotes(
|
|||
(unsigned char) j);
|
||||
|
||||
sproutEntriesRet.push_back(SproutNoteEntry {
|
||||
jsop, pa, plaintext.note(pa), plaintext.memo(), wtx.GetDepthInMainChain() });
|
||||
jsop, pa, plaintext.note(pa), plaintext.memo(), wtx.GetDepthInMainChain(asOfHeight) });
|
||||
|
||||
} catch (const note_decryption_failed &err) {
|
||||
// Couldn't decrypt with this spending key
|
||||
|
@ -7206,7 +7185,7 @@ void CWallet::GetFilteredNotes(
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ignoreSpent && nd.nullifier.has_value() && IsSaplingSpent(nd.nullifier.value())) {
|
||||
if (ignoreSpent && nd.nullifier.has_value() && IsSaplingSpent(nd.nullifier.value(), asOfHeight)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -7222,7 +7201,7 @@ void CWallet::GetFilteredNotes(
|
|||
|
||||
auto note = notePt.note(nd.ivk).value();
|
||||
saplingEntriesRet.push_back(SaplingNoteEntry {
|
||||
op, pa, note, notePt.memo(), wtx.GetDepthInMainChain() });
|
||||
op, pa, note, notePt.memo(), wtx.GetDepthInMainChain(asOfHeight) });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7248,13 +7227,13 @@ void CWallet::GetFilteredNotes(
|
|||
}
|
||||
|
||||
for (auto& noteMeta : orchardNotes) {
|
||||
if (ignoreSpent && IsOrchardSpent(noteMeta.GetOutPoint())) {
|
||||
if (ignoreSpent && IsOrchardSpent(noteMeta.GetOutPoint(), asOfHeight)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto wtx = GetWalletTx(noteMeta.GetOutPoint().hash);
|
||||
if (wtx) {
|
||||
auto confirmations = wtx->GetDepthInMainChain();
|
||||
auto confirmations = wtx->GetDepthInMainChain(asOfHeight);
|
||||
if (confirmations >= minDepth && confirmations <= maxDepth) {
|
||||
noteMeta.SetConfirmations(confirmations);
|
||||
orchardNotesRet.push_back(noteMeta);
|
||||
|
|
|
@ -399,7 +399,11 @@ struct SaplingNoteEntry
|
|||
class CMerkleTx : public CTransaction
|
||||
{
|
||||
private:
|
||||
int GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const;
|
||||
/**
|
||||
* **NB**: Unlike `GetDepthInMainChain`, this returns 0 for any case where
|
||||
* it’s not in the chain (including if it’s not in the mempool).
|
||||
*/
|
||||
int GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet, const std::optional<int>& asOfHeight) const;
|
||||
|
||||
public:
|
||||
uint256 hashBlock;
|
||||
|
@ -438,13 +442,16 @@ public:
|
|||
/**
|
||||
* Return depth of transaction in blockchain:
|
||||
* -1 : not in blockchain, and not in memory pool (conflicted transaction)
|
||||
* 0 : in memory pool, waiting to be included in a block
|
||||
* 0 : in memory pool, waiting to be included in a block (never returned if `asOfHeight` is set)
|
||||
* >=1 : this many blocks deep in the main chain
|
||||
*/
|
||||
int GetDepthInMainChain(const CBlockIndex* &pindexRet) const;
|
||||
int GetDepthInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
|
||||
bool IsInMainChain() const { const CBlockIndex *pindexRet; return GetDepthInMainChainINTERNAL(pindexRet) > 0; }
|
||||
int GetBlocksToMaturity() const;
|
||||
int GetDepthInMainChain(const CBlockIndex* &pindexRet, const std::optional<int>& asOfHeight) const;
|
||||
int GetDepthInMainChain(const std::optional<int>& asOfHeight) const {
|
||||
const CBlockIndex *pindexRet;
|
||||
return GetDepthInMainChain(pindexRet, asOfHeight);
|
||||
}
|
||||
bool IsInMainChain(const std::optional<int>& asOfHeight) const { return GetDepthInMainChain(asOfHeight) > 0; }
|
||||
int GetBlocksToMaturity(const std::optional<int>& asOfHeight) const;
|
||||
/** Pass this transaction to the mempool. Fails if absolute fee exceeds maxTxFee. */
|
||||
bool AcceptToMemoryPool(CValidationState& state, bool fLimitFree=true, bool fRejectAbsurdFee=true);
|
||||
};
|
||||
|
@ -670,10 +677,10 @@ public:
|
|||
|
||||
//! filter decides which addresses will count towards the debit
|
||||
CAmount GetDebit(const isminefilter& filter) const;
|
||||
CAmount GetCredit(const isminefilter& filter) const;
|
||||
CAmount GetImmatureCredit(bool fUseCache=true) const;
|
||||
CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const;
|
||||
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const;
|
||||
CAmount GetCredit(const std::optional<int>& asOfHeight, const isminefilter& filter) const;
|
||||
CAmount GetImmatureCredit(const std::optional<int>& asOfHeight, bool fUseCache=true) const;
|
||||
CAmount GetAvailableCredit(const std::optional<int>& asOfHeight, bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const;
|
||||
CAmount GetImmatureWatchOnlyCredit(const std::optional<int>& asOfHeight, const bool fUseCache=true) const;
|
||||
CAmount GetChange() const;
|
||||
|
||||
void GetAmounts(std::list<COutputEntry>& listReceived,
|
||||
|
@ -681,7 +688,7 @@ public:
|
|||
|
||||
bool IsFromMe(const isminefilter& filter) const;
|
||||
|
||||
bool IsTrusted() const;
|
||||
bool IsTrusted(const std::optional<int>& asOfHeight) const;
|
||||
|
||||
int64_t GetTxTime() const;
|
||||
int GetRequestCount() const;
|
||||
|
@ -1428,15 +1435,18 @@ public:
|
|||
|
||||
/**
|
||||
* populate vCoins with vector of available COutputs.
|
||||
*
|
||||
* **NB**: If `asOfHeight` is specified, then `nMinDepth` must be `> 0`.
|
||||
*/
|
||||
void AvailableCoins(std::vector<COutput>& vCoins,
|
||||
const std::optional<int>& asOfHeight,
|
||||
bool fOnlyConfirmed=true,
|
||||
const CCoinControl *coinControl = NULL,
|
||||
bool fIncludeZeroValue=false,
|
||||
bool fIncludeCoinBase=true,
|
||||
bool fOnlySpendable=false,
|
||||
int nMinDepth = 0,
|
||||
std::set<CTxDestination>* onlyFilterByDests = nullptr) const;
|
||||
const std::set<CTxDestination>& onlyFilterByDests = std::set<CTxDestination>()) const;
|
||||
|
||||
/**
|
||||
* Shuffle and select coins until nTargetValue is reached while avoiding
|
||||
|
@ -1516,16 +1526,17 @@ public:
|
|||
SpendableInputs FindSpendableInputs(
|
||||
ZTXOSelector paymentSource,
|
||||
bool allowTransparentCoinbase,
|
||||
uint32_t minDepth) const;
|
||||
uint32_t minDepth,
|
||||
const std::optional<int>& asOfHeight) const;
|
||||
|
||||
bool SelectorMatchesAddress(const ZTXOSelector& source, const CTxDestination& a0) const;
|
||||
bool SelectorMatchesAddress(const ZTXOSelector& source, const libzcash::SproutPaymentAddress& a0) const;
|
||||
bool SelectorMatchesAddress(const ZTXOSelector& source, const libzcash::SaplingPaymentAddress& a0) const;
|
||||
|
||||
bool IsSpent(const uint256& hash, unsigned int n) const;
|
||||
bool IsSproutSpent(const uint256& nullifier) const;
|
||||
bool IsSaplingSpent(const uint256& nullifier) const;
|
||||
bool IsOrchardSpent(const OrchardOutPoint& outpoint) const;
|
||||
bool IsSpent(const uint256& hash, unsigned int n, const std::optional<int>& asOfHeight) const;
|
||||
bool IsSproutSpent(const uint256& nullifier, const std::optional<int>& asOfHeight) const;
|
||||
bool IsSaplingSpent(const uint256& nullifier, const std::optional<int>& asOfHeight) const;
|
||||
bool IsOrchardSpent(const OrchardOutPoint& outpoint, const std::optional<int>& asOfHeight) const;
|
||||
|
||||
bool IsLockedCoin(uint256 hash, unsigned int n) const;
|
||||
void LockCoin(COutPoint& output);
|
||||
|
@ -1793,11 +1804,14 @@ public:
|
|||
void ReacceptWalletTransactions();
|
||||
void ResendWalletTransactions(int64_t nBestBlockTime);
|
||||
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime);
|
||||
CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
|
||||
CAmount GetUnconfirmedBalance() const;
|
||||
CAmount GetImmatureBalance() const;
|
||||
CAmount GetUnconfirmedWatchOnlyBalance() const;
|
||||
CAmount GetImmatureWatchOnlyBalance() const;
|
||||
CAmount GetBalance(const std::optional<int>& asOfHeight,
|
||||
const isminefilter& filter=ISMINE_SPENDABLE,
|
||||
const int min_depth=0) const;
|
||||
/**
|
||||
* Returns the balance taking into account _only_ transactions in the mempool.
|
||||
*/
|
||||
CAmount GetUnconfirmedTransparentBalance() const;
|
||||
CAmount GetImmatureBalance(const std::optional<int>& asOfHeight) const;
|
||||
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth) const;
|
||||
|
||||
/**
|
||||
|
@ -1872,7 +1886,7 @@ public:
|
|||
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
|
||||
|
||||
std::set< std::set<CTxDestination> > GetAddressGroupings();
|
||||
std::map<CTxDestination, CAmount> GetAddressBalances();
|
||||
std::map<CTxDestination, CAmount> GetAddressBalances(const std::optional<int>& asOfHeight);
|
||||
|
||||
std::optional<uint256> GetSproutNoteNullifier(
|
||||
const JSDescription& jsdesc,
|
||||
|
@ -2079,6 +2093,7 @@ public:
|
|||
std::vector<SaplingNoteEntry>& saplingEntriesRet,
|
||||
std::vector<OrchardNoteMetadata>& orchardNotesRet,
|
||||
const std::optional<NoteFilter>& noteFilter,
|
||||
const std::optional<int>& asOfHeight,
|
||||
int minDepth,
|
||||
int maxDepth=INT_MAX,
|
||||
bool ignoreSpent=true,
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
# one. Any remaining diff signals an error.
|
||||
|
||||
export LC_ALL=C
|
||||
if test "x$1" = "x"; then
|
||||
if test -z "$1"; then
|
||||
echo "Usage: $0 <commit>..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check that the working tree is clean (as this script performs hard resets).
|
||||
TREE_CHANGES=$(git diff HEAD | wc -l)
|
||||
if test "x$TREE_CHANGES" != "x0"; then
|
||||
if test "$TREE_CHANGES" != "0"; then
|
||||
echo "The working tree is not clean."
|
||||
exit 1
|
||||
fi
|
||||
|
@ -32,7 +32,7 @@ for commit in $(git rev-list --reverse $1); do
|
|||
if git rev-list -n 1 --pretty="%s" $commit | grep -q "^scripted-diff:"; then
|
||||
git checkout --quiet $commit^ || exit
|
||||
SCRIPT="$(git rev-list --format=%b -n1 $commit | sed '/^-BEGIN VERIFY SCRIPT-$/,/^-END VERIFY SCRIPT-$/{//!b};d')"
|
||||
if test "x$SCRIPT" = "x"; then
|
||||
if test -z "$SCRIPT"; then
|
||||
echo "Error: missing script for: $commit"
|
||||
echo "Failed"
|
||||
RET=1
|
||||
|
|
|
@ -40,7 +40,7 @@ if ! command -v shellcheck > /dev/null; then
|
|||
fi
|
||||
|
||||
EXCLUDE="--exclude=$(IFS=','; echo "${disabled[*]}")"
|
||||
if ! shellcheck "$EXCLUDE" $(git ls-files -- '*.sh' | grep -vE 'src/(leveldb|secp256k1|univalue)/'); then
|
||||
if ! shellcheck "$EXCLUDE" $(git ls-files -- '*.sh' | grep -vE '(qa/zcash/checksec\.sh|src/(|leveldb|secp256k1|univalue)/)'); then
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ if [ -z "${CONFIGURE_FLAGS-}" ]; then
|
|||
CONFIGURE_FLAGS=""
|
||||
fi
|
||||
|
||||
if [ "x$*" = 'x--help' ]
|
||||
if [ "$*" = '--help' ]
|
||||
then
|
||||
cat <<EOF
|
||||
Usage:
|
||||
|
|
Loading…
Reference in New Issue