Merge remote-tracking branch 'upstream/master' into bug/4119-rewind_detection
This commit is contained in:
commit
5a8f6f8058
|
@ -155,7 +155,9 @@ endif
|
|||
dist_bin_SCRIPTS = zcutil/fetch-params.sh
|
||||
dist_noinst_SCRIPTS = autogen.sh zcutil/build-debian-package.sh zcutil/build.sh
|
||||
|
||||
EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.sh qa/rpc-tests qa/zcash $(DIST_DOCS) $(BIN_CHECKS)
|
||||
RUST_DIST = $(top_srcdir)/.cargo $(top_srcdir)/Cargo.toml $(top_srcdir)/Cargo.lock rust-toolchain
|
||||
|
||||
EXTRA_DIST = $(top_srcdir)/share/genbuild.sh qa/pull-tester/rpc-tests.sh qa/rpc-tests qa/zcash $(DIST_DOCS) $(BIN_CHECKS) $(RUST_DIST)
|
||||
|
||||
install-exec-hook:
|
||||
mv $(DESTDIR)$(bindir)/fetch-params.sh $(DESTDIR)$(bindir)/zcash-fetch-params
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Zcash 2.1.1-1
|
||||
Zcash 2.1.2-rc1
|
||||
<img align="right" width="120" height="80" src="doc/imgs/logo.png">
|
||||
===========
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
|
|||
AC_PREREQ([2.60])
|
||||
define(_CLIENT_VERSION_MAJOR, 2)
|
||||
define(_CLIENT_VERSION_MINOR, 1)
|
||||
define(_CLIENT_VERSION_REVISION, 1)
|
||||
define(_CLIENT_VERSION_BUILD, 51)
|
||||
define(_CLIENT_VERSION_REVISION, 2)
|
||||
define(_CLIENT_VERSION_BUILD, 25)
|
||||
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)))
|
||||
define(_CLIENT_VERSION_IS_RELEASE, true)
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
zcash (2.1.2~rc1) stable; urgency=medium
|
||||
|
||||
* 2.1.2-rc1 release.
|
||||
|
||||
-- Electric Coin Company <team@electriccoin.co> Wed, 15 Apr 2020 17:47:45 -0600
|
||||
|
||||
zcash (2.1.1+1) stable; urgency=critical
|
||||
|
||||
* 2.1.1-1 release.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
name: "zcash-2.1.1-1"
|
||||
name: "zcash-2.1.2-rc1"
|
||||
enable_cache: true
|
||||
distro: "debian"
|
||||
suites:
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.11.
|
||||
.TH ZCASH-CLI "1" "February 2020" "zcash-cli v2.1.1-1" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
|
||||
.TH ZCASH-CLI "1" "April 2020" "zcash-cli v2.1.2-rc1" "User Commands"
|
||||
.SH NAME
|
||||
zcash-cli \- manual page for zcash-cli v2.1.1-1
|
||||
zcash-cli \- manual page for zcash-cli v2.1.2-rc1
|
||||
.SH DESCRIPTION
|
||||
Zcash RPC client version v2.1.1\-1
|
||||
Zcash RPC client version v2.1.2\-rc1
|
||||
.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.47.11.
|
||||
.TH ZCASH-TX "1" "February 2020" "zcash-tx v2.1.1-1" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
|
||||
.TH ZCASH-TX "1" "April 2020" "zcash-tx v2.1.2-rc1" "User Commands"
|
||||
.SH NAME
|
||||
zcash-tx \- manual page for zcash-tx v2.1.1-1
|
||||
zcash-tx \- manual page for zcash-tx v2.1.2-rc1
|
||||
.SH DESCRIPTION
|
||||
Zcash zcash\-tx utility version v2.1.1\-1
|
||||
Zcash zcash\-tx utility version v2.1.2\-rc1
|
||||
.SS "Usage:"
|
||||
.TP
|
||||
zcash\-tx [options] <hex\-tx> [commands]
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.11.
|
||||
.TH ZCASHD "1" "February 2020" "zcashd v2.1.1-1" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
|
||||
.TH ZCASHD "1" "April 2020" "zcashd v2.1.2-rc1" "User Commands"
|
||||
.SH NAME
|
||||
zcashd \- manual page for zcashd v2.1.1-1
|
||||
zcashd \- manual page for zcashd v2.1.2-rc1
|
||||
.SH DESCRIPTION
|
||||
Zcash Daemon version v2.1.1\-1
|
||||
Zcash Daemon version v2.1.2\-rc1
|
||||
.PP
|
||||
In order to ensure you are adequately protecting your privacy when using Zcash,
|
||||
please see <https://z.cash/support/security/>.
|
||||
|
@ -67,12 +67,6 @@ Imports blocks from external blk000??.dat file on startup
|
|||
.IP
|
||||
Keep at most <n> unconnectable transactions in memory (default: 100)
|
||||
.HP
|
||||
\fB\-maxtimeadjustment=\fR<n>
|
||||
.IP
|
||||
Maximum allowed median peer time offset adjustment, in seconds. Local
|
||||
perspective of time may be influenced by peers forward or backward by
|
||||
this amount. (default: 0 seconds, maximum: 1500 seconds)
|
||||
.HP
|
||||
\fB\-par=\fR<n>
|
||||
.IP
|
||||
Set the number of script verification threads (\fB\-16\fR to 16, 0 = auto, <0 =
|
||||
|
@ -99,6 +93,11 @@ Rebuild block chain index from current blk000??.dat files on startup
|
|||
Create new files with system default permissions, instead of umask 077
|
||||
(only effective with disabled wallet functionality)
|
||||
.HP
|
||||
\fB\-txexpirynotify=\fR<cmd>
|
||||
.IP
|
||||
Execute command when transaction expires (%s in cmd is replaced by
|
||||
transaction id)
|
||||
.HP
|
||||
\fB\-txindex\fR
|
||||
.IP
|
||||
Maintain a full transaction index, used by the getrawtransaction rpc
|
||||
|
@ -346,7 +345,8 @@ optional). If <category> is not supplied or if <category> = 1, output
|
|||
all debugging information. <category> can be: addrman, alert, bench,
|
||||
coindb, db, estimatefee, http, libevent, lock, mempool, net,
|
||||
partitioncheck, pow, proxy, prune, rand, reindex, rpc, selectcoins, tor,
|
||||
zmq, zrpc, zrpcunsafe (implies zrpc).
|
||||
zmq, zrpc, zrpcunsafe (implies zrpc). For multiple specific categories
|
||||
use \fB\-debug=\fR<category> multiple times.
|
||||
.HP
|
||||
\fB\-experimentalfeatures\fR
|
||||
.IP
|
||||
|
|
|
@ -4,6 +4,69 @@ release-notes at release time)
|
|||
Notable changes
|
||||
===============
|
||||
|
||||
Network Upgrade 3: Heartwood
|
||||
----------------------------
|
||||
|
||||
The code preparations for the Heartwood network upgrade are finished and
|
||||
included in this release. The following ZIPs are being deployed:
|
||||
|
||||
- [ZIP 213: Shielded Coinbase](https://zips.z.cash/zip-0213)
|
||||
- [ZIP 221: FlyClient - Consensus-Layer Changes](https://zips.z.cash/zip-0221)
|
||||
|
||||
Heartwood will activate on testnet at height 903800, and can also be activated
|
||||
at a specific height in regtest mode by setting the config option
|
||||
`-nuparams=f5b9230b:HEIGHT`.
|
||||
|
||||
As a reminder, because the Heartwood activation height is not yet specified for
|
||||
mainnet, version 2.1.2 will behave similarly as other pre-Heartwood releases
|
||||
even after a future activation of Heartwood on the network. Upgrading from 2.1.2
|
||||
will be required in order to follow the Heartwood network upgrade on mainnet.
|
||||
|
||||
See [ZIP 250](https://zips.z.cash/zip-0250) for additional information about the
|
||||
deployment process for Heartwood.
|
||||
|
||||
### Mining to Sapling addresses
|
||||
|
||||
Miners and mining pools that wish to test the new "shielded coinbase" support on
|
||||
the Heartwood testnet can generate a new Sapling address with `z_getnewaddress`,
|
||||
add the config option `mineraddress=SAPLING_ADDRESS` to their `zcash.conf` file,
|
||||
and then restart their `zcashd` node. `getblocktemplate` will then return
|
||||
coinbase transactions containing a shielded miner output.
|
||||
|
||||
Note that `mineraddress` should only be set to a Sapling address after the
|
||||
Heartwood network upgrade has activated; setting a Sapling address prior to
|
||||
Heartwood activation will cause `getblocktemplate` to return block templates
|
||||
that cannot be mined.
|
||||
|
||||
Sapling viewing keys support
|
||||
----------------------------
|
||||
|
||||
Support for Sapling viewing keys (specifically, Sapling extended full viewing
|
||||
keys, as described in [ZIP 32](https://zips.z.cash/zip-0032)), has been added to
|
||||
the wallet. Nodes will track both sent and received transactions for any Sapling
|
||||
addresses associated with the imported Sapling viewing keys.
|
||||
|
||||
- Use the `z_exportviewingkey` RPC method to obtain the viewing key for a
|
||||
shielded address in a node's wallet. For Sapling addresses, these always begin
|
||||
with "zxviews" (or "zxviewtestsapling" for testnet addresses).
|
||||
|
||||
- Use `z_importviewingkey` to import a viewing key into another node. Imported
|
||||
Sapling viewing keys will be stored in the wallet, and remembered across
|
||||
restarts.
|
||||
|
||||
- `z_getbalance` will show the balance of a Sapling address associated with an
|
||||
imported Sapling viewing key. Balances for Sapling viewing keys will be
|
||||
included in the output of `z_gettotalbalance` when the `includeWatchonly`
|
||||
parameter is set to `true`.
|
||||
|
||||
- RPC methods for viewing shielded transaction information (such as
|
||||
`z_listreceivedbyaddress`) will return information for Sapling addresses
|
||||
associated with imported Sapling viewing keys.
|
||||
|
||||
Details about what information can be viewed with these Sapling viewing keys,
|
||||
and what guarantees you have about that information, can be found in
|
||||
[ZIP 310](https://zips.z.cash/zip-0310).
|
||||
|
||||
Removal of time adjustment and the -maxtimeadjustment= option
|
||||
-------------------------------------------------------------
|
||||
|
||||
|
@ -19,8 +82,8 @@ This effectively disabled time adjustment; however, a `-maxtimeadjustment=`
|
|||
option was provided to override this default.
|
||||
|
||||
As a simplification the time adjustment code has now been completely removed,
|
||||
together with `-maxtimeadjustment=`. Node operators should instead simply
|
||||
ensure that local time is set reasonably accurately.
|
||||
together with `-maxtimeadjustment=`. Node operators should instead ensure that
|
||||
their local time is set reasonably accurately.
|
||||
|
||||
If it appears that the node has a significantly different time than its peers,
|
||||
a warning will still be logged and indicated on the metrics screen if enabled.
|
||||
|
@ -53,6 +116,62 @@ this includes watch-only addresses linked to viewing keys imported with
|
|||
`z_importviewingkey`, as well as addresses with spending keys (both generated
|
||||
with `z_getnewaddress` and imported with `z_importkey`).
|
||||
|
||||
Better error messages for rejected transactions after network upgrades
|
||||
----------------------------------------------------------------------
|
||||
|
||||
The Zcash network upgrade process includes several features designed to protect
|
||||
users. One of these is the "consensus branch ID", which prevents transactions
|
||||
created after a network upgrade has activated from being replayed on another
|
||||
chain (that might have occurred due to, for example, a
|
||||
[friendly fork](https://electriccoin.co/blog/future-friendly-fork/)). This is
|
||||
known as "two-way replay protection", and is a core requirement by
|
||||
[various](https://blog.bitgo.com/bitgos-approach-to-handling-a-hard-fork-71e572506d7d?gi=3b80c02e027e)
|
||||
[members](https://trezor.io/support/general/hard-forks/) of the cryptocurrency
|
||||
ecosystem for supporting "hard fork"-style changes like our network upgrades.
|
||||
|
||||
One downside of the way replay protection is implemented in Zcash, is that there
|
||||
is no visible difference between a transaction being rejected by a `zcashd` node
|
||||
due to targeting a different branch, and being rejected due to an invalid
|
||||
signature. This has caused issues in the past when a user had not upgraded their
|
||||
wallet software, or when a wallet lacked support for the new network upgrade's
|
||||
consensus branch ID; the resulting error messages when users tried to create
|
||||
transactions were non-intuitive, and particularly cryptic for transparent
|
||||
transactions.
|
||||
|
||||
Starting from this release, `zcashd` nodes will re-verify invalid transparent
|
||||
and Sprout signatures against the consensus branch ID from before the most
|
||||
recent network upgrade. If the signature then becomes valid, the transaction
|
||||
will be rejected with the error message `old-consensus-branch-id`. This error
|
||||
can be handled specifically by wallet providers to inform the user that they
|
||||
need to upgrade their wallet software.
|
||||
|
||||
Wallet software can also automatically obtain the latest consensus branch ID
|
||||
from their (up-to-date) `zcashd` node, by calling `getblockchaininfo` and
|
||||
looking at `{'consensus': {'nextblock': BRANCH_ID, ...}, ...}` in the JSON
|
||||
output.
|
||||
|
||||
Expired transactions notifications
|
||||
----------------------------------
|
||||
|
||||
A new config option `-txexpirynotify` has been added that will cause `zcashd` to
|
||||
execute a command when a transaction in the mempool expires. This can be used to
|
||||
notify external systems about transaction expiry, similar to the existing
|
||||
`-blocknotify` config option that notifies when the chain tip changes.
|
||||
|
||||
RPC methods
|
||||
-----------
|
||||
|
||||
- The `z_importkey` and `z_importviewingkey` RPC methods now return the type of
|
||||
the imported spending or viewing key (`sprout` or `sapling`), and the
|
||||
corresponding payment address.
|
||||
|
||||
- Negative heights are now permitted in `getblock` and `getblockhash`, to select
|
||||
blocks backwards from the chain tip. A height of `-1` corresponds to the last
|
||||
known valid block on the main chain.
|
||||
|
||||
- A new RPC method `getexperimentalfeatures` returns the list of enabled
|
||||
experimental features.
|
||||
|
||||
Build system
|
||||
------------
|
||||
|
||||
|
|
|
@ -0,0 +1,615 @@
|
|||
Notable changes
|
||||
===============
|
||||
|
||||
Network Upgrade 3: Heartwood
|
||||
----------------------------
|
||||
|
||||
The code preparations for the Heartwood network upgrade are finished and
|
||||
included in this release. The following ZIPs are being deployed:
|
||||
|
||||
- [ZIP 213: Shielded Coinbase](https://zips.z.cash/zip-0213)
|
||||
- [ZIP 221: FlyClient - Consensus-Layer Changes](https://zips.z.cash/zip-0221)
|
||||
|
||||
Heartwood will activate on testnet at height XXXXXX, and can also be activated
|
||||
at a specific height in regtest mode by setting the config option
|
||||
`-nuparams=f5b9230b:HEIGHT`.
|
||||
|
||||
As a reminder, because the Heartwood activation height is not yet specified for
|
||||
mainnet, version 2.1.2 will behave similarly as other pre-Heartwood releases
|
||||
even after a future activation of Heartwood on the network. Upgrading from 2.1.2
|
||||
will be required in order to follow the Heartwood network upgrade on mainnet.
|
||||
|
||||
See [ZIP 250](https://zips.z.cash/zip-0250) for additional information about the
|
||||
deployment process for Heartwood.
|
||||
|
||||
### Mining to Sapling addresses
|
||||
|
||||
Miners and mining pools that wish to test the new "shielded coinbase" support on
|
||||
the Heartwood testnet can generate a new Sapling address with `z_getnewaddress`,
|
||||
add the config option `mineraddress=SAPLING_ADDRESS` to their `zcash.conf` file,
|
||||
and then restart their `zcashd` node. `getblocktemplate` will then return
|
||||
coinbase transactions containing a shielded miner output.
|
||||
|
||||
Note that `mineraddress` should only be set to a Sapling address after the
|
||||
Heartwood network upgrade has activated; setting a Sapling address prior to
|
||||
Heartwood activation will cause `getblocktemplate` to return block templates
|
||||
that cannot be mined.
|
||||
|
||||
Sapling viewing keys support
|
||||
----------------------------
|
||||
|
||||
Support for Sapling viewing keys (specifically, Sapling extended full viewing
|
||||
keys, as described in [ZIP 32](https://zips.z.cash/zip-0032)), has been added to
|
||||
the wallet. Nodes will track both sent and received transactions for any Sapling
|
||||
addresses associated with the imported Sapling viewing keys.
|
||||
|
||||
- Use the `z_exportviewingkey` RPC method to obtain the viewing key for a
|
||||
shielded address in a node's wallet. For Sapling addresses, these always begin
|
||||
with "zxviews" (or "zxviewtestsapling" for testnet addresses).
|
||||
|
||||
- Use `z_importviewingkey` to import a viewing key into another node. Imported
|
||||
Sapling viewing keys will be stored in the wallet, and remembered across
|
||||
restarts.
|
||||
|
||||
- `z_getbalance` will show the balance of a Sapling address associated with an
|
||||
imported Sapling viewing key. Balances for Sapling viewing keys will be
|
||||
included in the output of `z_gettotalbalance` when the `includeWatchonly`
|
||||
parameter is set to `true`.
|
||||
|
||||
- RPC methods for viewing shielded transaction information (such as
|
||||
`z_listreceivedbyaddress`) will return information for Sapling addresses
|
||||
associated with imported Sapling viewing keys.
|
||||
|
||||
Details about what information can be viewed with these Sapling viewing keys,
|
||||
and what guarantees you have about that information, can be found in
|
||||
[ZIP 310](https://zips.z.cash/zip-0310).
|
||||
|
||||
Removal of time adjustment and the -maxtimeadjustment= option
|
||||
-------------------------------------------------------------
|
||||
|
||||
Prior to v2.1.1-1, `zcashd` would adjust the local time that it used by up
|
||||
to 70 minutes, according to a median of the times sent by the first 200 peers
|
||||
to connect to it. This mechanism was inherently insecure, since an adversary
|
||||
making multiple connections to the node could effectively control its time
|
||||
within that +/- 70 minute window (this is called a "timejacking attack").
|
||||
|
||||
In the v2.1.1-1 security release, in addition to other mitigations for
|
||||
timejacking attacks, the maximum time adjustment was set to zero by default.
|
||||
This effectively disabled time adjustment; however, a `-maxtimeadjustment=`
|
||||
option was provided to override this default.
|
||||
|
||||
As a simplification the time adjustment code has now been completely removed,
|
||||
together with `-maxtimeadjustment=`. Node operators should instead ensure that
|
||||
their local time is set reasonably accurately.
|
||||
|
||||
If it appears that the node has a significantly different time than its peers,
|
||||
a warning will still be logged and indicated on the metrics screen if enabled.
|
||||
|
||||
View shielded information in wallet transactions
|
||||
------------------------------------------------
|
||||
|
||||
In previous `zcashd` versions, to obtain information about shielded transactions
|
||||
you would use either the `z_listreceivedbyaddress` RPC method (which returns all
|
||||
notes received by an address) or `z_listunspent` (which returns unspent notes,
|
||||
optionally filtered by addresses). There were no RPC methods that directly
|
||||
returned details about spends, or anything equivalent to the `gettransaction`
|
||||
method (which returns transparent information about in-wallet transactions).
|
||||
|
||||
This release introduces a new RPC method `z_viewtransaction` to fill that gap.
|
||||
Given the ID of a transaction in the wallet, it decrypts the transaction and
|
||||
returns detailed shielded information for all decryptable new and spent notes,
|
||||
including:
|
||||
|
||||
- The address that each note belongs to.
|
||||
- Values in both decimal ZEC and zatoshis.
|
||||
- The ID of the transaction that each spent note was received in.
|
||||
- An `outgoing` flag on each new note, which will be `true` if the output is not
|
||||
for an address in the wallet.
|
||||
- A `memoStr` field for each new note, containing its text memo (if its memo
|
||||
field contains a valid UTF-8 string).
|
||||
|
||||
Information will be shown for any address that appears in `z_listaddresses`;
|
||||
this includes watch-only addresses linked to viewing keys imported with
|
||||
`z_importviewingkey`, as well as addresses with spending keys (both generated
|
||||
with `z_getnewaddress` and imported with `z_importkey`).
|
||||
|
||||
Better error messages for rejected transactions after network upgrades
|
||||
----------------------------------------------------------------------
|
||||
|
||||
The Zcash network upgrade process includes several features designed to protect
|
||||
users. One of these is the "consensus branch ID", which prevents transactions
|
||||
created after a network upgrade has activated from being replayed on another
|
||||
chain (that might have occurred due to, for example, a
|
||||
[friendly fork](https://electriccoin.co/blog/future-friendly-fork/)). This is
|
||||
known as "two-way replay protection", and is a core requirement by
|
||||
[various](https://blog.bitgo.com/bitgos-approach-to-handling-a-hard-fork-71e572506d7d?gi=3b80c02e027e)
|
||||
[members](https://trezor.io/support/general/hard-forks/) of the cryptocurrency
|
||||
ecosystem for supporting "hard fork"-style changes like our network upgrades.
|
||||
|
||||
One downside of the way replay protection is implemented in Zcash, is that there
|
||||
is no visible difference between a transaction being rejected by a `zcashd` node
|
||||
due to targeting a different branch, and being rejected due to an invalid
|
||||
signature. This has caused issues in the past when a user had not upgraded their
|
||||
wallet software, or when a wallet lacked support for the new network upgrade's
|
||||
consensus branch ID; the resulting error messages when users tried to create
|
||||
transactions were non-intuitive, and particularly cryptic for transparent
|
||||
transactions.
|
||||
|
||||
Starting from this release, `zcashd` nodes will re-verify invalid transparent
|
||||
and Sprout signatures against the consensus branch ID from before the most
|
||||
recent network upgrade. If the signature then becomes valid, the transaction
|
||||
will be rejected with the error message `old-consensus-branch-id`. This error
|
||||
can be handled specifically by wallet providers to inform the user that they
|
||||
need to upgrade their wallet software.
|
||||
|
||||
Wallet software can also automatically obtain the latest consensus branch ID
|
||||
from their (up-to-date) `zcashd` node, by calling `getblockchaininfo` and
|
||||
looking at `{'consensus': {'nextblock': BRANCH_ID, ...}, ...}` in the JSON
|
||||
output.
|
||||
|
||||
Expired transactions notifications
|
||||
----------------------------------
|
||||
|
||||
A new config option `-txexpirynotify` has been added that will cause `zcashd` to
|
||||
execute a command when a transaction in the mempool expires. This can be used to
|
||||
notify external systems about transaction expiry, similar to the existing
|
||||
`-blocknotify` config option that notifies when the chain tip changes.
|
||||
|
||||
RPC methods
|
||||
-----------
|
||||
|
||||
- The `z_importkey` and `z_importviewingkey` RPC methods now return the type of
|
||||
the imported spending or viewing key (`sprout` or `sapling`), and the
|
||||
corresponding payment address.
|
||||
|
||||
- Negative heights are now permitted in `getblock` and `getblockhash`, to select
|
||||
blocks backwards from the chain tip. A height of `-1` corresponds to the last
|
||||
known valid block on the main chain.
|
||||
|
||||
- A new RPC method `getexperimentalfeatures` returns the list of enabled
|
||||
experimental features.
|
||||
|
||||
Build system
|
||||
------------
|
||||
|
||||
- The `--enable-lcov`, `--disable-tests`, and `--disable-mining` flags for
|
||||
`zcutil/build.sh` have been removed. You can pass these flags instead by using
|
||||
the `CONFIGURE_FLAGS` environment variable. For example, to enable coverage
|
||||
instrumentation (thus enabling "make cov" to work), call:
|
||||
|
||||
```
|
||||
CONFIGURE_FLAGS="--enable-lcov --disable-hardening" ./zcutil/build.sh
|
||||
```
|
||||
|
||||
- The build system no longer defaults to verbose output. You can re-enable
|
||||
verbose output with `./zcutil/build.sh V=1`
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Alfredo Garcia (40):
|
||||
remove SignatureHash from python rpc tests
|
||||
add negative height to getblock
|
||||
allow negative index to getblockhash
|
||||
update docs
|
||||
add additional tests to rpc_wallet_z_getnewaddress
|
||||
change convention
|
||||
change regex
|
||||
Return address and type of imported key in z_importkey
|
||||
Delete travis file
|
||||
dedup decode keys and addresses
|
||||
remove unused imports
|
||||
add txexpirynotify
|
||||
fix rpx_wallet_tests
|
||||
remove debug noise from 2 gtests
|
||||
make type and size a pair in DecodeAny arguments
|
||||
add missing calls to DecodeAny
|
||||
add destination wrappers
|
||||
change tuples to classes
|
||||
change cm() to cmu() in SaplingNote class
|
||||
change the cm member of OutputDescription to cmu
|
||||
change maybe_cm to maybe_cmu
|
||||
add getexperimentalfeatures rpc call
|
||||
refactor experimental features
|
||||
make fInsightExplorer a local
|
||||
add check_node_log utility function
|
||||
remove space after new line
|
||||
move check_node_log framework test to a new file
|
||||
use check_node_log in turnstile.py
|
||||
add stop_node argument to check_node_log, use it in shieldingcoinbase
|
||||
change constructors
|
||||
minor comment fix
|
||||
preserve test semantics
|
||||
remove unused import
|
||||
multiple debug categories documentation
|
||||
return address info in z_importviewingkey
|
||||
add expected address check to tests
|
||||
change unclear wording in z_import calls address returned
|
||||
Lock with cs_main inside gtests that call chainActive.Height()
|
||||
add -lightwalletd experimental option
|
||||
compute more structures in mempool DynamicMemoryUsage
|
||||
|
||||
Carl Dong (1):
|
||||
autoconf: Sane --enable-debug defaults.
|
||||
|
||||
Chun Kuan Lee (1):
|
||||
Reset default -g -O2 flags when enable debug
|
||||
|
||||
Cory Fields (3):
|
||||
bench: switch to std::chrono for time measurements
|
||||
bench: prefer a steady clock if the resolution is no worse
|
||||
build: Split hardening/fPIE options out
|
||||
|
||||
Dagur Valberg Johannsson (1):
|
||||
Improve z_getnewaddress tests
|
||||
|
||||
Daira Hopwood (24):
|
||||
Add missing cases for Blossom in ContextualCheckBlock tests.
|
||||
Revert "Add -maxtimeadjustment with default of 0 instead of the 4200 seconds used in Bitcoin Core."
|
||||
Remove uses of GetTimeOffset().
|
||||
Replace time adjustment with warning only.
|
||||
Update GetAdjustedTime() to GetTime().
|
||||
Sort entries in zcash_gtest_SOURCES (other than test_tautology which is deliberately first).
|
||||
Add release notes for removal of -maxtimeadjustment.
|
||||
Resolve a race condition on `chainActive.Tip()` in initialization (introduced in #4379).
|
||||
Setting a std::atomic variable in a signal handler only has defined behaviour if it is lock-free.
|
||||
Add comment to `MilliSleep` documenting that it is an interruption point.
|
||||
Exit init early if we request shutdown before having loaded the genesis block.
|
||||
Fix typos/minor errors in comments, and wrap some lines.
|
||||
Avoid a theoretical possibility of division-by-zero introduced in #4368.
|
||||
Make the memo a mandatory argument for SendManyRecipient
|
||||
Add a `zcutil/clean.sh` script that works (unlike `make clean`).
|
||||
Split into clean.sh and distclean.sh.
|
||||
Minor refactoring.
|
||||
Executables end with .exe on Windows.
|
||||
Avoid spurious error messages when cleaning up directories.
|
||||
Address review comments.
|
||||
Use `SA_RESTART` in `sa_flags` when setting up signal handlers.
|
||||
Remove a redundant `rm -f` command.
|
||||
Refer to altitude instead of height for history tree peaks
|
||||
Address review comments: `target` and `depends/work` should be cleaned by clean.sh.
|
||||
|
||||
Dimitris Apostolou (8):
|
||||
Fix Boost compilation on macOS
|
||||
Remove libsnark preprocessor flags
|
||||
Fix typo
|
||||
End diff with LF character
|
||||
Remove stale comment
|
||||
Point at support community on Discord
|
||||
Update documentation info
|
||||
Fix typos
|
||||
|
||||
Eirik Ogilvie-Wigley (2):
|
||||
Include shielded transaction data when calculating RecursiveDynamicUsage of transactions
|
||||
Account for malloc overhead
|
||||
|
||||
Evan Klitzke (2):
|
||||
Add --with-sanitizers option to configure
|
||||
Make --enable-debug to pick better options
|
||||
|
||||
Gavin Andresen (2):
|
||||
Simple benchmarking framework
|
||||
Support very-fast-running benchmarks
|
||||
|
||||
Gregory Maxwell (4):
|
||||
Avoid integer division in the benchmark inner-most loop.
|
||||
Move GetWarnings and related globals to util.
|
||||
Eliminate data races for strMiscWarning and fLargeWork*Found.
|
||||
Move GetWarnings() into its own file.
|
||||
|
||||
Jack Grigg (94):
|
||||
Revert "Add configure flags for enabling ASan/UBSan and TSan"
|
||||
configure: Re-introduce additional sanitizer flags
|
||||
RPC: z_viewtransaction
|
||||
depends: Add utfcpp to dependencies
|
||||
RPC: Display valid UTF-8 memos in z_viewtransaction
|
||||
RPC: Use OutgoingViewingKeys to recover non-wallet Sapling outputs
|
||||
test: Check z_viewtransaction output in wallet_listreceived RPC test
|
||||
Benchmark Zcash verification operations
|
||||
Simulate worst-case block verification
|
||||
zcutil/build.sh: Remove lcov and mining flags
|
||||
configure: Change default Proton to match build.sh
|
||||
zcutil/build.sh: Turn off verbosity by default
|
||||
Make -fwrapv conditional on --enable-debug=no
|
||||
Move default -g flag into configure.ac behind --enable-debug=no
|
||||
Add build system changes to release notes
|
||||
test: Hard-code hex memo in wallet_listreceived for Python3 compatibility
|
||||
test: Fix pyflakes warnings
|
||||
bench: "Use" result of crypto_sign_verify_detached
|
||||
Add test vectors for small-order Ed25519 pubkeys
|
||||
Patch libsodium 1.0.15 pubkey validation onto 1.0.18
|
||||
Patch libsodium 1.0.15 signature validation onto 1.0.18
|
||||
Add release notes for z_viewtransaction
|
||||
Deduplicate some wallet keystore logic
|
||||
Move Sprout and Sapling address logic into separate files
|
||||
Move ZIP 32 classes inside zcash/Address.hpp
|
||||
SaplingFullViewingKey -> SaplingExtendedFullViewingKey in keystore maps
|
||||
Remove default address parameter from Sapling keystore methods
|
||||
test: Add test for CBasicKeyStore::AddSaplingFullViewingKey
|
||||
Add encoding and decoding for Sapling extended full viewing keys
|
||||
Add Sapling ExtFVK support to z_exportviewingkey
|
||||
Add in-memory Sapling ExtFVK support to z_importviewingkey
|
||||
Store imported Sapling ExtFVKs in wallet database
|
||||
OutputDescriptionInfo::Build()
|
||||
ZIP 213 consensus rules
|
||||
Add support for Sapling addresses in -mineraddress
|
||||
wallet: Include coinbase txs in Sapling note selection
|
||||
Add regtest-only -nurejectoldversions option
|
||||
test: Minor tweaks to comments in LibsodiumPubkeyValidation
|
||||
test: RPC test for shielded coinbase
|
||||
Adjust comments on ZIP 213 logic
|
||||
Use DoS level constants and parameters for ZIP 213 rejections
|
||||
test: Check that shielded coinbase can be spent to a t-address
|
||||
init: Inform on error that -mineraddress must be Sapling or transparent
|
||||
test: Explicitly check Sapling consensus rules apply to shielded coinbase
|
||||
Migrate GitHub issue template to new format
|
||||
Add GitHub issue templates for feature requests and UX reports
|
||||
depends: Remove comments from libsodium signature validation patch
|
||||
Bring in librustzcash crate
|
||||
Bring in Cargo.lock from librustzcash repo
|
||||
rust: Pin toolchain to 1.36.0, matching depends system
|
||||
rust: Adjust Cargo.toml so that it compiles
|
||||
Update .gitignore for Rust code
|
||||
Replace librustzcash from depends system with src/rust
|
||||
Move root of Rust crate into repo root
|
||||
depends: Remove unused vendored crates
|
||||
Fix Rust static library linking for Windows builds
|
||||
test: Rename FakeCoinsViewDB -> ValidationFakeCoinsViewDB
|
||||
test: Modify ValidationFakeCoinsViewDB to optionally contain a coin
|
||||
test: Add missing parameter selection to Validation.ReceivedBlockTransactions
|
||||
mempool: Check transparent signatures against the previous network upgrade
|
||||
mempool: Remove duplicate consensusBranchId from AcceptToMemoryPool
|
||||
test: Add Overwinter and Sapling support to GetValidTransaction() helper
|
||||
consensus: Check JoinSplit signatures against the previous network upgrade
|
||||
depends: Use Rust 1.42.0 toolchain
|
||||
Bring in updates to librustzcash crate
|
||||
depends: Define Rust target in a single location
|
||||
depends: Hard-code Rust target for all Darwin hosts
|
||||
Add ZIP 221 logic to block index
|
||||
Add ZIP 221 support to miner and getblocktemplate
|
||||
Implement ZIP 221 consensus rules
|
||||
Return the correct root from librustzcash_mmr_{append, delete}
|
||||
Use a C array for HistoryEntry instead of std::array
|
||||
test: Verify ZIP 221 logic against reference implementation
|
||||
build: Move cargo arguments into RUST_BUILD_OPTS
|
||||
build: Correctly remove generated files from .cargo
|
||||
test: Build Rust tests as part of qa/zcash/full_test_suite.py
|
||||
build: Connect cargo verbosity to make verbosity
|
||||
test: Assert that GetValidTransaction supports the given branch ID
|
||||
Comment tweaks and cleanups
|
||||
test: Add an extra assertion to feature_zip221.py
|
||||
Remove unnecessary else case in CCoinsViewCache::PreloadHistoryTree
|
||||
Improve documentation of CCoinsViewCache::PreloadHistoryTree
|
||||
Truncate HistoryCache.appends correctly for zero-indexed entries
|
||||
Comment clarifications and fixes
|
||||
Make peak_pos zero-indexed in CCoinsViewCache::PreloadHistoryTree
|
||||
test: Ignore timestamps in addressindex checks
|
||||
test: Add a second Sapling note to WalletTests.ClearNoteWitnessCache
|
||||
test: Run Equihash test vectors on both C++ and Rust validators
|
||||
Pass the block height through to CheckEquihashSolution()
|
||||
consensus: From Heartwood activation, use Rust Equihash validator
|
||||
zcutil/make-release.py: Fix to run with Python 3
|
||||
zcutil/make-release.py: Check for release dependencies
|
||||
Update release notes for v2.1.2
|
||||
zcutil/release-notes.py: Add Python 3 execution header
|
||||
|
||||
James O'Beirne (1):
|
||||
Add basic coverage reporting for RPC tests
|
||||
|
||||
Jeremy Rubin (3):
|
||||
Add Basic CheckQueue Benchmark
|
||||
Address ryanofsky feedback on CCheckQueue benchmarks. Eliminated magic numbers, fixed scoping of vectors (and memory movement component of benchmark).
|
||||
Add prevector destructor benchmark
|
||||
|
||||
Karl-Johan Alm (1):
|
||||
Refactoring: Removed using namespace <xxx> from bench/ and test/ source files.
|
||||
|
||||
Larry Ruane (2):
|
||||
zcutil/fetch-params.sh unneeded --testnet arg should warn user
|
||||
util: CBufferedFile fixes
|
||||
|
||||
LitecoinZ (1):
|
||||
Fix issue #3772
|
||||
|
||||
Marshall Gaucher (1):
|
||||
Update qa/rpc-tests/addressindex.py
|
||||
|
||||
Matt Corallo (2):
|
||||
Remove countMaskInv caching in bench framework
|
||||
Require a steady clock for bench with at least micro precision
|
||||
|
||||
MeshCollider (3):
|
||||
Fix race for mapBlockIndex in AppInitMain
|
||||
Make fReindex atomic to avoid race
|
||||
Consistent parameter names in txdb.h
|
||||
|
||||
NicolasDorier (1):
|
||||
[qa] assert_start_raises_init_error
|
||||
|
||||
NikVolf (3):
|
||||
push/pop history with tests
|
||||
update chain history in ConnectBlock and DisconnectBlock
|
||||
use iterative platform-independent log2i
|
||||
|
||||
Patrick Strateman (1):
|
||||
Acquire lock to check for genesis block.
|
||||
|
||||
Pavel Janík (3):
|
||||
Rewrite help texts for features enabled by default.
|
||||
Ignore bench_bitcoin binary.
|
||||
Prevent warning: variable 'x' is uninitialized
|
||||
|
||||
Philip Kaufmann (1):
|
||||
[Trivial] ensure minimal header conventions
|
||||
|
||||
Pieter Wuille (3):
|
||||
Benchmark rolling bloom filter
|
||||
Introduce FastRandomContext::randbool()
|
||||
FastRandom benchmark
|
||||
|
||||
Sean Bowe (9):
|
||||
Initialize ThreadNotifyWallets before additional blocks are imported.
|
||||
Handle case of fresh wallets in ThreadNotifyWallets.
|
||||
Clarify comment
|
||||
Add librustzcash tests to the full test suite.
|
||||
Add release profile optimizations and turn off panic unwinding in librustzcash.
|
||||
Minor typo fixes.
|
||||
Simplification for MacOS in rust-test.
|
||||
make-release.py: Versioning changes for 2.1.2-rc1.
|
||||
make-release.py: Updated manpages for 2.1.2-rc1.
|
||||
|
||||
Taylor Hornby (15):
|
||||
Make the equihash validator macro set its output to false when throwing an exception.
|
||||
Add test for unused bits in the Equihash solution encoding.
|
||||
Add Python script for checking if dependencies have updates.
|
||||
Add GitHub API credential
|
||||
Update list of dependencies to check
|
||||
Wrap long lines
|
||||
Cache releases to reduce network usage and improve performance
|
||||
Make updatecheck.py compatible with python2
|
||||
Have make clean delete temporary AFL build directory
|
||||
Add AFL build directory to .gitignore
|
||||
Have make clean delete AFL output directories.
|
||||
Fix bug in updatecheck.py and add utfcpp to its dependency list
|
||||
Fix typo in updatecheck.py
|
||||
Update updatecheck.py with the new Rust dependencies and improve the error message in case the untracked dependency list becomes out of date.
|
||||
Fix undefined behavior in CScriptNum
|
||||
|
||||
Wladimir J. van der Laan (7):
|
||||
bench: Add crypto hash benchmarks
|
||||
Kill insecure_random and associated global state
|
||||
bench: Fix subtle counting issue when rescaling iteration count
|
||||
bench: Add support for measuring CPU cycles
|
||||
bench: Fix initialization order in registration
|
||||
util: Don't set strMiscWarning on every exception
|
||||
test_framework: detect failure of bitcoind startup
|
||||
|
||||
Yuri Zhykin (1):
|
||||
bench: Added base58 encoding/decoding benchmarks
|
||||
|
||||
avnish (14):
|
||||
changed block_test to BlockTests
|
||||
changed test names from _ to CamelCase
|
||||
changed header_size_is_expected to HeaderSizeIsExpected
|
||||
changed "equihash_tests" to EquihashTests
|
||||
changed founders_reward_test to FoundersRewardTest
|
||||
changes tests to camelcase
|
||||
chnged keystore_tests to KeystoreTests
|
||||
changed libzcash_utils to LibzcashUtils
|
||||
changed test to CamelCase
|
||||
changed test to CamelCase
|
||||
changed test to CamelCase
|
||||
changed seven_eq_seven to SevenEqSeven
|
||||
changed txid_tests to TxidTests
|
||||
changed wallet_zkeys_test to WalletZkeysTest
|
||||
|
||||
avnish98 (1):
|
||||
requested changes are rectified
|
||||
|
||||
ca333 (2):
|
||||
update libsodium to v1.0.18
|
||||
fix dead openssl download path
|
||||
|
||||
gladcow (4):
|
||||
Show reindex state in metrics
|
||||
Use processed file size as progress in metrics during reindex
|
||||
Byte sizes format
|
||||
Move reindex progress globals to metrics.h/cpp
|
||||
|
||||
Marshall Gaucher (74):
|
||||
update /usr/bin/env; fix print conventions
|
||||
update test_framework modules
|
||||
Update rpc-test/test_framework to Py3 convention,modules,encoding
|
||||
Update ignored testScriptsExt to Python3
|
||||
Update python3 env path, remove python 2.7 assert
|
||||
Update hexlify for encoding, update to py3 io module
|
||||
Update py3 env path, remove py2 assert
|
||||
Update py2 conventions to py3, remove py2 env and assert
|
||||
Update py2 conventions to py3, update Decimal calls
|
||||
Update py2 env path, remove py2 assert
|
||||
Update py2 env path, remove py2 assert
|
||||
Update py2 env path, remove py2 assert, update filter to return list for py3
|
||||
Update py2 env path, remove py2 assert, update http module and assert encoding
|
||||
Update cmp to py3 functions, update map return to list for py3
|
||||
Standard py2 to py3 updates
|
||||
Update py2 modules to py3, update encoding to be py3 compatible
|
||||
Update to py3 conventions, update decimal calls to be consistent
|
||||
Update to py3 conventions, update filter to return list
|
||||
update to py3 conventions, update range to return list for py3
|
||||
update to py3 convention, update execfile to py3 call
|
||||
update to py3 conventions, update cmp to be py3 compatible, update map to return list for py3
|
||||
update to py3 conventions, preserve ipv6 patch
|
||||
update str cast to prevent address assert issues
|
||||
clean up binascii call
|
||||
Add keyerror execption
|
||||
update to py3 env path
|
||||
update to py3 conventions, update functions to be upstream consistent
|
||||
update to py3 conventions, clean up code to be upstream consistent
|
||||
update to py3 encodings
|
||||
update encoding, decoding, serialize funcs for py3
|
||||
Update type to be decimal
|
||||
update to py3 conventions, BUG with last assert_equal
|
||||
Update io modules for py3, ISSUE with create_transaction function
|
||||
Update to py3, ISSUE with encoding
|
||||
Update to py3, ISSUE with encoding
|
||||
Update to py3, ISSUE with encoding in create_block
|
||||
Update to py3, ISSUE with encoding in create_block
|
||||
Clean up code not needed from upstream
|
||||
update io module, fix py3 division, and string encoding
|
||||
update remaining encoding issues, add pyblake2
|
||||
Use more meaningful assert_equal from our original codebase
|
||||
Clean up code from upstream we dont use
|
||||
fix except bug for undefined url
|
||||
Remove semi colons
|
||||
make import urlparse module consistent,httplib update to py3
|
||||
correct update to python3
|
||||
clean-up imports, keep string notation consistent, remove spacing
|
||||
clean up
|
||||
Use upstream encoding for encodeDecimal
|
||||
fix type issue
|
||||
fix initialize statements for imports
|
||||
clean up initiliaze statements from imports
|
||||
update type for decimal 0
|
||||
remove debug lines from prior commits
|
||||
clean up to minimize diff
|
||||
remove u encoding
|
||||
Fix decimal 0 issues
|
||||
Clean up import calls
|
||||
clean up
|
||||
clean up
|
||||
clean up
|
||||
fix url and port issue
|
||||
cleanups and fixing odd casting
|
||||
Update json to simplejson to remove unicode and str issue from py2 to py3
|
||||
Update py3 division
|
||||
fix pyflakes errors
|
||||
clean up conventions and whitespace
|
||||
fix string pattern issue on byte object
|
||||
update comment regarding prior py2 exception
|
||||
Fix remaining python3 conventions
|
||||
Update remaining Python3 conventions
|
||||
Updating remaining python3 conventions
|
||||
Update #! env for python3
|
||||
Update RPCs to support cross platform paths and libs
|
||||
|
||||
murrayn (1):
|
||||
Add build support for 'gprof' profiling.
|
||||
|
||||
practicalswift (8):
|
||||
build: Show enabled sanitizers in configure output
|
||||
Add -ftrapv to DEBUG_CXXFLAGS when --enable-debug is used
|
||||
Assert that what might look like a possible division by zero is actually unreachable
|
||||
Replace boost::function with std::function (C++11)
|
||||
Avoid static analyzer warnings regarding uninitialized arguments
|
||||
Restore default format state of cout after printing with std::fixed/setprecision
|
||||
Initialize recently introduced non-static class member lastCycles to zero in constructor
|
||||
Replace boost::function with std::function (C++11)
|
||||
|
||||
ptschip (1):
|
||||
Enable python tests for Native Windows
|
||||
|
||||
zancas (3):
|
||||
update comment, to correctly specify number of methods injected
|
||||
replace "virtual" with "override" in subclasses
|
||||
Remove remaining instances of boost::function
|
||||
|
|
@ -84,6 +84,7 @@ testScripts=(
|
|||
'mining_shielded_coinbase.py'
|
||||
'framework.py'
|
||||
'sapling_rewind_check.py'
|
||||
'feature_zip221.py'
|
||||
);
|
||||
testScriptsExt=(
|
||||
'getblocktemplate_longpoll.py'
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2020 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.flyclient import (ZcashMMRNode, append, delete, make_root_commitment)
|
||||
from test_framework.mininode import (HEARTWOOD_BRANCH_ID, CBlockHeader)
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
bytes_to_hex_str,
|
||||
hex_str_to_bytes,
|
||||
initialize_chain_clean,
|
||||
start_nodes,
|
||||
)
|
||||
|
||||
from io import BytesIO
|
||||
|
||||
NULL_FIELD = "00" * 32
|
||||
CHAIN_HISTORY_ROOT_VERSION = 2010200
|
||||
|
||||
# Verify block header field 'hashLightClientRoot' is set correctly for Heartwood blocks.
|
||||
class Zip221Test(BitcoinTestFramework):
|
||||
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
initialize_chain_clean(self.options.tmpdir, 4)
|
||||
|
||||
def setup_nodes(self):
|
||||
return start_nodes(4, self.options.tmpdir, extra_args=[[
|
||||
'-nuparams=2bb40e60:1', # Blossom
|
||||
'-nuparams=f5b9230b:10', # Heartwood
|
||||
'-nurejectoldversions=false',
|
||||
]] * 4)
|
||||
|
||||
def node_for_block(self, height):
|
||||
block_header = CBlockHeader()
|
||||
block_header.deserialize(BytesIO(hex_str_to_bytes(
|
||||
self.nodes[0].getblock(str(height), 0))))
|
||||
sapling_root = hex_str_to_bytes(
|
||||
self.nodes[0].getblock(str(height))["finalsaplingroot"])[::-1]
|
||||
return ZcashMMRNode.from_block(
|
||||
block_header, height, sapling_root, 0, HEARTWOOD_BRANCH_ID)
|
||||
|
||||
def run_test(self):
|
||||
self.nodes[0].generate(10)
|
||||
self.sync_all()
|
||||
|
||||
# Verify all blocks up to and including Heartwood activation set
|
||||
# hashChainHistoryRoot to null.
|
||||
print("Verifying blocks up to and including Heartwood activation")
|
||||
blockcount = self.nodes[0].getblockcount()
|
||||
assert_equal(blockcount, 10)
|
||||
for height in range(0, blockcount + 1):
|
||||
blk = self.nodes[0].getblock(str(height))
|
||||
assert_equal(blk["chainhistoryroot"], NULL_FIELD)
|
||||
|
||||
|
||||
# Create the initial history tree, containing a single node.
|
||||
root = self.node_for_block(10)
|
||||
|
||||
# Generate the first block that contains a non-null chain history root.
|
||||
print("Verifying first non-null chain history root")
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
# Verify that hashChainHistoryRoot is set correctly.
|
||||
assert_equal(
|
||||
self.nodes[0].getblock('11')["chainhistoryroot"],
|
||||
bytes_to_hex_str(make_root_commitment(root)[::-1]))
|
||||
|
||||
# Generate 9 more blocks on node 0, and verify their chain history roots.
|
||||
print("Mining 9 blocks on node 0")
|
||||
self.nodes[0].generate(9)
|
||||
self.sync_all()
|
||||
|
||||
print("Verifying node 0's chain history")
|
||||
for height in range(12, 21):
|
||||
leaf = self.node_for_block(height - 1)
|
||||
root = append(root, leaf)
|
||||
assert_equal(
|
||||
self.nodes[0].getblock(str(height))["chainhistoryroot"],
|
||||
bytes_to_hex_str(make_root_commitment(root)[::-1]))
|
||||
|
||||
# The rest of the test only applies to Heartwood-aware node versions.
|
||||
# Earlier versions won't serialize chain history roots in the block
|
||||
# index, and splitting the network below requires restarting the nodes.
|
||||
if self.nodes[0].getnetworkinfo()["version"] < CHAIN_HISTORY_ROOT_VERSION:
|
||||
print("Node's block index is not Heartwood-aware, skipping reorg test")
|
||||
return
|
||||
|
||||
# Split the network so we can test the effect of a reorg.
|
||||
print("Splitting the network")
|
||||
self.split_network()
|
||||
|
||||
# Generate 10 more blocks on node 0, and verify their chain history roots.
|
||||
print("Mining 10 more blocks on node 0")
|
||||
self.nodes[0].generate(10)
|
||||
self.sync_all()
|
||||
|
||||
print("Verifying node 0's chain history")
|
||||
for height in range(21, 31):
|
||||
leaf = self.node_for_block(height - 1)
|
||||
root = append(root, leaf)
|
||||
assert_equal(
|
||||
self.nodes[0].getblock(str(height))["chainhistoryroot"],
|
||||
bytes_to_hex_str(make_root_commitment(root)[::-1]))
|
||||
|
||||
# Generate 11 blocks on node 2.
|
||||
print("Mining alternate chain on node 2")
|
||||
self.nodes[2].generate(11)
|
||||
self.sync_all()
|
||||
|
||||
# Reconnect the nodes; node 0 will re-org to node 2's chain.
|
||||
print("Re-joining the network so that node 0 reorgs")
|
||||
self.join_network()
|
||||
|
||||
# Verify that node 0's chain history was correctly updated.
|
||||
print("Deleting orphaned blocks from the expected chain history")
|
||||
for _ in range(10):
|
||||
root = delete(root)
|
||||
|
||||
print("Verifying that node 0 is now on node 1's chain history")
|
||||
for height in range(21, 32):
|
||||
leaf = self.node_for_block(height - 1)
|
||||
root = append(root, leaf)
|
||||
assert_equal(
|
||||
self.nodes[2].getblock(str(height))["chainhistoryroot"],
|
||||
bytes_to_hex_str(make_root_commitment(root)[::-1]))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
Zip221Test().main()
|
|
@ -0,0 +1,176 @@
|
|||
from pyblake2 import blake2b
|
||||
import struct
|
||||
from typing import (List, Optional)
|
||||
|
||||
from .mininode import (CBlockHeader, block_work_from_compact, ser_compactsize, ser_uint256)
|
||||
|
||||
def H(msg: bytes, consensusBranchId: int) -> bytes:
|
||||
digest = blake2b(
|
||||
digest_size=32,
|
||||
person=b'ZcashHistory' + struct.pack("<I", consensusBranchId))
|
||||
digest.update(msg)
|
||||
return digest.digest()
|
||||
|
||||
class ZcashMMRNode():
|
||||
# leaf nodes have no children
|
||||
left_child: Optional['ZcashMMRNode']
|
||||
right_child: Optional['ZcashMMRNode']
|
||||
|
||||
# commitments
|
||||
hashSubtreeCommitment: bytes
|
||||
nEarliestTimestamp: int
|
||||
nLatestTimestamp: int
|
||||
nEarliestTargetBits: int
|
||||
nLatestTargetBits: int
|
||||
hashEarliestSaplingRoot: bytes # left child's sapling root
|
||||
hashLatestSaplingRoot: bytes # right child's sapling root
|
||||
nSubTreeTotalWork: int # total difficulty accumulated within each subtree
|
||||
nEarliestHeight: int
|
||||
nLatestHeight: int
|
||||
nSaplingTxCount: int # number of Sapling transactions in block
|
||||
|
||||
consensusBranchId: bytes
|
||||
|
||||
@classmethod
|
||||
def from_block(Z, block: CBlockHeader, height, sapling_root, sapling_tx_count, consensusBranchId) -> 'ZcashMMRNode':
|
||||
'''Create a leaf node from a block'''
|
||||
node = Z()
|
||||
node.left_child = None
|
||||
node.right_child = None
|
||||
node.hashSubtreeCommitment = ser_uint256(block.rehash())
|
||||
node.nEarliestTimestamp = block.nTime
|
||||
node.nLatestTimestamp = block.nTime
|
||||
node.nEarliestTargetBits = block.nBits
|
||||
node.nLatestTargetBits = block.nBits
|
||||
node.hashEarliestSaplingRoot = sapling_root
|
||||
node.hashLatestSaplingRoot = sapling_root
|
||||
node.nSubTreeTotalWork = block_work_from_compact(block.nBits)
|
||||
node.nEarliestHeight = height
|
||||
node.nLatestHeight = height
|
||||
node.nSaplingTxCount = sapling_tx_count
|
||||
node.consensusBranchId = consensusBranchId
|
||||
return node
|
||||
|
||||
def serialize(self) -> bytes:
|
||||
'''serializes a node'''
|
||||
buf = b''
|
||||
buf += self.hashSubtreeCommitment
|
||||
buf += struct.pack("<I", self.nEarliestTimestamp)
|
||||
buf += struct.pack("<I", self.nLatestTimestamp)
|
||||
buf += struct.pack("<I", self.nEarliestTargetBits)
|
||||
buf += struct.pack("<I", self.nLatestTargetBits)
|
||||
buf += self.hashEarliestSaplingRoot
|
||||
buf += self.hashLatestSaplingRoot
|
||||
buf += ser_uint256(self.nSubTreeTotalWork)
|
||||
buf += ser_compactsize(self.nEarliestHeight)
|
||||
buf += ser_compactsize(self.nLatestHeight)
|
||||
buf += ser_compactsize(self.nSaplingTxCount)
|
||||
return buf
|
||||
|
||||
def make_parent(
|
||||
left_child: ZcashMMRNode,
|
||||
right_child: ZcashMMRNode) -> ZcashMMRNode:
|
||||
parent = ZcashMMRNode()
|
||||
parent.left_child = left_child
|
||||
parent.right_child = right_child
|
||||
parent.hashSubtreeCommitment = H(
|
||||
left_child.serialize() + right_child.serialize(),
|
||||
left_child.consensusBranchId,
|
||||
)
|
||||
parent.nEarliestTimestamp = left_child.nEarliestTimestamp
|
||||
parent.nLatestTimestamp = right_child.nLatestTimestamp
|
||||
parent.nEarliestTargetBits = left_child.nEarliestTargetBits
|
||||
parent.nLatestTargetBits = right_child.nLatestTargetBits
|
||||
parent.hashEarliestSaplingRoot = left_child.hashEarliestSaplingRoot
|
||||
parent.hashLatestSaplingRoot = right_child.hashLatestSaplingRoot
|
||||
parent.nSubTreeTotalWork = left_child.nSubTreeTotalWork + right_child.nSubTreeTotalWork
|
||||
parent.nEarliestHeight = left_child.nEarliestHeight
|
||||
parent.nLatestHeight = right_child.nLatestHeight
|
||||
parent.nSaplingTxCount = left_child.nSaplingTxCount + right_child.nSaplingTxCount
|
||||
parent.consensusBranchId = left_child.consensusBranchId
|
||||
return parent
|
||||
|
||||
def make_root_commitment(root: ZcashMMRNode) -> bytes:
|
||||
'''Makes the root commitment for a blockheader'''
|
||||
return H(root.serialize(), root.consensusBranchId)
|
||||
|
||||
def get_peaks(node: ZcashMMRNode) -> List[ZcashMMRNode]:
|
||||
peaks: List[ZcashMMRNode] = []
|
||||
|
||||
# Get number of leaves.
|
||||
leaves = node.nLatestHeight - (node.nEarliestHeight - 1)
|
||||
assert(leaves > 0)
|
||||
|
||||
# Check if the number of leaves in this subtree is a power of two.
|
||||
if (leaves & (leaves - 1)) == 0:
|
||||
# This subtree is full, and therefore a single peak. This also covers
|
||||
# the case of a single isolated leaf.
|
||||
peaks.append(node)
|
||||
else:
|
||||
# This is one of the generated nodes; search within its children.
|
||||
peaks.extend(get_peaks(node.left_child))
|
||||
peaks.extend(get_peaks(node.right_child))
|
||||
|
||||
return peaks
|
||||
|
||||
|
||||
def bag_peaks(peaks: List[ZcashMMRNode]) -> ZcashMMRNode:
|
||||
'''
|
||||
"Bag" a list of peaks, and return the final root
|
||||
'''
|
||||
root = peaks[0]
|
||||
for i in range(1, len(peaks)):
|
||||
root = make_parent(root, peaks[i])
|
||||
return root
|
||||
|
||||
|
||||
def append(root: ZcashMMRNode, leaf: ZcashMMRNode) -> ZcashMMRNode:
|
||||
'''Append a leaf to an existing tree, return the new tree root'''
|
||||
# recursively find a list of peaks in the current tree
|
||||
peaks: List[ZcashMMRNode] = get_peaks(root)
|
||||
merged: List[ZcashMMRNode] = []
|
||||
|
||||
# Merge peaks from right to left.
|
||||
# This will produce a list of peaks in reverse order
|
||||
current = leaf
|
||||
for peak in peaks[::-1]:
|
||||
current_leaves = current.nLatestHeight - (current.nEarliestHeight - 1)
|
||||
peak_leaves = peak.nLatestHeight - (peak.nEarliestHeight - 1)
|
||||
|
||||
if current_leaves == peak_leaves:
|
||||
current = make_parent(peak, current)
|
||||
else:
|
||||
merged.append(current)
|
||||
current = peak
|
||||
merged.append(current)
|
||||
|
||||
# finally, bag the merged peaks
|
||||
return bag_peaks(merged[::-1])
|
||||
|
||||
def delete(root: ZcashMMRNode) -> ZcashMMRNode:
|
||||
'''
|
||||
Delete the rightmost leaf node from an existing MMR
|
||||
Return the new tree root
|
||||
'''
|
||||
|
||||
n_leaves = root.nLatestHeight - (root.nEarliestHeight - 1)
|
||||
# if there were an odd number of leaves,
|
||||
# simply replace root with left_child
|
||||
if n_leaves & 1:
|
||||
return root.left_child
|
||||
|
||||
# otherwise, we need to re-bag the peaks.
|
||||
else:
|
||||
# first peak
|
||||
peaks = [root.left_child]
|
||||
|
||||
# we do this traversing the right (unbalanced) side of the tree
|
||||
# we keep the left side (balanced subtree or leaf) of each subtree
|
||||
# until we reach a leaf
|
||||
subtree_root = root.right_child
|
||||
while subtree_root.left_child:
|
||||
peaks.append(subtree_root.left_child)
|
||||
subtree_root = subtree_root.right_child
|
||||
|
||||
new_root = bag_peaks(peaks)
|
||||
return new_root
|
|
@ -83,6 +83,15 @@ def hash256(s):
|
|||
return sha256(sha256(s))
|
||||
|
||||
|
||||
def ser_compactsize(n):
|
||||
if n < 253:
|
||||
return struct.pack("B", n)
|
||||
elif n < 0x10000:
|
||||
return struct.pack("<BH", 253, n)
|
||||
elif n < 0x100000000:
|
||||
return struct.pack("<BI", 254, n)
|
||||
return struct.pack("<BQ", 255, n)
|
||||
|
||||
def deser_string(f):
|
||||
nit = struct.unpack("<B", f.read(1))[0]
|
||||
if nit == 253:
|
||||
|
@ -132,6 +141,11 @@ def uint256_from_compact(c):
|
|||
return v
|
||||
|
||||
|
||||
def block_work_from_compact(c):
|
||||
target = uint256_from_compact(c)
|
||||
return 2**256 // (target + 1)
|
||||
|
||||
|
||||
def deser_vector(f, c):
|
||||
nit = struct.unpack("<B", f.read(1))[0]
|
||||
if nit == 253:
|
||||
|
|
|
@ -167,7 +167,7 @@ def wait_for_bitcoind_start(process, url, i):
|
|||
raise # unknown IO error
|
||||
except JSONRPCException as e: # Initialization phase
|
||||
if e.error['code'] != -28: # RPC in warmup?
|
||||
raise # unkown JSON RPC exception
|
||||
raise # unknown JSON RPC exception
|
||||
time.sleep(0.25)
|
||||
|
||||
def initialize_chain(test_dir):
|
||||
|
@ -541,7 +541,7 @@ def wait_and_assert_operationid_status_result(node, myopid, in_status='success',
|
|||
break
|
||||
time.sleep(1)
|
||||
|
||||
assert_true(result is not None, "timeout occured")
|
||||
assert_true(result is not None, "timeout occurred")
|
||||
status = result['status']
|
||||
|
||||
debug = os.getenv("PYTHON_DEBUG", "")
|
||||
|
|
|
@ -140,6 +140,7 @@ LIBZCASH_H = \
|
|||
zcash/address/sapling.hpp \
|
||||
zcash/address/sprout.hpp \
|
||||
zcash/address/zip32.h \
|
||||
zcash/History.hpp \
|
||||
zcash/JoinSplit.hpp \
|
||||
zcash/Note.hpp \
|
||||
zcash/prf.h \
|
||||
|
@ -569,6 +570,7 @@ libzcash_a_SOURCES = \
|
|||
zcash/address/sapling.cpp \
|
||||
zcash/address/sprout.cpp \
|
||||
zcash/address/zip32.cpp \
|
||||
zcash/History.cpp \
|
||||
zcash/JoinSplit.cpp \
|
||||
zcash/Proof.cpp \
|
||||
zcash/Note.cpp \
|
||||
|
@ -614,7 +616,7 @@ CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno */*.gcno wal
|
|||
|
||||
DISTCLEANFILES = obj/build.h
|
||||
|
||||
EXTRA_DIST = leveldb
|
||||
EXTRA_DIST = leveldb rust
|
||||
|
||||
clean-local:
|
||||
rm -f $(top_srcdir)/.cargo/config $(top_srcdir)/.cargo/.configured-for-*
|
||||
|
|
|
@ -28,6 +28,7 @@ zcash_gtest_SOURCES += \
|
|||
gtest/test_deprecation.cpp \
|
||||
gtest/test_dynamicusage.cpp \
|
||||
gtest/test_equihash.cpp \
|
||||
gtest/test_history.cpp \
|
||||
gtest/test_httprpc.cpp \
|
||||
gtest/test_joinsplit.cpp \
|
||||
gtest/test_keys.cpp \
|
||||
|
|
|
@ -99,6 +99,7 @@ BITCOIN_TESTS =\
|
|||
test/streams_tests.cpp \
|
||||
test/test_bitcoin.cpp \
|
||||
test/test_bitcoin.h \
|
||||
test/test_random.h \
|
||||
test/torcontrol_tests.cpp \
|
||||
test/transaction_tests.cpp \
|
||||
test/txvalidationcache_tests.cpp \
|
||||
|
|
40
src/chain.h
40
src/chain.h
|
@ -16,6 +16,7 @@
|
|||
|
||||
static const int SPROUT_VALUE_VERSION = 1001400;
|
||||
static const int SAPLING_VALUE_VERSION = 1010100;
|
||||
static const int CHAIN_HISTORY_ROOT_VERSION = 2010200;
|
||||
|
||||
/**
|
||||
* Maximum amount of time that a block timestamp is allowed to be ahead of the
|
||||
|
@ -253,10 +254,26 @@ public:
|
|||
//! Will be boost::none if nChainTx is zero.
|
||||
boost::optional<CAmount> nChainSaplingValue;
|
||||
|
||||
//! Root of the Sapling commitment tree as of the end of this block.
|
||||
//!
|
||||
//! - For blocks prior to (not including) the Heartwood activation block, this is
|
||||
//! always equal to hashLightClientRoot.
|
||||
//! - For blocks including and after the Heartwood activation block, this is only set
|
||||
//! once a block has been connected to the main chain, and will be null otherwise.
|
||||
uint256 hashFinalSaplingRoot;
|
||||
|
||||
//! Root of the ZIP 221 history tree as of the end of the previous block.
|
||||
//!
|
||||
//! - For blocks prior to and including the Heartwood activation block, this is
|
||||
//! always null.
|
||||
//! - For blocks after (not including) the Heartwood activation block, this is
|
||||
//! always equal to hashLightClientRoot.
|
||||
uint256 hashChainHistoryRoot;
|
||||
|
||||
//! block header
|
||||
int nVersion;
|
||||
uint256 hashMerkleRoot;
|
||||
uint256 hashFinalSaplingRoot;
|
||||
uint256 hashLightClientRoot;
|
||||
unsigned int nTime;
|
||||
unsigned int nBits;
|
||||
uint256 nNonce;
|
||||
|
@ -289,7 +306,7 @@ public:
|
|||
|
||||
nVersion = 0;
|
||||
hashMerkleRoot = uint256();
|
||||
hashFinalSaplingRoot = uint256();
|
||||
hashLightClientRoot = uint256();
|
||||
nTime = 0;
|
||||
nBits = 0;
|
||||
nNonce = uint256();
|
||||
|
@ -307,7 +324,7 @@ public:
|
|||
|
||||
nVersion = block.nVersion;
|
||||
hashMerkleRoot = block.hashMerkleRoot;
|
||||
hashFinalSaplingRoot = block.hashFinalSaplingRoot;
|
||||
hashLightClientRoot = block.hashLightClientRoot;
|
||||
nTime = block.nTime;
|
||||
nBits = block.nBits;
|
||||
nNonce = block.nNonce;
|
||||
|
@ -339,7 +356,7 @@ public:
|
|||
if (pprev)
|
||||
block.hashPrevBlock = pprev->GetBlockHash();
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashFinalSaplingRoot = hashFinalSaplingRoot;
|
||||
block.hashLightClientRoot = hashLightClientRoot;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
|
@ -461,7 +478,7 @@ public:
|
|||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrev);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(hashLightClientRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
|
@ -479,6 +496,17 @@ public:
|
|||
READWRITE(nSaplingValue);
|
||||
}
|
||||
|
||||
// Only read/write hashFinalSaplingRoot and hashChainHistoryRoot if the
|
||||
// client version used to create this index was storing them.
|
||||
if ((s.GetType() & SER_DISK) && (nVersion >= CHAIN_HISTORY_ROOT_VERSION)) {
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(hashChainHistoryRoot);
|
||||
} else if (ser_action.ForRead()) {
|
||||
// For block indices written before the client was Heartwood-aware,
|
||||
// these are always identical.
|
||||
hashFinalSaplingRoot = hashLightClientRoot;
|
||||
}
|
||||
|
||||
// If you have just added new serialized fields above, remember to add
|
||||
// them to CBlockTreeDB::LoadBlockIndexGuts() in txdb.cpp :)
|
||||
}
|
||||
|
@ -489,7 +517,7 @@ public:
|
|||
block.nVersion = nVersion;
|
||||
block.hashPrevBlock = hashPrev;
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashFinalSaplingRoot = hashFinalSaplingRoot;
|
||||
block.hashLightClientRoot = hashLightClientRoot;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
|
|
|
@ -321,8 +321,7 @@ public:
|
|||
consensus.vUpgrades[Consensus::UPGRADE_BLOSSOM].hashActivationBlock =
|
||||
uint256S("00367515ef2e781b8c9358b443b6329572599edd02c59e8af67db9785122f298");
|
||||
consensus.vUpgrades[Consensus::UPGRADE_HEARTWOOD].nProtocolVersion = 170010;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_HEARTWOOD].nActivationHeight =
|
||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||
consensus.vUpgrades[Consensus::UPGRADE_HEARTWOOD].nActivationHeight = 903800;
|
||||
|
||||
// On testnet we activate this rule 6 blocks after Blossom activation. From block 299188 and
|
||||
// prior to Blossom activation, the testnet minimum-difficulty threshold was 15 minutes (i.e.
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
|
||||
#define CLIENT_VERSION_MAJOR 2
|
||||
#define CLIENT_VERSION_MINOR 1
|
||||
#define CLIENT_VERSION_REVISION 1
|
||||
#define CLIENT_VERSION_BUILD 51
|
||||
#define CLIENT_VERSION_REVISION 2
|
||||
#define CLIENT_VERSION_BUILD 25
|
||||
|
||||
//! Set to true for release, false for prerelease or test build
|
||||
#define CLIENT_VERSION_IS_RELEASE true
|
||||
|
|
344
src/coins.cpp
344
src/coins.cpp
|
@ -49,6 +49,10 @@ bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return fal
|
|||
bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; }
|
||||
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
||||
uint256 CCoinsView::GetBestAnchor(ShieldedType type) const { return uint256(); };
|
||||
HistoryIndex CCoinsView::GetHistoryLength(uint32_t epochId) const { return 0; }
|
||||
HistoryNode CCoinsView::GetHistoryAt(uint32_t epochId, HistoryIndex index) const { return HistoryNode(); }
|
||||
uint256 CCoinsView::GetHistoryRoot(uint32_t epochId) const { return uint256(); }
|
||||
|
||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashSproutAnchor,
|
||||
|
@ -56,7 +60,8 @@ bool CCoinsView::BatchWrite(CCoinsMap &mapCoins,
|
|||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers) { return false; }
|
||||
CNullifiersMap &mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMap) { return false; }
|
||||
bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
|
||||
|
||||
|
||||
|
@ -69,6 +74,9 @@ bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { retu
|
|||
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
|
||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||
uint256 CCoinsViewBacked::GetBestAnchor(ShieldedType type) const { return base->GetBestAnchor(type); }
|
||||
HistoryIndex CCoinsViewBacked::GetHistoryLength(uint32_t epochId) const { return base->GetHistoryLength(epochId); }
|
||||
HistoryNode CCoinsViewBacked::GetHistoryAt(uint32_t epochId, HistoryIndex index) const { return base->GetHistoryAt(epochId, index); }
|
||||
uint256 CCoinsViewBacked::GetHistoryRoot(uint32_t epochId) const { return base->GetHistoryRoot(epochId); }
|
||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
|
@ -77,7 +85,12 @@ bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins,
|
|||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, mapSproutAnchors, mapSaplingAnchors, mapSproutNullifiers, mapSaplingNullifiers); }
|
||||
CNullifiersMap &mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMap) {
|
||||
return base->BatchWrite(mapCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor,
|
||||
mapSproutAnchors, mapSaplingAnchors, mapSproutNullifiers, mapSaplingNullifiers,
|
||||
historyCacheMap);
|
||||
}
|
||||
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
|
||||
|
||||
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
|
||||
|
@ -95,6 +108,7 @@ size_t CCoinsViewCache::DynamicMemoryUsage() const {
|
|||
memusage::DynamicUsage(cacheSaplingAnchors) +
|
||||
memusage::DynamicUsage(cacheSproutNullifiers) +
|
||||
memusage::DynamicUsage(cacheSaplingNullifiers) +
|
||||
memusage::DynamicUsage(historyCacheMap) +
|
||||
cachedCoinsUsage;
|
||||
}
|
||||
|
||||
|
@ -188,6 +202,31 @@ bool CCoinsViewCache::GetNullifier(const uint256 &nullifier, ShieldedType type)
|
|||
return tmp;
|
||||
}
|
||||
|
||||
HistoryIndex CCoinsViewCache::GetHistoryLength(uint32_t epochId) const {
|
||||
HistoryCache& historyCache = SelectHistoryCache(epochId);
|
||||
return historyCache.length;
|
||||
}
|
||||
|
||||
HistoryNode CCoinsViewCache::GetHistoryAt(uint32_t epochId, HistoryIndex index) const {
|
||||
HistoryCache& historyCache = SelectHistoryCache(epochId);
|
||||
|
||||
if (index >= historyCache.length) {
|
||||
// Caller should ensure that it is limiting history
|
||||
// request to 0..GetHistoryLength(epochId)-1 range
|
||||
throw std::runtime_error("Invalid history request");
|
||||
}
|
||||
|
||||
if (index >= historyCache.updateDepth) {
|
||||
return historyCache.appends[index];
|
||||
}
|
||||
|
||||
return base->GetHistoryAt(epochId, index);
|
||||
}
|
||||
|
||||
uint256 CCoinsViewCache::GetHistoryRoot(uint32_t epochId) const {
|
||||
return SelectHistoryCache(epochId).root;
|
||||
}
|
||||
|
||||
template<typename Tree, typename Cache, typename CacheIterator, typename CacheEntry>
|
||||
void CCoinsViewCache::AbstractPushAnchor(
|
||||
const Tree &tree,
|
||||
|
@ -260,6 +299,262 @@ void CCoinsViewCache::BringBestAnchorIntoCache(
|
|||
assert(GetSaplingAnchorAt(currentRoot, tree));
|
||||
}
|
||||
|
||||
void draftMMRNode(std::vector<uint32_t> &indices,
|
||||
std::vector<HistoryEntry> &entries,
|
||||
HistoryNode nodeData,
|
||||
uint32_t alt,
|
||||
uint32_t peak_pos)
|
||||
{
|
||||
HistoryEntry newEntry = alt == 0
|
||||
? libzcash::LeafToEntry(nodeData)
|
||||
// peak_pos - (1 << alt) is the array position of left child.
|
||||
// peak_pos - 1 is the array position of right child.
|
||||
: libzcash::NodeToEntry(nodeData, peak_pos - (1 << alt), peak_pos - 1);
|
||||
|
||||
indices.push_back(peak_pos);
|
||||
entries.push_back(newEntry);
|
||||
}
|
||||
|
||||
// Computes floor(log2(x)).
|
||||
static inline uint32_t floor_log2(uint32_t x) {
|
||||
assert(x > 0);
|
||||
int log = 0;
|
||||
while (x >>= 1) { ++log; }
|
||||
return log;
|
||||
}
|
||||
|
||||
// Computes the altitude of the largest subtree for an MMR with n nodes,
|
||||
// which is floor(log2(n + 1)) - 1.
|
||||
static inline uint32_t altitude(uint32_t n) {
|
||||
return floor_log2(n + 1) - 1;
|
||||
}
|
||||
|
||||
uint32_t CCoinsViewCache::PreloadHistoryTree(uint32_t epochId, bool extra, std::vector<HistoryEntry> &entries, std::vector<uint32_t> &entry_indices) {
|
||||
auto treeLength = GetHistoryLength(epochId);
|
||||
|
||||
if (treeLength <= 0) {
|
||||
throw std::runtime_error("Invalid PreloadHistoryTree state called - tree should exist");
|
||||
} else if (treeLength == 1) {
|
||||
entries.push_back(libzcash::LeafToEntry(GetHistoryAt(epochId, 0)));
|
||||
entry_indices.push_back(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t last_peak_pos = 0;
|
||||
uint32_t last_peak_alt = 0;
|
||||
uint32_t alt = 0;
|
||||
uint32_t peak_pos = 0;
|
||||
uint32_t total_peaks = 0;
|
||||
|
||||
// Assume the following example peak layout with 14 leaves, and 25 stored nodes in
|
||||
// total (the "tree length"):
|
||||
//
|
||||
// P
|
||||
// /\
|
||||
// / \
|
||||
// / \ \
|
||||
// / \ \ Altitude
|
||||
// _A_ \ \ 3
|
||||
// _/ \_ B \ 2
|
||||
// / \ / \ / \ C 1
|
||||
// /\ /\ /\ /\ /\ /\ /\ 0
|
||||
//
|
||||
// We start by determining the altitude of the highest peak (A).
|
||||
alt = altitude(treeLength);
|
||||
|
||||
// We determine the position of the highest peak (A) by pretending it is the right
|
||||
// sibling in a tree, and its left-most leaf has position 0. Then the left sibling
|
||||
// of (A) has position -1, and so we can "jump" to the peak's position by computing
|
||||
// -1 + 2^(alt + 1) - 1.
|
||||
peak_pos = (1 << (alt + 1)) - 2;
|
||||
|
||||
// Now that we have the position and altitude of the highest peak (A), we collect
|
||||
// the remaining peaks (B, C). We navigate the peaks as if they were nodes in this
|
||||
// Merkle tree (with additional imaginary nodes 1 and 2, that have positions beyond
|
||||
// the MMR's length):
|
||||
//
|
||||
// / \
|
||||
// / \
|
||||
// / \
|
||||
// / \
|
||||
// A ==========> 1
|
||||
// / \ // \
|
||||
// _/ \_ B ==> 2
|
||||
// /\ /\ /\ //
|
||||
// / \ / \ / \ C
|
||||
// /\ /\ /\ /\ /\ /\ /\
|
||||
//
|
||||
while (alt != 0) {
|
||||
// If peak_pos is out of bounds of the tree, we compute the position of its left
|
||||
// child, and drop down one level in the tree.
|
||||
if (peak_pos >= treeLength) {
|
||||
// left child, -2^alt
|
||||
peak_pos = peak_pos - (1 << alt);
|
||||
alt = alt - 1;
|
||||
}
|
||||
|
||||
// If the peak exists, we take it and then continue with its right sibling.
|
||||
if (peak_pos < treeLength) {
|
||||
draftMMRNode(entry_indices, entries, GetHistoryAt(epochId, peak_pos), alt, peak_pos);
|
||||
|
||||
last_peak_pos = peak_pos;
|
||||
last_peak_alt = alt;
|
||||
|
||||
// right sibling
|
||||
peak_pos = peak_pos + (1 << (alt + 1)) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
total_peaks = entries.size();
|
||||
|
||||
// Return early if we don't require extra nodes.
|
||||
if (!extra) return total_peaks;
|
||||
|
||||
alt = last_peak_alt;
|
||||
peak_pos = last_peak_pos;
|
||||
|
||||
|
||||
// P
|
||||
// /\
|
||||
// / \
|
||||
// / \ \
|
||||
// / \ \
|
||||
// _A_ \ \
|
||||
// _/ \_ B \
|
||||
// / \ / \ / \ C
|
||||
// /\ /\ /\ /\ /\ /\ /\
|
||||
// D E
|
||||
//
|
||||
// For extra peaks needed for deletion, we do extra pass on right slope of the last peak
|
||||
// and add those nodes + their siblings. Extra would be (D, E) for the picture above.
|
||||
while (alt > 0) {
|
||||
uint32_t left_pos = peak_pos - (1 << alt);
|
||||
uint32_t right_pos = peak_pos - 1;
|
||||
alt = alt - 1;
|
||||
|
||||
// drafting left child
|
||||
draftMMRNode(entry_indices, entries, GetHistoryAt(epochId, left_pos), alt, left_pos);
|
||||
|
||||
// drafting right child
|
||||
draftMMRNode(entry_indices, entries, GetHistoryAt(epochId, right_pos), alt, right_pos);
|
||||
|
||||
// continuing on right slope
|
||||
peak_pos = right_pos;
|
||||
}
|
||||
|
||||
return total_peaks;
|
||||
}
|
||||
|
||||
HistoryCache& CCoinsViewCache::SelectHistoryCache(uint32_t epochId) const {
|
||||
auto entry = historyCacheMap.find(epochId);
|
||||
|
||||
if (entry != historyCacheMap.end()) {
|
||||
return entry->second;
|
||||
} else {
|
||||
auto cache = HistoryCache(
|
||||
base->GetHistoryLength(epochId),
|
||||
base->GetHistoryRoot(epochId),
|
||||
epochId
|
||||
);
|
||||
return historyCacheMap.insert({epochId, cache}).first->second;
|
||||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::PushHistoryNode(uint32_t epochId, const HistoryNode node) {
|
||||
HistoryCache& historyCache = SelectHistoryCache(epochId);
|
||||
|
||||
if (historyCache.length == 0) {
|
||||
// special case, it just goes into the cache right away
|
||||
historyCache.Extend(node);
|
||||
|
||||
if (librustzcash_mmr_hash_node(epochId, node.data(), historyCache.root.begin()) != 0) {
|
||||
throw std::runtime_error("hashing node failed");
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<HistoryEntry> entries;
|
||||
std::vector<uint32_t> entry_indices;
|
||||
|
||||
PreloadHistoryTree(epochId, false, entries, entry_indices);
|
||||
|
||||
uint256 newRoot;
|
||||
std::array<HistoryNode, 32> appendBuf;
|
||||
|
||||
uint32_t appends = librustzcash_mmr_append(
|
||||
epochId,
|
||||
historyCache.length,
|
||||
entry_indices.data(),
|
||||
entries.data(),
|
||||
entry_indices.size(),
|
||||
node.data(),
|
||||
newRoot.begin(),
|
||||
appendBuf.data()->data()
|
||||
);
|
||||
|
||||
for (size_t i = 0; i < appends; i++) {
|
||||
historyCache.Extend(appendBuf[i]);
|
||||
}
|
||||
|
||||
historyCache.root = newRoot;
|
||||
}
|
||||
|
||||
void CCoinsViewCache::PopHistoryNode(uint32_t epochId) {
|
||||
HistoryCache& historyCache = SelectHistoryCache(epochId);
|
||||
uint256 newRoot;
|
||||
|
||||
switch (historyCache.length) {
|
||||
case 0:
|
||||
// Caller is not expected to pop from empty tree! Caller should
|
||||
// switch to previous epoch and pop history from there.
|
||||
throw std::runtime_error("popping history node from empty history");
|
||||
case 1:
|
||||
// Just resetting tree to empty
|
||||
historyCache.Truncate(0);
|
||||
historyCache.root = uint256();
|
||||
return;
|
||||
case 2:
|
||||
// - A tree with one leaf has length 1.
|
||||
// - A tree with two leaves has length 3.
|
||||
throw std::runtime_error("a history tree cannot have two nodes");
|
||||
case 3:
|
||||
// After removing a leaf from a tree with two leaves, we are left
|
||||
// with a single-node tree, whose root is just the hash of that
|
||||
// node.
|
||||
if (librustzcash_mmr_hash_node(
|
||||
epochId,
|
||||
GetHistoryAt(epochId, 0).data(),
|
||||
newRoot.begin()
|
||||
) != 0) {
|
||||
throw std::runtime_error("hashing node failed");
|
||||
}
|
||||
historyCache.Truncate(1);
|
||||
historyCache.root = newRoot;
|
||||
return;
|
||||
default:
|
||||
// This is a non-elementary pop, so use the full tree logic.
|
||||
std::vector<HistoryEntry> entries;
|
||||
std::vector<uint32_t> entry_indices;
|
||||
|
||||
uint32_t peak_count = PreloadHistoryTree(epochId, true, entries, entry_indices);
|
||||
|
||||
uint32_t numberOfDeletes = librustzcash_mmr_delete(
|
||||
epochId,
|
||||
historyCache.length,
|
||||
entry_indices.data(),
|
||||
entries.data(),
|
||||
peak_count,
|
||||
entries.size() - peak_count,
|
||||
newRoot.begin()
|
||||
);
|
||||
|
||||
historyCache.Truncate(historyCache.length - numberOfDeletes);
|
||||
historyCache.root = newRoot;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Tree, typename Cache, typename CacheEntry>
|
||||
void CCoinsViewCache::AbstractPopAnchor(
|
||||
const uint256 &newrt,
|
||||
|
@ -470,6 +765,35 @@ void BatchWriteAnchors(
|
|||
}
|
||||
}
|
||||
|
||||
void BatchWriteHistory(CHistoryCacheMap& historyCacheMap, CHistoryCacheMap& historyCacheMapIn) {
|
||||
for (auto nextHistoryCache = historyCacheMapIn.begin(); nextHistoryCache != historyCacheMapIn.end(); nextHistoryCache++) {
|
||||
auto historyCacheIn = nextHistoryCache->second;
|
||||
auto epochId = nextHistoryCache->first;
|
||||
|
||||
auto historyCache = historyCacheMap.find(epochId);
|
||||
if (historyCache != historyCacheMap.end()) {
|
||||
// delete old entries since updateDepth
|
||||
historyCache->second.Truncate(historyCacheIn.updateDepth);
|
||||
|
||||
// Replace/append new/updated entries. HistoryCache.Extend
|
||||
// auto-indexes the nodes, so we need to extend in the same order as
|
||||
// this cache is indexed.
|
||||
for (size_t i = historyCacheIn.updateDepth; i < historyCacheIn.length; i++) {
|
||||
historyCache->second.Extend(historyCacheIn.appends[i]);
|
||||
}
|
||||
|
||||
// the lengths should now match
|
||||
assert(historyCache->second.length == historyCacheIn.length);
|
||||
|
||||
// write current root
|
||||
historyCache->second.root = historyCacheIn.root;
|
||||
} else {
|
||||
// Just insert the history cache into its parent
|
||||
historyCacheMap.insert({epochId, historyCacheIn});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlockIn,
|
||||
const uint256 &hashSproutAnchorIn,
|
||||
|
@ -477,7 +801,8 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers) {
|
||||
CNullifiersMap &mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMapIn) {
|
||||
assert(!hasModifier);
|
||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
|
@ -520,6 +845,8 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||
::BatchWriteNullifiers(mapSproutNullifiers, cacheSproutNullifiers);
|
||||
::BatchWriteNullifiers(mapSaplingNullifiers, cacheSaplingNullifiers);
|
||||
|
||||
::BatchWriteHistory(historyCacheMap, historyCacheMapIn);
|
||||
|
||||
hashSproutAnchor = hashSproutAnchorIn;
|
||||
hashSaplingAnchor = hashSaplingAnchorIn;
|
||||
hashBlock = hashBlockIn;
|
||||
|
@ -527,12 +854,21 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||
}
|
||||
|
||||
bool CCoinsViewCache::Flush() {
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, cacheSproutAnchors, cacheSaplingAnchors, cacheSproutNullifiers, cacheSaplingNullifiers);
|
||||
bool fOk = base->BatchWrite(cacheCoins,
|
||||
hashBlock,
|
||||
hashSproutAnchor,
|
||||
hashSaplingAnchor,
|
||||
cacheSproutAnchors,
|
||||
cacheSaplingAnchors,
|
||||
cacheSproutNullifiers,
|
||||
cacheSaplingNullifiers,
|
||||
historyCacheMap);
|
||||
cacheCoins.clear();
|
||||
cacheSproutAnchors.clear();
|
||||
cacheSaplingAnchors.clear();
|
||||
cacheSproutNullifiers.clear();
|
||||
cacheSaplingNullifiers.clear();
|
||||
historyCacheMap.clear();
|
||||
cachedCoinsUsage = 0;
|
||||
return fOk;
|
||||
}
|
||||
|
|
46
src/coins.h
46
src/coins.h
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "zcash/History.hpp"
|
||||
#include "zcash/IncrementalMerkleTree.hpp"
|
||||
|
||||
/**
|
||||
|
@ -321,6 +322,7 @@ typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsM
|
|||
typedef boost::unordered_map<uint256, CAnchorsSproutCacheEntry, CCoinsKeyHasher> CAnchorsSproutMap;
|
||||
typedef boost::unordered_map<uint256, CAnchorsSaplingCacheEntry, CCoinsKeyHasher> CAnchorsSaplingMap;
|
||||
typedef boost::unordered_map<uint256, CNullifiersCacheEntry, CCoinsKeyHasher> CNullifiersMap;
|
||||
typedef boost::unordered_map<uint32_t, HistoryCache> CHistoryCacheMap;
|
||||
|
||||
struct CCoinsStats
|
||||
{
|
||||
|
@ -362,6 +364,15 @@ public:
|
|||
//! Get the current "tip" or the latest anchored tree root in the chain
|
||||
virtual uint256 GetBestAnchor(ShieldedType type) const;
|
||||
|
||||
//! Get the current chain history length (which should be roughly chain height x2)
|
||||
virtual HistoryIndex GetHistoryLength(uint32_t epochId) const;
|
||||
|
||||
//! Get history node at specified index
|
||||
virtual HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const;
|
||||
|
||||
//! Get current history root
|
||||
virtual uint256 GetHistoryRoot(uint32_t epochId) const;
|
||||
|
||||
//! Do a bulk modification (multiple CCoins changes + BestBlock change).
|
||||
//! The passed mapCoins can be modified.
|
||||
virtual bool BatchWrite(CCoinsMap &mapCoins,
|
||||
|
@ -371,7 +382,8 @@ public:
|
|||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers);
|
||||
CNullifiersMap &mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMap);
|
||||
|
||||
//! Calculate statistics about the unspent transaction output set
|
||||
virtual bool GetStats(CCoinsStats &stats) const;
|
||||
|
@ -396,6 +408,9 @@ public:
|
|||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
uint256 GetBestAnchor(ShieldedType type) const;
|
||||
HistoryIndex GetHistoryLength(uint32_t epochId) const;
|
||||
HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const;
|
||||
uint256 GetHistoryRoot(uint32_t epochId) const;
|
||||
void SetBackend(CCoinsView &viewIn);
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
|
@ -404,7 +419,8 @@ public:
|
|||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers);
|
||||
CNullifiersMap &mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMap);
|
||||
bool GetStats(CCoinsStats &stats) const;
|
||||
};
|
||||
|
||||
|
@ -451,6 +467,7 @@ protected:
|
|||
mutable CAnchorsSaplingMap cacheSaplingAnchors;
|
||||
mutable CNullifiersMap cacheSproutNullifiers;
|
||||
mutable CNullifiersMap cacheSaplingNullifiers;
|
||||
mutable CHistoryCacheMap historyCacheMap;
|
||||
|
||||
/* Cached dynamic memory usage for the inner CCoins objects. */
|
||||
mutable size_t cachedCoinsUsage;
|
||||
|
@ -467,6 +484,9 @@ public:
|
|||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
uint256 GetBestAnchor(ShieldedType type) const;
|
||||
HistoryIndex GetHistoryLength(uint32_t epochId) const;
|
||||
HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const;
|
||||
uint256 GetHistoryRoot(uint32_t epochId) const;
|
||||
void SetBestBlock(const uint256 &hashBlock);
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
|
@ -475,8 +495,8 @@ public:
|
|||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers);
|
||||
|
||||
CNullifiersMap &mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMap);
|
||||
|
||||
// Adds the tree to mapSproutAnchors (or mapSaplingAnchors based on the type of tree)
|
||||
// and sets the current commitment root to this root.
|
||||
|
@ -489,6 +509,12 @@ public:
|
|||
// Marks nullifiers for a given transaction as spent or not.
|
||||
void SetNullifiers(const CTransaction& tx, bool spent);
|
||||
|
||||
// Push MMR node history at the end of the history tree
|
||||
void PushHistoryNode(uint32_t epochId, const HistoryNode node);
|
||||
|
||||
// Pop MMR node history from the end of the history tree
|
||||
void PopHistoryNode(uint32_t epochId);
|
||||
|
||||
/**
|
||||
* Return a pointer to CCoins in the cache, or NULL if not found. This is
|
||||
* more efficient than GetCoins. Modifications to other cache entries are
|
||||
|
@ -582,6 +608,18 @@ private:
|
|||
const uint256 ¤tRoot,
|
||||
Tree &tree
|
||||
);
|
||||
|
||||
//! Preload history tree for further update.
|
||||
//!
|
||||
//! If extra = true, extra nodes for deletion are also preloaded.
|
||||
//! This will allow to delete tail entries from preloaded tree without
|
||||
//! any further database lookups.
|
||||
//!
|
||||
//! Returns number of peaks, not total number of loaded nodes.
|
||||
uint32_t PreloadHistoryTree(uint32_t epochId, bool extra, std::vector<HistoryEntry> &entries, std::vector<uint32_t> &entry_indices);
|
||||
|
||||
//! Selects history cache for specified epoch.
|
||||
HistoryCache& SelectHistoryCache(uint32_t epochId) const;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_COINS_H
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// Deprecation policy:
|
||||
// * Shut down 16 weeks' worth of blocks after the estimated release block height.
|
||||
// * A warning is shown during the 2 weeks' worth of blocks prior to shut down.
|
||||
static const int APPROX_RELEASE_HEIGHT = 719034;
|
||||
static const int APPROX_RELEASE_HEIGHT = 798142;
|
||||
static const int WEEKS_UNTIL_DEPRECATION = 16;
|
||||
static const int DEPRECATION_HEIGHT = APPROX_RELEASE_HEIGHT + (WEEKS_UNTIL_DEPRECATION * 7 * 24 * 48);
|
||||
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "utiltest.h"
|
||||
#include "zcash/History.hpp"
|
||||
|
||||
// Fake an empty view
|
||||
class FakeCoinsViewDB : public CCoinsView {
|
||||
public:
|
||||
FakeCoinsViewDB() {}
|
||||
|
||||
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HaveCoins(const uint256 &txid) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint256 GetBestBlock() const {
|
||||
uint256 a;
|
||||
return a;
|
||||
}
|
||||
|
||||
uint256 GetBestAnchor(ShieldedType type) const {
|
||||
uint256 a;
|
||||
return a;
|
||||
}
|
||||
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashSproutAnchor,
|
||||
const uint256 &hashSaplingAnchor,
|
||||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap saplingNullifiersMap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetStats(CCoinsStats &stats) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
HistoryIndex GetHistoryLength(uint32_t branchId) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HistoryNode GetHistoryAt(uint32_t branchId, HistoryIndex index) const {
|
||||
return HistoryNode();
|
||||
}
|
||||
};
|
||||
|
||||
HistoryNode getLeafN(uint64_t block_num) {
|
||||
HistoryNode node = libzcash::NewLeaf(
|
||||
uint256(),
|
||||
block_num*10,
|
||||
block_num*13,
|
||||
uint256(),
|
||||
uint256(),
|
||||
block_num,
|
||||
3
|
||||
);
|
||||
return node;
|
||||
}
|
||||
|
||||
TEST(History, Smoky) {
|
||||
// Fake an empty view
|
||||
FakeCoinsViewDB fakeDB;
|
||||
CCoinsViewCache view(&fakeDB);
|
||||
|
||||
// Test initial value
|
||||
EXPECT_EQ(view.GetHistoryLength(0), 0);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(1));
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 1);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(2));
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 3);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(3));
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 4);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(4));
|
||||
|
||||
uint256 h4Root = view.GetHistoryRoot(1);
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 7);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(5));
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
||||
|
||||
view.PopHistoryNode(1);
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 7);
|
||||
EXPECT_EQ(h4Root, view.GetHistoryRoot(1));
|
||||
}
|
||||
|
||||
|
||||
TEST(History, EpochBoundaries) {
|
||||
// Fake an empty view
|
||||
FakeCoinsViewDB fakeDB;
|
||||
CCoinsViewCache view(&fakeDB);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(1));
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 1);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(2));
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 3);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(3));
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 4);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(4));
|
||||
|
||||
uint256 h4Root = view.GetHistoryRoot(1);
|
||||
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 7);
|
||||
|
||||
view.PushHistoryNode(1, getLeafN(5));
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
||||
|
||||
|
||||
// New Epoch(2)
|
||||
view.PushHistoryNode(2, getLeafN(6));
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
||||
EXPECT_EQ(view.GetHistoryLength(2), 1);
|
||||
|
||||
view.PushHistoryNode(2, getLeafN(7));
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
||||
EXPECT_EQ(view.GetHistoryLength(2), 3);
|
||||
|
||||
view.PushHistoryNode(2, getLeafN(8));
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
||||
EXPECT_EQ(view.GetHistoryLength(2), 4);
|
||||
|
||||
// Rolling epoch back to 1
|
||||
view.PopHistoryNode(2);
|
||||
EXPECT_EQ(view.GetHistoryLength(2), 3);
|
||||
|
||||
view.PopHistoryNode(2);
|
||||
EXPECT_EQ(view.GetHistoryLength(2), 1);
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 8);
|
||||
|
||||
// And even rolling epoch 1 back a bit
|
||||
view.PopHistoryNode(1);
|
||||
EXPECT_EQ(view.GetHistoryLength(1), 7);
|
||||
|
||||
// And also rolling epoch 2 back to 0
|
||||
view.PopHistoryNode(2);
|
||||
EXPECT_EQ(view.GetHistoryLength(2), 0);
|
||||
|
||||
}
|
|
@ -874,7 +874,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||
struct sigaction sa;
|
||||
sa.sa_handler = HandleSIGTERM;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
|
||||
|
@ -883,7 +883,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||
struct sigaction sa_hup;
|
||||
sa_hup.sa_handler = HandleSIGHUP;
|
||||
sigemptyset(&sa_hup.sa_mask);
|
||||
sa_hup.sa_flags = 0;
|
||||
sa_hup.sa_flags = SA_RESTART;
|
||||
sigaction(SIGHUP, &sa_hup, NULL);
|
||||
|
||||
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly
|
||||
|
|
120
src/main.cpp
120
src/main.cpp
|
@ -1740,7 +1740,7 @@ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHea
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
|
||||
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, int nHeight, const Consensus::Params& consensusParams)
|
||||
{
|
||||
block.SetNull();
|
||||
|
||||
|
@ -1758,7 +1758,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus:
|
|||
}
|
||||
|
||||
// Check the header
|
||||
if (!(CheckEquihashSolution(&block, consensusParams) &&
|
||||
if (!(CheckEquihashSolution(&block, nHeight, consensusParams) &&
|
||||
CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)))
|
||||
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
|
||||
|
||||
|
@ -1767,7 +1767,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus:
|
|||
|
||||
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
||||
{
|
||||
if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams))
|
||||
if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), pindex->nHeight, consensusParams))
|
||||
return false;
|
||||
if (block.GetHash() != pindex->GetBlockHash())
|
||||
return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
|
||||
|
@ -2433,6 +2433,12 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s
|
|||
view.PopAnchor(SaplingMerkleTree::empty_root(), SAPLING);
|
||||
}
|
||||
|
||||
auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus());
|
||||
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
view.PopHistoryNode(consensusBranchId);
|
||||
}
|
||||
|
||||
// move best block pointer to prevout block
|
||||
view.SetBestBlock(pindex->pprev->GetBlockHash());
|
||||
|
||||
|
@ -2690,8 +2696,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
SaplingMerkleTree sapling_tree;
|
||||
assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree));
|
||||
|
||||
// Grab the consensus branch ID for the block's height
|
||||
// Grab the consensus branch ID for this block and its parent
|
||||
auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus());
|
||||
auto prevConsensusBranchId = CurrentEpochBranchId(pindex->nHeight - 1, chainparams.GetConsensus());
|
||||
|
||||
size_t total_sapling_tx = 0;
|
||||
|
||||
std::vector<PrecomputedTransactionData> txdata;
|
||||
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
|
||||
|
@ -2811,6 +2820,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
sapling_tree.append(outputDescription.cmu);
|
||||
}
|
||||
|
||||
if (!(tx.vShieldedSpend.empty() && tx.vShieldedOutput.empty())) {
|
||||
total_sapling_tx += 1;
|
||||
}
|
||||
|
||||
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
}
|
||||
|
@ -2819,19 +2832,66 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
view.PushAnchor(sapling_tree);
|
||||
if (!fJustCheck) {
|
||||
pindex->hashFinalSproutRoot = sprout_tree.root();
|
||||
// - If this block is before Heartwood activation, then we don't set
|
||||
// hashFinalSaplingRoot here to maintain the invariant documented in
|
||||
// CBlockIndex (which was ensured in AddToBlockIndex).
|
||||
// - If this block is on or after Heartwood activation, this is where we
|
||||
// set the correct value of hashFinalSaplingRoot; in particular,
|
||||
// blocks that are never passed to ConnectBlock() (and thus never on
|
||||
// the main chain) will stay with hashFinalSaplingRoot set to null.
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
pindex->hashFinalSaplingRoot = sapling_tree.root();
|
||||
}
|
||||
}
|
||||
blockundo.old_sprout_tree_root = old_sprout_tree_root;
|
||||
|
||||
// If Sapling is active, block.hashFinalSaplingRoot must be the
|
||||
// same as the root of the Sapling tree
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_SAPLING)) {
|
||||
if (block.hashFinalSaplingRoot != sapling_tree.root()) {
|
||||
if (IsActivationHeight(pindex->nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// In the block that activates ZIP 221, block.hashLightClientRoot MUST
|
||||
// be set to all zero bytes.
|
||||
if (!block.hashLightClientRoot.IsNull()) {
|
||||
return state.DoS(100,
|
||||
error("ConnectBlock(): block's hashFinalSaplingRoot is incorrect"),
|
||||
error("ConnectBlock(): block's hashLightClientRoot is incorrect (should be null)"),
|
||||
REJECT_INVALID, "bad-heartwood-root-in-block");
|
||||
}
|
||||
} else if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// If Heartwood is active, block.hashLightClientRoot must be the same as
|
||||
// the root of the history tree for the previous block. We only store
|
||||
// one tree per epoch, so we have two possible cases:
|
||||
// - If the previous block is in the previous epoch, this block won't
|
||||
// affect that epoch's tree root.
|
||||
// - If the previous block is in this epoch, this block would affect
|
||||
// this epoch's tree root, but as we haven't updated the tree for this
|
||||
// block yet, view.GetHistoryRoot() returns the root we need.
|
||||
if (block.hashLightClientRoot != view.GetHistoryRoot(prevConsensusBranchId)) {
|
||||
return state.DoS(100,
|
||||
error("ConnectBlock(): block's hashLightClientRoot is incorrect (should be history tree root)"),
|
||||
REJECT_INVALID, "bad-heartwood-root-in-block");
|
||||
}
|
||||
} else if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_SAPLING)) {
|
||||
// If Sapling is active, block.hashLightClientRoot must be the
|
||||
// same as the root of the Sapling tree
|
||||
if (block.hashLightClientRoot != sapling_tree.root()) {
|
||||
return state.DoS(100,
|
||||
error("ConnectBlock(): block's hashLightClientRoot is incorrect (should be Sapling tree root)"),
|
||||
REJECT_INVALID, "bad-sapling-root-in-block");
|
||||
}
|
||||
}
|
||||
|
||||
// History read/write is started with Heartwood update.
|
||||
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
auto historyNode = libzcash::NewLeaf(
|
||||
block.GetHash(),
|
||||
block.nTime,
|
||||
block.nBits,
|
||||
pindex->hashFinalSaplingRoot,
|
||||
ArithToUint256(GetBlockProof(*pindex)),
|
||||
pindex->nHeight,
|
||||
total_sapling_tx
|
||||
);
|
||||
|
||||
view.PushHistoryNode(consensusBranchId, historyNode);
|
||||
}
|
||||
|
||||
int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart;
|
||||
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
|
||||
|
||||
|
@ -3542,7 +3602,7 @@ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) {
|
|||
return true;
|
||||
}
|
||||
|
||||
CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
|
||||
CBlockIndex* AddToBlockIndex(const CBlockHeader& block, const Consensus::Params& consensusParams)
|
||||
{
|
||||
// Check for duplicate
|
||||
uint256 hash = block.GetHash();
|
||||
|
@ -3564,6 +3624,18 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
|
|||
{
|
||||
pindexNew->pprev = (*miPrev).second;
|
||||
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
|
||||
|
||||
if (IsActivationHeight(pindexNew->nHeight, consensusParams, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// hashFinalSaplingRoot is currently null, and will be set correctly in ConnectBlock.
|
||||
// hashChainHistoryRoot is null.
|
||||
} else if (consensusParams.NetworkUpgradeActive(pindexNew->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
// hashFinalSaplingRoot is currently null, and will be set correctly in ConnectBlock.
|
||||
pindexNew->hashChainHistoryRoot = pindexNew->hashLightClientRoot;
|
||||
} else {
|
||||
// hashChainHistoryRoot is null.
|
||||
pindexNew->hashFinalSaplingRoot = pindexNew->hashLightClientRoot;
|
||||
}
|
||||
|
||||
pindexNew->BuildSkip();
|
||||
}
|
||||
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
|
||||
|
@ -3797,13 +3869,19 @@ bool CheckBlockHeader(
|
|||
const CChainParams& chainparams,
|
||||
bool fCheckPOW)
|
||||
{
|
||||
auto consensusParams = chainparams.GetConsensus();
|
||||
|
||||
// Check block version
|
||||
if (block.nVersion < MIN_BLOCK_VERSION)
|
||||
return state.DoS(100, error("CheckBlockHeader(): block version too low"),
|
||||
REJECT_INVALID, "version-too-low");
|
||||
|
||||
// Check Equihash solution is valid
|
||||
if (fCheckPOW && !CheckEquihashSolution(&block, chainparams.GetConsensus()))
|
||||
// Check Equihash solution is valid. The main check is in ContextualCheckBlockHeader,
|
||||
// because we currently need to know the block height. That function skips the genesis
|
||||
// block because it has no previous block, so we check it specifically here.
|
||||
if (fCheckPOW &&
|
||||
block.GetHash() == consensusParams.hashGenesisBlock &&
|
||||
!CheckEquihashSolution(&block, 0, consensusParams))
|
||||
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),
|
||||
REJECT_INVALID, "invalid-solution");
|
||||
|
||||
|
@ -3880,7 +3958,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state,
|
|||
|
||||
bool ContextualCheckBlockHeader(
|
||||
const CBlockHeader& block, CValidationState& state,
|
||||
const CChainParams& chainParams, CBlockIndex * const pindexPrev)
|
||||
const CChainParams& chainParams, CBlockIndex * const pindexPrev,
|
||||
bool fCheckPOW)
|
||||
{
|
||||
const Consensus::Params& consensusParams = chainParams.GetConsensus();
|
||||
uint256 hash = block.GetHash();
|
||||
|
@ -3892,6 +3971,11 @@ bool ContextualCheckBlockHeader(
|
|||
|
||||
int nHeight = pindexPrev->nHeight+1;
|
||||
|
||||
// Check Equihash solution is valid
|
||||
if (fCheckPOW && !CheckEquihashSolution(&block, nHeight, consensusParams))
|
||||
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),
|
||||
REJECT_INVALID, "invalid-solution");
|
||||
|
||||
// Check proof of work
|
||||
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) {
|
||||
return state.DoS(100, error("%s: incorrect proof of work", __func__),
|
||||
|
@ -4044,7 +4128,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
|
|||
return false;
|
||||
|
||||
if (pindex == NULL)
|
||||
pindex = AddToBlockIndex(block);
|
||||
pindex = AddToBlockIndex(block, chainparams.GetConsensus());
|
||||
|
||||
if (ppindex)
|
||||
*ppindex = pindex;
|
||||
|
@ -4181,7 +4265,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
|
|||
auto verifier = libzcash::ProofVerifier::Disabled();
|
||||
|
||||
// NOTE: CheckBlockHeader is called by CheckBlock
|
||||
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev))
|
||||
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, fCheckPOW))
|
||||
return false;
|
||||
if (!CheckBlock(block, state, chainparams, verifier, fCheckPOW, fCheckMerkleRoot))
|
||||
return false;
|
||||
|
@ -4369,7 +4453,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash)
|
|||
bool static LoadBlockIndexDB()
|
||||
{
|
||||
const CChainParams& chainparams = Params();
|
||||
if (!pblocktree->LoadBlockIndexGuts(InsertBlockIndex))
|
||||
if (!pblocktree->LoadBlockIndexGuts(InsertBlockIndex, chainparams))
|
||||
return false;
|
||||
|
||||
boost::this_thread::interruption_point();
|
||||
|
@ -4881,7 +4965,7 @@ bool InitBlockIndex(const CChainParams& chainparams)
|
|||
return error("LoadBlockIndex(): FindBlockPos failed");
|
||||
if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart()))
|
||||
return error("LoadBlockIndex(): writing genesis block to disk failed");
|
||||
CBlockIndex *pindex = AddToBlockIndex(block);
|
||||
CBlockIndex *pindex = AddToBlockIndex(block, chainparams.GetConsensus());
|
||||
if (!ReceivedBlockTransactions(block, state, chainparams, pindex, blockPos))
|
||||
return error("LoadBlockIndex(): genesis block not accepted");
|
||||
if (!ActivateBestChain(state, chainparams, &block))
|
||||
|
@ -4975,7 +5059,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
|
|||
std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
|
||||
while (range.first != range.second) {
|
||||
std::multimap<uint256, CDiskBlockPos>::iterator it = range.first;
|
||||
if (ReadBlockFromDisk(block, it->second, chainparams.GetConsensus()))
|
||||
if (ReadBlockFromDisk(block, it->second, mapBlockIndex[head]->nHeight, chainparams.GetConsensus()))
|
||||
{
|
||||
LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(),
|
||||
head.ToString());
|
||||
|
|
|
@ -436,7 +436,7 @@ bool GetTimestampIndex(unsigned int high, unsigned int low, bool fActiveOnly,
|
|||
|
||||
/** Functions for disk access for blocks */
|
||||
bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart);
|
||||
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
|
||||
bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, int nHeight, const Consensus::Params& consensusParams);
|
||||
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
|
||||
|
||||
/** Functions for validating blocks and updating the block tree */
|
||||
|
@ -454,7 +454,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state,
|
|||
* By "context", we mean only the previous block headers, but not the UTXO
|
||||
* set; UTXO-related validity checks are done in ConnectBlock(). */
|
||||
bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state,
|
||||
const CChainParams& chainparams, CBlockIndex *pindexPrev);
|
||||
const CChainParams& chainparams, CBlockIndex *pindexPrev,
|
||||
bool fCheckPOW = true);
|
||||
bool ContextualCheckBlock(const CBlock& block, CValidationState& state,
|
||||
const CChainParams& chainparams, CBlockIndex *pindexPrev);
|
||||
|
||||
|
|
|
@ -88,8 +88,8 @@ static inline size_t DynamicUsage(const std::set<X>& s)
|
|||
return MallocUsage(sizeof(stl_tree_node<X>)) * s.size();
|
||||
}
|
||||
|
||||
template<typename X, typename Y>
|
||||
static inline size_t DynamicUsage(const std::map<X, Y>& m)
|
||||
template<typename X, typename Y, typename C>
|
||||
static inline size_t DynamicUsage(const std::map<X, Y, C>& m)
|
||||
{
|
||||
return MallocUsage(sizeof(stl_tree_node<std::pair<const X, Y> >)) * m.size();
|
||||
}
|
||||
|
|
|
@ -520,7 +520,13 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre
|
|||
|
||||
// Fill in header
|
||||
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
||||
pblock->hashFinalSaplingRoot = sapling_tree.root();
|
||||
if (IsActivationHeight(nHeight, chainparams.GetConsensus(), Consensus::UPGRADE_HEARTWOOD)) {
|
||||
pblock->hashLightClientRoot.SetNull();
|
||||
} else if (chainparams.GetConsensus().NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
pblock->hashLightClientRoot = view.GetHistoryRoot(consensusBranchId);
|
||||
} else {
|
||||
pblock->hashLightClientRoot = sapling_tree.root();
|
||||
}
|
||||
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
|
||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
|
||||
pblock->nSolution.clear();
|
||||
|
|
|
@ -59,8 +59,11 @@ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ;
|
|||
static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
|
||||
/** The maximum number of peer connections to maintain. */
|
||||
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
|
||||
/** The period before a network upgrade activates, where connections to upgrading peers are preferred (in blocks). */
|
||||
static const int NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD = 24 * 24 * 3;
|
||||
/**
|
||||
* The period before a network upgrade activates, where connections to upgrading peers are preferred (in blocks).
|
||||
* This was three days for upgrades up to and including Blossom, and is 1.5 days from Heartwood onward.
|
||||
*/
|
||||
static const int NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD = 1728;
|
||||
|
||||
static const bool DEFAULT_FORCEDNSSEED = false;
|
||||
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
|
||||
|
|
14
src/pow.cpp
14
src/pow.cpp
|
@ -13,6 +13,7 @@
|
|||
#include "streams.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include <librustzcash.h>
|
||||
#include "sodium.h"
|
||||
|
||||
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
|
||||
|
@ -92,7 +93,7 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
|
|||
return bnNew.GetCompact();
|
||||
}
|
||||
|
||||
bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params& params)
|
||||
bool CheckEquihashSolution(const CBlockHeader *pblock, int nHeight, const Consensus::Params& params)
|
||||
{
|
||||
unsigned int n = params.nEquihashN;
|
||||
unsigned int k = params.nEquihashK;
|
||||
|
@ -106,6 +107,17 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params&
|
|||
// I||V
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << I;
|
||||
|
||||
// From Heartwood activation, check with the Rust validator
|
||||
if (params.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
return librustzcash_eh_isvalid(
|
||||
n, k,
|
||||
(unsigned char*)&ss[0], ss.size(),
|
||||
pblock->nNonce.begin(), pblock->nNonce.size(),
|
||||
pblock->nSolution.data(), pblock->nSolution.size());
|
||||
}
|
||||
|
||||
// Before Heartwood activation, check with the C++ validator
|
||||
ss << pblock->nNonce;
|
||||
|
||||
// H(I||V||...
|
||||
|
|
|
@ -23,7 +23,7 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
|
|||
int nextHeight);
|
||||
|
||||
/** Check whether the Equihash solution in a block header is valid */
|
||||
bool CheckEquihashSolution(const CBlockHeader *pblock, const Consensus::Params&);
|
||||
bool CheckEquihashSolution(const CBlockHeader *pblock, int nHeight, const Consensus::Params&);
|
||||
|
||||
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
|
||||
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
|
||||
|
|
|
@ -112,12 +112,12 @@ uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMer
|
|||
std::string CBlock::ToString() const
|
||||
{
|
||||
std::stringstream s;
|
||||
s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, hashFinalSaplingRoot=%s, nTime=%u, nBits=%08x, nNonce=%s, vtx=%u)\n",
|
||||
s << strprintf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, hashLightClientRoot=%s, nTime=%u, nBits=%08x, nNonce=%s, vtx=%u)\n",
|
||||
GetHash().ToString(),
|
||||
nVersion,
|
||||
hashPrevBlock.ToString(),
|
||||
hashMerkleRoot.ToString(),
|
||||
hashFinalSaplingRoot.ToString(),
|
||||
hashLightClientRoot.ToString(),
|
||||
nTime, nBits, nNonce.ToString(),
|
||||
vtx.size());
|
||||
for (unsigned int i = 0; i < vtx.size(); i++)
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
int32_t nVersion;
|
||||
uint256 hashPrevBlock;
|
||||
uint256 hashMerkleRoot;
|
||||
uint256 hashFinalSaplingRoot;
|
||||
uint256 hashLightClientRoot;
|
||||
uint32_t nTime;
|
||||
uint32_t nBits;
|
||||
uint256 nNonce;
|
||||
|
@ -44,7 +44,7 @@ public:
|
|||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(hashLightClientRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
|
@ -56,7 +56,7 @@ public:
|
|||
nVersion = CBlockHeader::CURRENT_VERSION;
|
||||
hashPrevBlock.SetNull();
|
||||
hashMerkleRoot.SetNull();
|
||||
hashFinalSaplingRoot.SetNull();
|
||||
hashLightClientRoot.SetNull();
|
||||
nTime = 0;
|
||||
nBits = 0;
|
||||
nNonce = uint256();
|
||||
|
@ -118,7 +118,7 @@ public:
|
|||
block.nVersion = nVersion;
|
||||
block.hashPrevBlock = hashPrevBlock;
|
||||
block.hashMerkleRoot = hashMerkleRoot;
|
||||
block.hashFinalSaplingRoot = hashFinalSaplingRoot;
|
||||
block.hashLightClientRoot = hashLightClientRoot;
|
||||
block.nTime = nTime;
|
||||
block.nBits = nBits;
|
||||
block.nNonce = nNonce;
|
||||
|
@ -158,7 +158,7 @@ public:
|
|||
READWRITE(this->nVersion);
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(hashFinalSaplingRoot);
|
||||
READWRITE(hashLightClientRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
}
|
||||
|
|
|
@ -229,7 +229,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
|
|||
result.push_back(Pair("height", blockindex->nHeight));
|
||||
result.push_back(Pair("version", block.nVersion));
|
||||
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
|
||||
result.push_back(Pair("finalsaplingroot", block.hashFinalSaplingRoot.GetHex()));
|
||||
result.push_back(Pair("finalsaplingroot", blockindex->hashFinalSaplingRoot.GetHex()));
|
||||
result.push_back(Pair("chainhistoryroot", blockindex->hashChainHistoryRoot.GetHex()));
|
||||
UniValue txs(UniValue::VARR);
|
||||
BOOST_FOREACH(const CTransaction&tx, block.vtx)
|
||||
{
|
||||
|
|
|
@ -431,7 +431,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
|
|||
"{\n"
|
||||
" \"version\" : n, (numeric) The block version\n"
|
||||
" \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
|
||||
" \"finalsaplingroothash\" : \"xxxx\", (string) The hash of the final sapling root\n"
|
||||
" \"lightclientroothash\" : \"xxxx\", (string) The hash of the light client root field in the block header\n"
|
||||
" \"finalsaplingroothash\" : \"xxxx\", (string) (DEPRECATED) The hash of the light client root field in the block header\n"
|
||||
" \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
|
||||
" {\n"
|
||||
" \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n"
|
||||
|
@ -696,7 +697,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
|
|||
result.push_back(Pair("capabilities", aCaps));
|
||||
result.push_back(Pair("version", pblock->nVersion));
|
||||
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
|
||||
result.push_back(Pair("finalsaplingroothash", pblock->hashFinalSaplingRoot.GetHex()));
|
||||
result.push_back(Pair("lightclientroothash", pblock->hashLightClientRoot.GetHex()));
|
||||
// Deprecated; remove in a future release.
|
||||
result.push_back(Pair("finalsaplingroothash", pblock->hashLightClientRoot.GetHex()));
|
||||
result.push_back(Pair("transactions", transactions));
|
||||
if (coinbasetxn) {
|
||||
assert(txCoinbase.isObject());
|
||||
|
|
|
@ -3,6 +3,15 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
const int ENTRY_SERIALIZED_LENGTH = 180;
|
||||
typedef struct HistoryEntry {
|
||||
unsigned char bytes[ENTRY_SERIALIZED_LENGTH];
|
||||
} HistoryEntry;
|
||||
static_assert(
|
||||
sizeof(HistoryEntry) == ENTRY_SERIALIZED_LENGTH,
|
||||
"HistoryEntry struct is not the same size as the underlying byte array");
|
||||
static_assert(alignof(HistoryEntry) == 1, "HistoryEntry struct alignment is not 1");
|
||||
|
||||
extern "C" {
|
||||
#ifdef WIN32
|
||||
typedef uint16_t codeunit;
|
||||
|
@ -312,7 +321,7 @@ extern "C" {
|
|||
uint32_t cbranch,
|
||||
uint32_t t_len,
|
||||
const uint32_t *ni_ptr,
|
||||
const unsigned char *n_ptr,
|
||||
const HistoryEntry *n_ptr,
|
||||
size_t p_len,
|
||||
const unsigned char *nn_ptr,
|
||||
unsigned char *rt_ret,
|
||||
|
@ -323,7 +332,7 @@ extern "C" {
|
|||
uint32_t cbranch,
|
||||
uint32_t t_len,
|
||||
const uint32_t *ni_ptr,
|
||||
const unsigned char *n_ptr,
|
||||
const HistoryEntry *n_ptr,
|
||||
size_t p_len,
|
||||
size_t e_len,
|
||||
unsigned char *rt_ret
|
||||
|
|
|
@ -1257,7 +1257,7 @@ pub extern "system" fn librustzcash_mmr_append(
|
|||
.root_node()
|
||||
.expect("Just added, should resolve always; qed");
|
||||
unsafe {
|
||||
*rt_ret = root_node.data().subtree_commitment;
|
||||
*rt_ret = root_node.data().hash();
|
||||
|
||||
for (idx, next_buf) in slice::from_raw_parts_mut(buf_ret, return_count as usize)
|
||||
.iter_mut()
|
||||
|
@ -1310,7 +1310,7 @@ pub extern "system" fn librustzcash_mmr_delete(
|
|||
.root_node()
|
||||
.expect("Just generated without errors, root should be resolving")
|
||||
.data()
|
||||
.subtree_commitment;
|
||||
.hash();
|
||||
}
|
||||
|
||||
truncate_len
|
||||
|
|
|
@ -297,6 +297,14 @@ public:
|
|||
if(value == 0)
|
||||
return std::vector<unsigned char>();
|
||||
|
||||
if (value == INT64_MIN) {
|
||||
// The code below is buggy, and produces the "wrong" result for
|
||||
// INT64_MIN. To avoid undefined behavior while attempting to
|
||||
// negate a value of INT64_MIN, we intentionally return the result
|
||||
// that the code below would produce on an x86_64 system.
|
||||
return {0,0,0,0,0,0,0,128,128};
|
||||
}
|
||||
|
||||
std::vector<unsigned char> result;
|
||||
const bool neg = value < 0;
|
||||
uint64_t absvalue = neg ? -value : value;
|
||||
|
@ -326,11 +334,25 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
static int64_t set_vch(const std::vector<unsigned char>& vch)
|
||||
{
|
||||
if (vch.empty())
|
||||
return 0;
|
||||
|
||||
if (vch == std::vector<unsigned char>({0,0,0,0,0,0,0,128,128})) {
|
||||
// On an x86_64 system, the code below would actually decode the buggy
|
||||
// INT64_MIN encoding correctly. However in this case, it would be
|
||||
// performing left shifts of a signed type by 64, which has undefined
|
||||
// behavior.
|
||||
return INT64_MIN;
|
||||
}
|
||||
|
||||
// Guard against undefined behavior. INT64_MIN is the only allowed 9-byte encoding.
|
||||
if (vch.size() > 8) {
|
||||
throw scriptnum_error("script number overflow");
|
||||
}
|
||||
|
||||
int64_t result = 0;
|
||||
for (size_t i = 0; i != vch.size(); ++i)
|
||||
result |= static_cast<int64_t>(vch[i]) << 8*i;
|
||||
|
|
|
@ -170,7 +170,8 @@ public:
|
|||
CAnchorsSproutMap& mapSproutAnchors,
|
||||
CAnchorsSaplingMap& mapSaplingAnchors,
|
||||
CNullifiersMap& mapSproutNullifiers,
|
||||
CNullifiersMap& mapSaplingNullifiers)
|
||||
CNullifiersMap& mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMap)
|
||||
{
|
||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
||||
|
@ -214,7 +215,8 @@ public:
|
|||
memusage::DynamicUsage(cacheSproutAnchors) +
|
||||
memusage::DynamicUsage(cacheSaplingAnchors) +
|
||||
memusage::DynamicUsage(cacheSproutNullifiers) +
|
||||
memusage::DynamicUsage(cacheSaplingNullifiers);
|
||||
memusage::DynamicUsage(cacheSaplingNullifiers) +
|
||||
memusage::DynamicUsage(historyCacheMap);
|
||||
for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
|
||||
ret += it->second.coins.DynamicMemoryUsage();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include "sodium.h"
|
||||
|
||||
#include "librustzcash.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
@ -87,6 +89,9 @@ void TestEquihashSolvers(unsigned int n, unsigned int k, const std::string &I, c
|
|||
|
||||
void TestEquihashValidator(unsigned int n, unsigned int k, const std::string &I, const arith_uint256 &nonce, std::vector<uint32_t> soln, bool expected) {
|
||||
size_t cBitLen { n/(k+1) };
|
||||
auto minimal = GetMinimalFromIndices(soln, cBitLen);
|
||||
|
||||
// First test the C++ validator
|
||||
crypto_generichash_blake2b_state state;
|
||||
EhInitialiseState(n, k, state);
|
||||
uint256 V = ArithToUint256(nonce);
|
||||
|
@ -97,7 +102,15 @@ void TestEquihashValidator(unsigned int n, unsigned int k, const std::string &I,
|
|||
PrintSolution(strm, soln);
|
||||
BOOST_TEST_MESSAGE(strm.str());
|
||||
bool isValid;
|
||||
EhIsValidSolution(n, k, state, GetMinimalFromIndices(soln, cBitLen), isValid);
|
||||
EhIsValidSolution(n, k, state, minimal, isValid);
|
||||
BOOST_CHECK(isValid == expected);
|
||||
|
||||
// The Rust validator should have the exact same result
|
||||
isValid = librustzcash_eh_isvalid(
|
||||
n, k,
|
||||
(unsigned char*)&I[0], I.size(),
|
||||
V.begin(), V.size(),
|
||||
minimal.data(), minimal.size());
|
||||
BOOST_CHECK(isValid == expected);
|
||||
}
|
||||
|
||||
|
@ -219,6 +232,11 @@ BOOST_AUTO_TEST_CASE(validator_allbitsmatter) {
|
|||
bool isValid;
|
||||
EhIsValidSolution(n, k, state, sol_char, isValid);
|
||||
BOOST_CHECK(isValid == true);
|
||||
BOOST_CHECK(librustzcash_eh_isvalid(
|
||||
n, k,
|
||||
(unsigned char*)&I[0], I.size(),
|
||||
V.begin(), V.size(),
|
||||
sol_char.data(), sol_char.size()));
|
||||
|
||||
// Changing any single bit of the encoded solution should make it invalid.
|
||||
for (size_t i = 0; i < sol_char.size() * 8; i++) {
|
||||
|
@ -226,6 +244,11 @@ BOOST_AUTO_TEST_CASE(validator_allbitsmatter) {
|
|||
mutated.at(i/8) ^= (1 << (i % 8));
|
||||
EhIsValidSolution(n, k, state, mutated, isValid);
|
||||
BOOST_CHECK(isValid == false);
|
||||
BOOST_CHECK(!librustzcash_eh_isvalid(
|
||||
n, k,
|
||||
(unsigned char*)&I[0], I.size(),
|
||||
V.begin(), V.size(),
|
||||
mutated.data(), mutated.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -273,8 +273,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
}
|
||||
*/
|
||||
|
||||
// These tests assume null hashFinalSaplingRoot (before Sapling)
|
||||
pblock->hashFinalSaplingRoot = uint256();
|
||||
// These tests assume null hashLightClientRoot (before Sapling)
|
||||
pblock->hashLightClientRoot = uint256();
|
||||
|
||||
CValidationState state;
|
||||
BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL));
|
||||
|
|
|
@ -196,4 +196,23 @@ BOOST_AUTO_TEST_CASE(operators)
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(intmin)
|
||||
{
|
||||
// INT64_MIN encodes to the buggy encoding.
|
||||
const CScriptNum sn(INT64_MIN);
|
||||
std::vector<unsigned char> buggy_int64_min_encoding = {0, 0, 0, 0, 0, 0, 0, 128, 128};
|
||||
BOOST_CHECK(sn.getvch() == buggy_int64_min_encoding);
|
||||
|
||||
// The buggy INT64_MIN encoding decodes correctly.
|
||||
const CScriptNum sn2(buggy_int64_min_encoding, true, 9);
|
||||
BOOST_CHECK(sn2 == INT64_MIN);
|
||||
BOOST_CHECK(sn2.getvch() == buggy_int64_min_encoding);
|
||||
// getint() saturates at the min/max value of the int type
|
||||
BOOST_CHECK((sn2.getint()) == std::numeric_limits<int>::min());
|
||||
|
||||
// Should throw for any other 9+ byte encoding.
|
||||
std::vector<unsigned char> invalid_nine_bytes = {0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
BOOST_CHECK_THROW (CScriptNum sn3(invalid_nine_bytes, false, 9), scriptnum_error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
88
src/txdb.cpp
88
src/txdb.cpp
|
@ -35,6 +35,10 @@ static const char DB_FLAG = 'F';
|
|||
static const char DB_REINDEX_FLAG = 'R';
|
||||
static const char DB_LAST_BLOCK = 'l';
|
||||
|
||||
static const char DB_MMR_LENGTH = 'M';
|
||||
static const char DB_MMR_NODE = 'm';
|
||||
static const char DB_MMR_ROOT = 'r';
|
||||
|
||||
// insightexplorer
|
||||
static const char DB_ADDRESSINDEX = 'd';
|
||||
static const char DB_ADDRESSUNSPENTINDEX = 'u';
|
||||
|
@ -124,6 +128,39 @@ uint256 CCoinsViewDB::GetBestAnchor(ShieldedType type) const {
|
|||
return hashBestAnchor;
|
||||
}
|
||||
|
||||
HistoryIndex CCoinsViewDB::GetHistoryLength(uint32_t epochId) const {
|
||||
HistoryIndex historyLength;
|
||||
if (!db.Read(make_pair(DB_MMR_LENGTH, epochId), historyLength)) {
|
||||
// Starting new history
|
||||
historyLength = 0;
|
||||
}
|
||||
|
||||
return historyLength;
|
||||
}
|
||||
|
||||
HistoryNode CCoinsViewDB::GetHistoryAt(uint32_t epochId, HistoryIndex index) const {
|
||||
HistoryNode mmrNode;
|
||||
|
||||
if (index >= GetHistoryLength(epochId)) {
|
||||
throw runtime_error("History data inconsistent - reindex?");
|
||||
}
|
||||
|
||||
if (!db.Read(make_pair(DB_MMR_NODE, make_pair(epochId, index)), mmrNode)) {
|
||||
throw runtime_error("History data inconsistent (expected node not found) - reindex?");
|
||||
}
|
||||
|
||||
return mmrNode;
|
||||
}
|
||||
|
||||
uint256 CCoinsViewDB::GetHistoryRoot(uint32_t epochId) const {
|
||||
uint256 root;
|
||||
if (!db.Read(make_pair(DB_MMR_ROOT, epochId), root))
|
||||
{
|
||||
root = uint256();
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
void BatchWriteNullifiers(CDBBatch& batch, CNullifiersMap& mapToUse, const char& dbChar)
|
||||
{
|
||||
for (CNullifiersMap::iterator it = mapToUse.begin(); it != mapToUse.end();) {
|
||||
|
@ -158,6 +195,29 @@ void BatchWriteAnchors(CDBBatch& batch, Map& mapToUse, const char& dbChar)
|
|||
}
|
||||
}
|
||||
|
||||
void BatchWriteHistory(CDBBatch& batch, CHistoryCacheMap& historyCacheMap) {
|
||||
for (auto nextHistoryCache = historyCacheMap.begin(); nextHistoryCache != historyCacheMap.end(); nextHistoryCache++) {
|
||||
auto historyCache = nextHistoryCache->second;
|
||||
auto epochId = nextHistoryCache->first;
|
||||
|
||||
// delete old entries since updateDepth
|
||||
for (int i = historyCache.updateDepth + 1; i <= historyCache.length; i++) {
|
||||
batch.Erase(make_pair(DB_MMR_NODE, make_pair(epochId, i)));
|
||||
}
|
||||
|
||||
// replace/append new/updated entries
|
||||
for (auto it = historyCache.appends.begin(); it != historyCache.appends.end(); it++) {
|
||||
batch.Write(make_pair(DB_MMR_NODE, make_pair(epochId, it->first)), it->second);
|
||||
}
|
||||
|
||||
// write new length
|
||||
batch.Write(make_pair(DB_MMR_LENGTH, epochId), historyCache.length);
|
||||
|
||||
// write current root
|
||||
batch.Write(make_pair(DB_MMR_ROOT, epochId), historyCache.root);
|
||||
}
|
||||
}
|
||||
|
||||
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashSproutAnchor,
|
||||
|
@ -165,7 +225,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
|
|||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers) {
|
||||
CNullifiersMap &mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMap) {
|
||||
CDBBatch batch(db);
|
||||
size_t count = 0;
|
||||
size_t changed = 0;
|
||||
|
@ -188,6 +249,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
|
|||
::BatchWriteNullifiers(batch, mapSproutNullifiers, DB_NULLIFIER);
|
||||
::BatchWriteNullifiers(batch, mapSaplingNullifiers, DB_SAPLING_NULLIFIER);
|
||||
|
||||
::BatchWriteHistory(batch, historyCacheMap);
|
||||
|
||||
if (!hashBlock.IsNull())
|
||||
batch.Write(DB_BEST_BLOCK, hashBlock);
|
||||
if (!hashSproutAnchor.IsNull())
|
||||
|
@ -456,7 +519,9 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
|
||||
bool CBlockTreeDB::LoadBlockIndexGuts(
|
||||
std::function<CBlockIndex*(const uint256&)> insertBlockIndex,
|
||||
const CChainParams& chainParams)
|
||||
{
|
||||
boost::scoped_ptr<CDBIterator> pcursor(NewIterator());
|
||||
|
||||
|
@ -479,7 +544,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)
|
|||
pindexNew->hashSproutAnchor = diskindex.hashSproutAnchor;
|
||||
pindexNew->nVersion = diskindex.nVersion;
|
||||
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
|
||||
pindexNew->hashFinalSaplingRoot = diskindex.hashFinalSaplingRoot;
|
||||
pindexNew->hashLightClientRoot = diskindex.hashLightClientRoot;
|
||||
pindexNew->nTime = diskindex.nTime;
|
||||
pindexNew->nBits = diskindex.nBits;
|
||||
pindexNew->nNonce = diskindex.nNonce;
|
||||
|
@ -489,6 +554,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)
|
|||
pindexNew->nTx = diskindex.nTx;
|
||||
pindexNew->nSproutValue = diskindex.nSproutValue;
|
||||
pindexNew->nSaplingValue = diskindex.nSaplingValue;
|
||||
pindexNew->hashFinalSaplingRoot = diskindex.hashFinalSaplingRoot;
|
||||
pindexNew->hashChainHistoryRoot = diskindex.hashChainHistoryRoot;
|
||||
|
||||
// Consistency checks
|
||||
auto header = pindexNew->GetBlockHeader();
|
||||
|
@ -498,6 +565,21 @@ bool CBlockTreeDB::LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)
|
|||
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus()))
|
||||
return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());
|
||||
|
||||
// ZIP 221 consistency checks
|
||||
if (chainParams.GetConsensus().NetworkUpgradeActive(pindexNew->nHeight, Consensus::UPGRADE_HEARTWOOD)) {
|
||||
if (pindexNew->hashLightClientRoot != pindexNew->hashChainHistoryRoot) {
|
||||
return error(
|
||||
"LoadBlockIndex(): block index inconsistency detected (hashLightClientRoot != hashChainHistoryRoot): %s",
|
||||
pindexNew->ToString());
|
||||
}
|
||||
} else {
|
||||
if (pindexNew->hashLightClientRoot != pindexNew->hashFinalSaplingRoot) {
|
||||
return error(
|
||||
"LoadBlockIndex(): block index inconsistency detected (hashLightClientRoot != hashFinalSaplingRoot): %s",
|
||||
pindexNew->ToString());
|
||||
}
|
||||
}
|
||||
|
||||
pcursor->Next();
|
||||
} else {
|
||||
return error("LoadBlockIndex() : failed to read value");
|
||||
|
|
13
src/txdb.h
13
src/txdb.h
|
@ -15,6 +15,9 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
#include "zcash/History.hpp"
|
||||
|
||||
class CBlockIndex;
|
||||
|
||||
// START insightexplorer
|
||||
|
@ -85,6 +88,9 @@ public:
|
|||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
uint256 GetBestAnchor(ShieldedType type) const;
|
||||
HistoryIndex GetHistoryLength(uint32_t epochId) const;
|
||||
HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const;
|
||||
uint256 GetHistoryRoot(uint32_t epochId) const;
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashSproutAnchor,
|
||||
|
@ -92,7 +98,8 @@ public:
|
|||
CAnchorsSproutMap &mapSproutAnchors,
|
||||
CAnchorsSaplingMap &mapSaplingAnchors,
|
||||
CNullifiersMap &mapSproutNullifiers,
|
||||
CNullifiersMap &mapSaplingNullifiers);
|
||||
CNullifiersMap &mapSaplingNullifiers,
|
||||
CHistoryCacheMap &historyCacheMap);
|
||||
bool GetStats(CCoinsStats &stats) const;
|
||||
};
|
||||
|
||||
|
@ -132,7 +139,9 @@ public:
|
|||
|
||||
bool WriteFlag(const std::string &name, bool fValue);
|
||||
bool ReadFlag(const std::string &name, bool &fValue);
|
||||
bool LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
|
||||
bool LoadBlockIndexGuts(
|
||||
std::function<CBlockIndex*(const uint256&)> insertBlockIndex,
|
||||
const CChainParams& chainParams);
|
||||
};
|
||||
|
||||
#endif // BITCOIN_TXDB_H
|
||||
|
|
|
@ -790,8 +790,37 @@ bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const {
|
|||
|
||||
size_t CTxMemPool::DynamicMemoryUsage() const {
|
||||
LOCK(cs);
|
||||
// Estimate the overhead of mapTx to be 6 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
|
||||
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 6 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + cachedInnerUsage;
|
||||
|
||||
size_t total = 0;
|
||||
|
||||
// Estimate the overhead of mapTx to be 6 pointers + an allocation, as no exact formula for
|
||||
// boost::multi_index_contained is implemented.
|
||||
total += memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 6 * sizeof(void*)) * mapTx.size();
|
||||
|
||||
// Two metadata maps inherited from Bitcoin Core
|
||||
total += memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas);
|
||||
|
||||
// Saves iterating over the full map
|
||||
total += cachedInnerUsage;
|
||||
|
||||
// Wallet notification
|
||||
total += memusage::DynamicUsage(mapRecentlyAddedTx);
|
||||
|
||||
// Nullifier set tracking
|
||||
total += memusage::DynamicUsage(mapSproutNullifiers) + memusage::DynamicUsage(mapSaplingNullifiers);
|
||||
|
||||
// DoS mitigation
|
||||
total += memusage::DynamicUsage(recentlyEvicted) + memusage::DynamicUsage(weightedTxTree);
|
||||
|
||||
// Insight-related structures
|
||||
size_t insight;
|
||||
insight += memusage::DynamicUsage(mapAddress);
|
||||
insight += memusage::DynamicUsage(mapAddressInserted);
|
||||
insight += memusage::DynamicUsage(mapSpent);
|
||||
insight += memusage::DynamicUsage(mapSpentInserted);
|
||||
total += insight;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
void CTxMemPool::SetMempoolCostLimit(int64_t totalCostLimit, int64_t evictionMemorySeconds) {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* network protocol versioning
|
||||
*/
|
||||
|
||||
static const int PROTOCOL_VERSION = 170009;
|
||||
static const int PROTOCOL_VERSION = 170010;
|
||||
|
||||
//! initial proto version, to be increased after version/verack negotiation
|
||||
static const int INIT_PROTO_VERSION = 209;
|
||||
|
|
|
@ -185,7 +185,7 @@ TEST(WalletTests, FindUnspentSproutNotes) {
|
|||
auto consensusParams = RegtestActivateSapling();
|
||||
|
||||
CWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
auto sk = libzcash::SproutSpendingKey::random();
|
||||
wallet.AddSproutSpendingKey(sk);
|
||||
|
||||
|
@ -643,7 +643,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
|
|||
auto consensusParams = RegtestActivateSapling();
|
||||
|
||||
TestWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
|
||||
// Generate Sapling address
|
||||
auto sk = GetTestMasterSaplingSpendingKey();
|
||||
|
@ -759,7 +759,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
|
|||
|
||||
TEST(WalletTests, SproutNullifierIsSpent) {
|
||||
CWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
|
||||
auto sk = libzcash::SproutSpendingKey::random();
|
||||
wallet.AddSproutSpendingKey(sk);
|
||||
|
@ -802,7 +802,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) {
|
|||
auto consensusParams = RegtestActivateSapling();
|
||||
|
||||
TestWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
|
||||
// Generate dummy Sapling address
|
||||
auto sk = GetTestMasterSaplingSpendingKey();
|
||||
|
@ -887,7 +887,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
|
|||
auto consensusParams = RegtestActivateSapling();
|
||||
|
||||
TestWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
|
||||
// Generate dummy Sapling address
|
||||
auto sk = GetTestMasterSaplingSpendingKey();
|
||||
|
@ -1010,7 +1010,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
|
|||
auto consensusParams = RegtestActivateSapling();
|
||||
|
||||
TestWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
|
||||
// Generate Sapling address
|
||||
auto sk = GetTestMasterSaplingSpendingKey();
|
||||
|
@ -1801,7 +1801,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
|
|||
auto consensusParams = RegtestActivateSapling();
|
||||
|
||||
TestWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
|
||||
auto m = GetTestMasterSaplingSpendingKey();
|
||||
|
||||
|
@ -1944,7 +1944,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
|
|||
auto consensusParams = RegtestActivateSapling();
|
||||
|
||||
TestWallet wallet;
|
||||
LOCK(wallet.cs_wallet);
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
|
||||
// Generate Sapling address
|
||||
auto sk = GetTestMasterSaplingSpendingKey();
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
#include "zcash/History.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
#include "uint256.h"
|
||||
#include "librustzcash.h"
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
void HistoryCache::Extend(const HistoryNode &leaf) {
|
||||
appends[length++] = leaf;
|
||||
}
|
||||
|
||||
void HistoryCache::Truncate(HistoryIndex newLength) {
|
||||
// Remove any to-be-appended nodes beyond the new length. The array representation is
|
||||
// zero-indexed, and HistoryIndex is unsigned, so we handle the truncate-to-zero case
|
||||
// separately.
|
||||
if (newLength > 0) {
|
||||
for (HistoryIndex idx = length; idx >= newLength; idx--) {
|
||||
appends.erase(idx);
|
||||
}
|
||||
} else {
|
||||
appends.clear();
|
||||
}
|
||||
|
||||
length = newLength;
|
||||
// we track how deep updates go back in the tree, so we could later
|
||||
// update everything starting from `updateDepth`
|
||||
//
|
||||
// imagine we rolled two blocks back and then put another 3 blocks on top
|
||||
// of the rolled back state. In that case `updateDepth` will be H-3, while length
|
||||
// will be H (where H is a final chain height after such operation). So we know that
|
||||
// history entries in the range of H-3..H are expected to be pushed into the database
|
||||
// to replace/append to the persistent nodes there.
|
||||
if (updateDepth > length) updateDepth = length;
|
||||
}
|
||||
|
||||
HistoryNode NewNode(
|
||||
uint256 subtreeCommitment,
|
||||
uint32_t startTime,
|
||||
uint32_t endTime,
|
||||
uint32_t startTarget,
|
||||
uint32_t endTarget,
|
||||
uint256 startSaplingRoot,
|
||||
uint256 endSaplingRoot,
|
||||
uint256 subtreeTotalWork,
|
||||
uint64_t startHeight,
|
||||
uint64_t endHeight,
|
||||
uint64_t saplingTxCount
|
||||
)
|
||||
{
|
||||
CDataStream buf(SER_DISK, 0);
|
||||
HistoryNode result;
|
||||
|
||||
buf << subtreeCommitment;
|
||||
buf << startTime;
|
||||
buf << endTime;
|
||||
buf << startTarget;
|
||||
buf << endTarget;
|
||||
buf << startSaplingRoot;
|
||||
buf << endSaplingRoot;
|
||||
buf << subtreeTotalWork;
|
||||
buf << COMPACTSIZE(startHeight);
|
||||
buf << COMPACTSIZE(endHeight);
|
||||
buf << COMPACTSIZE(saplingTxCount);
|
||||
|
||||
std::copy(buf.begin(), buf.end(), result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
HistoryNode NewLeaf(
|
||||
uint256 commitment,
|
||||
uint32_t time,
|
||||
uint32_t target,
|
||||
uint256 saplingRoot,
|
||||
uint256 totalWork,
|
||||
uint64_t height,
|
||||
uint64_t saplingTxCount
|
||||
) {
|
||||
return NewNode(
|
||||
commitment,
|
||||
time,
|
||||
time,
|
||||
target,
|
||||
target,
|
||||
saplingRoot,
|
||||
saplingRoot,
|
||||
totalWork,
|
||||
height,
|
||||
height,
|
||||
saplingTxCount
|
||||
);
|
||||
}
|
||||
|
||||
HistoryEntry NodeToEntry(const HistoryNode node, uint32_t left, uint32_t right) {
|
||||
CDataStream buf(SER_DISK, 0);
|
||||
HistoryEntry result;
|
||||
|
||||
uint8_t code = 0;
|
||||
buf << code;
|
||||
buf << left;
|
||||
buf << right;
|
||||
buf << node;
|
||||
|
||||
assert(buf.size() <= ENTRY_SERIALIZED_LENGTH);
|
||||
std::copy(std::begin(buf), std::end(buf), result.bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
HistoryEntry LeafToEntry(const HistoryNode node) {
|
||||
CDataStream buf(SER_DISK, 0);
|
||||
HistoryEntry result;
|
||||
|
||||
uint8_t code = 1;
|
||||
buf << code;
|
||||
buf << node;
|
||||
|
||||
assert(buf.size() <= ENTRY_SERIALIZED_LENGTH);
|
||||
std::copy(std::begin(buf), std::end(buf), result.bytes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
#ifndef ZC_HISTORY_H_
|
||||
#define ZC_HISTORY_H_
|
||||
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "serialize.h"
|
||||
#include "streams.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include "librustzcash.h"
|
||||
|
||||
namespace libzcash {
|
||||
|
||||
const int NODE_SERIALIZED_LENGTH = 171;
|
||||
|
||||
typedef std::array<unsigned char, NODE_SERIALIZED_LENGTH> HistoryNode;
|
||||
|
||||
typedef uint64_t HistoryIndex;
|
||||
|
||||
class HistoryCache {
|
||||
public:
|
||||
// updates to the persistent(db) layer
|
||||
std::unordered_map<HistoryIndex, HistoryNode> appends;
|
||||
// current length of the history
|
||||
HistoryIndex length;
|
||||
// how much back into the old state current update state
|
||||
// goes
|
||||
HistoryIndex updateDepth;
|
||||
// current root of the history
|
||||
uint256 root;
|
||||
// current epoch of this history state
|
||||
uint32_t epoch;
|
||||
|
||||
HistoryCache(HistoryIndex initialLength, uint256 initialRoot, uint32_t initialEpoch) :
|
||||
length(initialLength), updateDepth(initialLength), root(initialRoot), epoch(initialEpoch) { };
|
||||
|
||||
HistoryCache() { }
|
||||
|
||||
// Extends current history update by one history node.
|
||||
void Extend(const HistoryNode &leaf);
|
||||
|
||||
// Truncates current history to the new length.
|
||||
void Truncate(HistoryIndex newLength);
|
||||
};
|
||||
|
||||
// New history node with metadata based on block state.
|
||||
HistoryNode NewLeaf(
|
||||
uint256 commitment,
|
||||
uint32_t time,
|
||||
uint32_t target,
|
||||
uint256 saplingRoot,
|
||||
uint256 totalWork,
|
||||
uint64_t height,
|
||||
uint64_t saplingTxCount
|
||||
);
|
||||
|
||||
// Convert history node to tree node (with children references)
|
||||
HistoryEntry NodeToEntry(const HistoryNode node, uint32_t left, uint32_t right);
|
||||
|
||||
// Convert history node to leaf node (end nodes without children)
|
||||
HistoryEntry LeafToEntry(const HistoryNode node);
|
||||
|
||||
}
|
||||
|
||||
typedef libzcash::HistoryCache HistoryCache;
|
||||
typedef libzcash::HistoryIndex HistoryIndex;
|
||||
typedef libzcash::HistoryNode HistoryNode;
|
||||
|
||||
#endif /* ZC_HISTORY_H_ */
|
|
@ -204,7 +204,7 @@ double benchmark_verify_equihash()
|
|||
CBlockHeader genesis_header = genesis.GetBlockHeader();
|
||||
struct timeval tv_start;
|
||||
timer_start(tv_start);
|
||||
CheckEquihashSolution(&genesis_header, params.GetConsensus());
|
||||
assert(CheckEquihashSolution(&genesis_header, 1, params.GetConsensus()));
|
||||
return timer_stop(tv_start);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2020 The Zcash developers
|
||||
|
||||
rm -f src/Makefile
|
||||
rm -f src/Makefile.in
|
||||
rm -f doc/man/Makefile
|
||||
rm -f doc/man/Makefile.in
|
||||
|
||||
rm -f .cargo/config
|
||||
rm -f .cargo/.configured-for-online
|
||||
rm -f .cargo/.configured-for-offline
|
||||
|
||||
rm -f src/config/stamp-h1
|
||||
rm -f src/config/bitcoin-config.h
|
||||
rm -f src/obj/build.h
|
||||
rm -f src/leveldb/build_config.mk
|
||||
|
||||
rm -f src/test/buildenv.py
|
||||
rm -f src/test/data/*.json.h
|
||||
rm -f src/test/data/*.raw.h
|
||||
|
||||
rm -f qa/pull-tester/run-bitcoind-for-test.sh
|
||||
rm -f qa/pull-tester/tests-config.sh
|
||||
|
||||
rm -f src/fuzz.cpp
|
||||
|
||||
rm -rf test_bitcoin.coverage/ zcash-gtest.coverage/ total.coverage/
|
||||
|
||||
rm -rf cache
|
||||
rm -rf target
|
||||
rm -rf depends/work
|
||||
|
||||
find src -type f -and \( -name '*.Po' -or -name '*.Plo' -or -name '*.o' -or -name '*.a' -or -name '*.la' -or -name '*.lo' -or -name '*.lai' -or -name '*.pc' -or -name '.dirstamp' -or -name '*.gcda' -or -name '*.gcno' -or -name '*.sage.py' -or -name '*.trs' \) -delete
|
||||
|
||||
clean_dirs()
|
||||
{
|
||||
find . -depth -path "*/$1/*" -delete
|
||||
find . -type d -name "$1" -delete
|
||||
}
|
||||
|
||||
clean_exe()
|
||||
{
|
||||
rm -f "$1" "$1.exe"
|
||||
}
|
||||
|
||||
clean_dep()
|
||||
{
|
||||
rm -rf "$1/autom4te.cache"
|
||||
rm -f "$1/build-aux/compile"
|
||||
rm -f "$1/build-aux/config.guess"
|
||||
rm -f "$1/build-aux/config.sub"
|
||||
rm -f "$1/build-aux/depcomp"
|
||||
rm -f "$1/build-aux/install-sh"
|
||||
rm -f "$1/build-aux/ltmain.sh"
|
||||
rm -f "$1/build-aux/missing"
|
||||
rm -f "$1/build-aux/test-driver"
|
||||
rm -f "$1/build-aux/m4/libtool.m4"
|
||||
rm -f "$1/build-aux/m4/lt~obsolete.m4"
|
||||
rm -f "$1/build-aux/m4/ltoptions.m4"
|
||||
rm -f "$1/build-aux/m4/ltsugar.m4"
|
||||
rm -f "$1/build-aux/m4/ltversion.m4"
|
||||
rm -f "$1/aclocal.m4"
|
||||
rm -f "$1/config.log"
|
||||
rm -f "$1/config.status"
|
||||
rm -f "$1/gen_context"
|
||||
rm -f "$1/configure"
|
||||
rm -f "$1/libtool"
|
||||
rm -f "$1/Makefile"
|
||||
rm -f "$1/Makefile.in"
|
||||
rm -f "$1/$2"
|
||||
rm -f "$1/$2~"
|
||||
}
|
||||
|
||||
clean_dirs .deps
|
||||
clean_dirs .libs
|
||||
clean_dirs __pycache__
|
||||
|
||||
clean_exe src/bench/bench_bitcoin
|
||||
clean_exe src/zcash-cli
|
||||
clean_exe src/zcashd
|
||||
clean_exe src/zcash-gtest
|
||||
clean_exe src/zcash-tx
|
||||
clean_exe src/test/test_bitcoin
|
||||
|
||||
clean_exe src/leveldb/db_bench
|
||||
clean_exe src/leveldb/leveldbutil
|
||||
rm -f src/leveldb/*_test src/leveldb/*_test.exe
|
||||
rm -f src/leveldb/*.so src/leveldb/*.so.*
|
||||
|
||||
clean_dep . src/config/bitcoin-config.h.in
|
||||
|
||||
clean_dep src/secp256k1 src/libsecp256k1-config.h.in
|
||||
rm -f src/secp256k1/src/ecmult_static_context.h
|
||||
rm -f src/secp256k1/src/libsecp256k1-config.h
|
||||
rm -f src/secp256k1/src/stamp-h1
|
||||
rm -f src/secp256k1/.so_locations
|
||||
clean_exe src/secp256k1/tests
|
||||
clean_exe src/secp256k1/exhaustive_tests
|
||||
rm -f src/secp256k1/tests.log src/secp256k1/exhaustive-tests.log src/secp256k1/test-suite.log
|
||||
|
||||
clean_dep src/univalue univalue-config.h.in
|
||||
rm -f src/univalue/univalue-config.h
|
||||
rm -f src/univalue/stamp-h1
|
||||
clean_exe src/univalue/test_json
|
||||
clean_exe src/univalue/unitester
|
||||
clean_exe src/univalue/no_nul
|
||||
rm -f src/univalue/test/*.log
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2020 The Zcash developers
|
||||
|
||||
zcutil/clean.sh
|
||||
|
||||
rm -rf depends/*-*-*
|
||||
rm -rf depends/built
|
||||
rm -rf depends/sources
|
||||
|
||||
rm -rf afl-temp
|
||||
rm -rf src/fuzzing/*/output
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#! /usr/bin/env python2
|
||||
#! /usr/bin/env python3
|
||||
|
||||
import os
|
||||
import re
|
||||
|
@ -9,7 +9,7 @@ import subprocess
|
|||
import traceback
|
||||
import unittest
|
||||
import random
|
||||
from cStringIO import StringIO
|
||||
from io import StringIO
|
||||
from functools import wraps
|
||||
|
||||
|
||||
|
@ -77,6 +77,11 @@ def parse_args(args):
|
|||
|
||||
# Top-level flow:
|
||||
def main_logged(release, releaseprev, releasefrom, releaseheight, hotfix):
|
||||
verify_dependencies([
|
||||
('help2man', None),
|
||||
('debchange', 'devscripts'),
|
||||
])
|
||||
|
||||
verify_tags(releaseprev, releasefrom)
|
||||
verify_version(release, releaseprev, hotfix)
|
||||
initialize_git(release, hotfix)
|
||||
|
@ -107,6 +112,20 @@ def phase(message):
|
|||
return deco
|
||||
|
||||
|
||||
@phase('Checking release script dependencies.')
|
||||
def verify_dependencies(dependencies):
|
||||
for (dependency, pkg) in dependencies:
|
||||
try:
|
||||
sh_log(dependency, '--version')
|
||||
except OSError:
|
||||
raise SystemExit(
|
||||
"Missing dependency {}{}".format(
|
||||
dependency,
|
||||
" (part of {} Debian package)".format(pkg) if pkg else "",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@phase('Checking tags.')
|
||||
def verify_tags(releaseprev, releasefrom):
|
||||
candidates = []
|
||||
|
@ -403,7 +422,7 @@ def initialize_logging():
|
|||
|
||||
def sh_out(*args):
|
||||
logging.debug('Run (out): %r', args)
|
||||
return subprocess.check_output(args)
|
||||
return subprocess.check_output(args).decode()
|
||||
|
||||
|
||||
def sh_log(*args):
|
||||
|
@ -417,7 +436,7 @@ def sh_log(*args):
|
|||
|
||||
logging.debug('Run (log PID %r): %r', p.pid, args)
|
||||
for line in p.stdout:
|
||||
logging.debug('> %s', line.rstrip())
|
||||
logging.debug('> %s', line.decode().rstrip())
|
||||
status = p.wait()
|
||||
if status != 0:
|
||||
raise SystemExit('Nonzero exit status: {!r}'.format(status))
|
||||
|
@ -443,6 +462,7 @@ def sh_progress(markers, *args):
|
|||
pbar.update(marker)
|
||||
logging.debug('Run (log PID %r): %r', p.pid, args)
|
||||
for line in p.stdout:
|
||||
line = line.decode()
|
||||
logging.debug('> %s', line.rstrip())
|
||||
for idx, val in enumerate(markers[marker:]):
|
||||
if val in line:
|
||||
|
@ -557,6 +577,12 @@ class Version (object):
|
|||
self.hotfix,
|
||||
)
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._sort_tup() < other._sort_tup()
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._sort_tup() == other._sort_tup()
|
||||
|
||||
|
||||
class PathPatcher (object):
|
||||
def __init__(self, path):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import re, os, os.path
|
||||
|
@ -110,7 +111,7 @@ def generate_release_note(version, prev, clear):
|
|||
latest_tag = subprocess.Popen(['git describe --abbrev=0'], shell=True, stdout=subprocess.PIPE).communicate()[0].strip()
|
||||
print("Previous release tag: ", latest_tag)
|
||||
notes = subprocess.Popen(['git shortlog --no-merges {0}..HEAD'.format(latest_tag)], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[0]
|
||||
lines = notes.split('\n')
|
||||
lines = notes.decode().split('\n')
|
||||
lines = [alias_authors_in_release_notes(line) for line in lines]
|
||||
temp_release_note = os.path.join(doc_dir, 'release-notes.md')
|
||||
with open(temp_release_note, 'r') as f:
|
||||
|
|
Loading…
Reference in New Issue