Compare commits

...

288 Commits

Author SHA1 Message Date
Jack Grigg e5210d50c9 Set NU5 protocol version for regtest to 170040, bump protocol version
We need to bump the `zcashd` protocol version because the new rules are
not compatible with existing rules followed by 170015 nodes, but we
_also_ need to ensure we can still bump it again once we set the testnet
reactivation height (changing node network behaviour again). This commit
also enables RPC tests to run (because previously the nodes considered
each other to be too old for NU5 to be active, and were disconnecting).
2022-03-23 18:37:56 +00:00
Jack Grigg d9c1b05a83 builder: Use correct `PrecomputedTransactionData` for transparent sigs
Before merging 4.7.0-rc1 into the nu5-consensus branch, we were in a
split state:

- 4.7.0-rc1 included Orchard support in the transaction builder, which
  required special handling of Orchard bundles when computing sighashes.
  The `PrecomputedTransactionData` structure could be shared, because
  its digests were only relevant to transparent signatures (as shielded
  signatures signed the txid directly even in shielding transactions).

- nu5-consensus included the changes to ZIP 244, which required passing
  around a `PrecomputedTransactionData` that contained the set of all
  transparent inputs being spent, because shielding transactions now also
  need to commit to transparent inputs.

In the merge commit, we incorrectly handled the resolution: we correctly
derived a fresh `PrecomputedTransactionData` when signing the Orchard
bundle, but we reused the `PrecomputedTransactionData` that was
previously derived before checking whether or not we even had an Orchard
bundle, for transparent inputs. This meant that its commitments didn't
commit to the Orchard bundle, and so transparent signatures on
transactions with Orchard bundles would fail to verify.

Incidentally, this is the exact inverse of a bug we encounted while
implementing the ZIP 244 changes on the nu5-consensus branch: we were
correctly computing the transparent sighash, but we were relying on the
initial `TxDigests` derived within `PrecomputedTransactionData` for the
Orchard sighash, even though we were actively rewriting the transaction
to include the Orchard bundle. The fix there was similarly to re-compute
the `TxDigests` before computing the sighash.
2022-03-23 18:27:12 +00:00
Jack Grigg 85b5595519 Fix logical merge conflicts after merging 4.7.0-rc1
The merge commit includes changes to address direct merge conflicts.
This commit makes the remaining changes necessary to integrate the
Orchard wallet changes with the NU5 consensus changes.
2022-03-23 03:19:39 +00:00
Jack Grigg 9e9f58b26f Merge branch 'master' into unify-nu5-consensus-changes 2022-03-23 02:57:16 +00:00
str4d bd1feab2db
Merge pull request #5730 from superbaud/missing-wallet-orchard-h
Add missing wallet/orchard.h to src/Makefile.am and gtest/test_transaction_builder.h to Makefile.gtest.include
2022-03-23 02:11:56 +00:00
Jack Grigg 77a971fbc1 Migrate to latest `zcash/librustzcash` revision
This includes:
- `orchard =0.1.0-beta.3` which includes the final circuit changes.
- The new NU5 consensus branch ID.
- Updated ZIP 244 test vectors (which use the NU5 consensus branch ID).
2022-03-23 02:05:06 +00:00
sasha 5cfaea5dd4 Add missing gtest/test_transaction_builder.h to Makefile.gtest.include
Like 562f5add87, the missing header ref
means that the make dist step of gitian builds don't include it in the
tarball, causing an error upon build.
2022-03-22 17:38:32 -07:00
sasha c85fae833e Add missing wallet/orchard.h to src/Makefile.am
Like 562f5add87, the missing header ref
means that the make dist step of gitian builds don't include it in the
tarball, causing an error upon build.
2022-03-22 17:03:16 -07:00
str4d d38da460eb
Merge pull request #5728 from zcash/release-v4.7.0-rc1
Release v4.7.0-rc1
2022-03-22 22:46:08 +00:00
Jack Grigg 1f3c145e4a make-release.py: Updated release notes and changelog for 4.7.0-rc1. 2022-03-22 21:14:02 +00:00
Jack Grigg 5ea2aaff0d make-release.py: Updated manpages for 4.7.0-rc1. 2022-03-22 21:14:02 +00:00
Jack Grigg 96edba2e41 make-release.py: Versioning changes for 4.7.0-rc1. 2022-03-22 21:04:37 +00:00
str4d 584e8e56a8
Merge pull request #5726 from str4d/update-deps-4.7.0-rc1
Update dependencies for 4.7.0-rc1
2022-03-22 21:02:54 +00:00
Jack Grigg e646905f1b cargo update 2022-03-22 19:34:29 +00:00
Jack Grigg 479b10364b qa: Exclude `native_libtinfo` from dependency update checks
We are pinning a specific version to get Arch builds working.
2022-03-22 19:33:14 +00:00
Charlie O'Keefe 7b7ac148ac
Merge pull request #5681 from LarryRuane/2022-02-listaddresses
Update the listaddresses RPC endpoint to correctly report unified addresses
2022-03-22 12:55:41 -06:00
Jack Grigg c21890af8d wallet: Fix expected `listaddresses` sources in `rpc_wallet_tests` 2022-03-22 17:18:30 +00:00
Charlie O'Keefe b62d4917be
Merge pull request #5715 from nuttycom/z_viewtransaction_cleanup
z_viewtransaction cleanup following #5700
2022-03-22 09:16:11 -06:00
Jack Grigg d4e47e4720 wallet: Fix Sapling address bug in `listaddresses`
`CWallet::FindUnifiedAddressByReceiver` is a wrapper around the visitor
`UnifiedAddressForReceiver`, which looks up the Unified Address (if any)
corresponding to the given receiver. However, this only returns external
UAs, and returns `std::nullopt` both if the receiver does not correspond
to a UFVK, and if it does but is derived from an internal FVK. By using
this method as a filter, `listaddresses` was not filtering out internal
Sapling receivers, and thus was leaking Sapling change addresses for
accounts (which we do not want revealed in the UI anywhere).

Instead, we now check for UFVK metadata directly, which verifies that
the Sapling receiver is derived from a UFVK without any extra filtering.
2022-03-22 14:55:40 +00:00
Jack Grigg 24089109b2 wallet: Fix bugs in `listaddresses`
- Legacy transparent addresses derived from the mnemonic seed are no
  longer duplicated in the `legacy_random` source.
- Legacy transparent change addresses derived from the mnemonic seed are
  now shown under that source.
- Sapling addresses that aren't part of a UA are now identified
  correctly when derived from the mnemonic seed, rather than being
  assumed to be derived from the legacy HD seed.
2022-03-22 14:55:40 +00:00
Jack Grigg e576f6616f Fix bugs in wallet_addresses RPC test
- The legacy_random source should only contain Sprout addresses (as
  zcashd now only derives transparent addresses from the mnemonic seed).
- Sapling addresses should appear in the mnemonic_seed source, not the
  legacy_seed source (as zcashd no longer ever generates keys there).

The RPC test also now has several additional checks:

- `listaddresses` does not return duplicate addresses (an address can
  only come from a single source).
- Address sets are now explicitly checked (to ensure that there are no
  unexpected addresses under the checked sources).
2022-03-22 14:55:39 +00:00
Larry Ruane e575e0f217 update listaddresses RPC for UAs, Orchard
Co-authored-by: Kris Nuttycombe <kris@nutty.land>
2022-03-22 14:55:17 +00:00
Kris Nuttycombe 17e078f068 Documentation fix for UnifiedAddressForReciever (+ spelling corrections.) 2022-03-21 20:20:19 -06:00
Kris Nuttycombe 151074cbb1 Fix legacy address handling in CWallet::GetPaymentAddressForRecipient
Fix handling of legacy transparent change addresses & legacy Sapling addresses.
2022-03-21 20:03:08 -06:00
Kris Nuttycombe a8bae004b1 Lock cs_main for accesses to chainActive in GetPaymentAddressForRecipient. 2022-03-21 16:46:15 -06:00
Kris Nuttycombe 758852b281 Return failure rather than asserting on WriteRecipientMapping failure.
Addresses https://github.com/zcash/zcash/pull/5700#discussion_r830538438
2022-03-21 16:46:15 -06:00
Kris Nuttycombe 9220ebb0de Make CWallet::DefaultReceiverTypes height-dependent.
Addresses https://github.com/zcash/zcash/pull/5700#discussion_r830538538
2022-03-21 16:46:15 -06:00
Charlie O'Keefe 11bd9463c1
Merge pull request #5698 from charlieok/swap_stretch_for_bullseye
Remove stretch (debian 9), add bullseye (debian 11) in gitian descriptor
2022-03-21 09:10:25 -06:00
Kris Nuttycombe 87cc0d22db Add RecipientType to GetPaymentAddressForRecipient result.
This now returns whether or not the address corresponds to an
internal recipient, an external address for the wallet, or a
counterparty's address (an address not derived from any of
the wallet's keys) from GetPaymentAddressForRecipient.

Fixes #5708
2022-03-20 16:36:38 -06:00
Kris Nuttycombe 15bb06f452 Minor rename & documentation improvement.
Addresses https://github.com/zcash/zcash/pull/5700#discussion_r830395940
and https://github.com/zcash/zcash/pull/5700#discussion_r830396140
2022-03-20 12:30:37 -06:00
Charlie O'Keefe 8c0e76e12b Remove stretch (debian 9), add bullseye (debian 11) in gitian descriptor 2022-03-19 15:56:55 -06:00
Charlie O'Keefe 612f3e7e81
Merge pull request #5711 from nuttycom/5186-updates-to-z_viewtransaction-fixes
Uncomment addtional tests that depend on z_viewtransaction.
2022-03-19 15:53:39 -06:00
Kris Nuttycombe ba3594f9ee Uncomment addtional tests that depend on z_viewtransaction. 2022-03-19 12:11:27 -06:00
Charlie O'Keefe 9974be75fb
Merge pull request #5710 from zcash/feature/wallet_orchard
Add Orchard support to the zcashd wallet.
2022-03-19 10:25:57 -06:00
Kris Nuttycombe b32c8d3f0f Fix missing AllowRevealedSenders flag in test. 2022-03-18 21:41:38 -06:00
Kris Nuttycombe b73ca36c45 Merge remote-tracking branch 'upstream/master' into feature/wallet_orchard 2022-03-18 19:19:50 -06:00
Kris Nuttycombe 4392375404
Merge pull request #5709 from str4d/5673-keypath-info-in-backups
Include HD keypath info in `dumpwallet` and `z_exportwallet`
2022-03-18 17:22:35 -06:00
Kris Nuttycombe fabff30d88
Merge pull request #5700 from nuttycom/5186-updates-to-z_viewtransaction
Add Orchard and Unified Address support to z_viewtransaction
2022-03-18 17:19:31 -06:00
Kris Nuttycombe d724c81289
Merge pull request #5438 from sandakersmann/master
Update copyright year to 2022
2022-03-18 16:13:43 -06:00
Jack Grigg 78fea33cde wallet: Add seedfp to transparent keys in dumpwallet / z_exportwallet
Also refactors Sapling key export logic to match. Sapling key exports
already have hdkeypath and seedfp (in a different spot in the line).

Sprout key exports are unmodified, because we have removed the ability
to generate new Sprout keys, so there will never be HD Sprout keys.
2022-03-18 21:53:12 +00:00
Jonas Schnelli eaf8eb40f4 [Wallet] add HD xpriv to dumpwallet
Zcash: Only the refactor and addition of hdkeypath to transparent keys.

(cherry picked from commit bitcoin/bitcoin@77c912d21c)
2022-03-18 21:29:22 +00:00
Kris Nuttycombe a1bd440f2b Return the default unified address if we have the UFVK but no address metadata. 2022-03-18 12:00:44 -06:00
Kris Nuttycombe 4df6c028f4 Correctly report change outputs in z_viewtransaction.
One of the invariants that we want to hold for unified addresses
is that we never want change addresses to be visible to users.
This updates address metadata retrieval to also indicate whether
or not an address is associated with a UFVK's internal key,
and omits and flags change addresses in z_viewtransaction output.
2022-03-18 11:09:35 -06:00
Kris Nuttycombe 7c3afad111 Add debug printing for receivers and recipient addresses. 2022-03-18 11:08:04 -06:00
Kris Nuttycombe 2d673ca704 Update z_viewtransaction documentation to correctly represent field names. 2022-03-18 11:05:48 -06:00
Kris Nuttycombe 0c977076bc Apply suggestions from code review
Co-authored-by: str4d <thestr4d@gmail.com>
2022-03-18 08:34:27 -06:00
Steven Smith e1e480e9c4 Ensure z_viewtransaction returns Orchard details 2022-03-18 08:31:51 -06:00
Steven Smith 445f4de0f4 Add Orchard & unified address support to z_viewtransaction.
Fixes #5186
2022-03-18 08:31:48 -06:00
Kris Nuttycombe 28210791a1
Merge pull request #5693 from str4d/z_sendmany-privacy-policy
z_sendmany: Replace `allowRevealedAmount` with `privacyStrategy`
2022-03-18 08:09:43 -06:00
Kris Nuttycombe 315392ce3b Add OrchardWallet::GetTxActions
This method detects and returns metadata for any notes belonging to the
wallet that are spent in the transaction. It also trial-decrypts the
Orchard outputs of a transaction using the wallet's incoming viewing
keys along a set of OVKs provided by the caller, and returns metadata
about the notes that were successfully decrypted.
2022-03-18 08:03:16 -06:00
Steven Smith 9993d948ef Load previously persisted sent transaction recipient metadata back into the wallet.
This completes some unfinished work from #5549
2022-03-18 08:03:12 -06:00
Daira Hopwood 6f4b283a3d
Merge pull request #5701 from str4d/5697-gitian-fix
build: Add missing `util/match.h` to `src/Makefile.am`
2022-03-18 11:28:31 +00:00
Jack Grigg 562f5add87 build: Add missing `util/match.h` to `src/Makefile.am`
The missing header reference meant that Gitian builds didn't include the
file in its distribution step, causing subsequent builds to fail with a
'file not found' error.

Closes zcash/zcash#5697.
2022-03-18 02:54:26 +00:00
Jack Grigg 65cb79e023 z_sendmany: Expand `privacyPolicy` cases
We now have a full roster of privacy policies. We can graph the
relationships between the policies; arrows point from more-private to
less-private, and each policy is permitted to reveal information covered
by all policies pointing to it (in addition to the extra information it
is permitted to reveal).

                       FullPrivacy
                            v
                   AllowRevealedAmounts
                        v           v
    AllowRevealedRecipients   ---- AllowRevealedSenders
               v             /               v
     AllowFullyTransparent <-  AllowLinkingAccountAddresses
                        v       v
                        NoPrivacy

The synthetic `LegacyCompat` policy now uses the `AllowFullyTransparent`
policy for backwards compatibility, and the `FullPrivacy` policy if the
sender or recipients involve Unified Addresses.

Closes zcash/zcash#5677.
Closes zcash/zcash#5678.
2022-03-18 02:45:23 +00:00
Jack Grigg 1505121877 z_sendmany: Replace `allowRevealedAmount` with `privacyStrategy`
This replaces the bool argument with a string argument, enabling us to
add additional privacy strategies. We also alter the default to be
backwards-compatible with existing RPC method usage, by only enabling
the strongest checks if a Unified Address is involved.

Closes zcash/zcash#5676.
2022-03-18 00:58:13 +00:00
Kris Nuttycombe cf84e1e79d
Merge pull request #5699 from nuttycom/feature/wallet_orchard-merge_master
Merge master branch back to feature/wallet_orchard
2022-03-17 18:25:28 -06:00
Kris Nuttycombe 80324bc653 Merge remote-tracking branch 'upstream/master' into feature/wallet_orchard-merge_master 2022-03-17 16:52:49 -06:00
Kris Nuttycombe 7cd4462b20
Merge pull request #5695 from str4d/5694-gitian-fix
build: Fix `zcash/address/orchard.hpp` filename in `src/Makefile.am`
2022-03-17 16:30:33 -06:00
Kris Nuttycombe b708b8ef60
Merge pull request #5692 from nuttycom/feature/wallet_orchard-limited_ua_inputs
Add UnifiedAddress variant to ZTXOSelector
2022-03-17 13:42:41 -06:00
Jack Grigg 22c6bf88a3 build: Fix `zcash/address/orchard.hpp` filename in `src/Makefile.am`
The incorrect extension caused `make distdir-am` to fail due to a
missing rule for the header file.

Closes zcash/zcash#5694.
2022-03-17 19:38:10 +00:00
Kris Nuttycombe a783de43e3 Simplify retrieval of unified account by address.
Co-authored-by: ying tong <yingtong@z.cash>
2022-03-17 11:38:44 -06:00
Kris Nuttycombe 9582a2c1f1 Eliminate redundancy between the wallet and the keystore.
The methods `FindUnifiedFullViewingKey` and `FindUFVKByReceiver`
are implementable with only the information that'a available from
the keystore, so it's better to use the methods that are defined
there in the previous commit.
2022-03-17 11:38:44 -06:00
Kris Nuttycombe 9d8c20ed78 Add UnifiedAddress variant to ZTXOSelector
When a user supplies a unified address as the source of funds
for z_sendmany, the previous behavior could have risked user
privacy by spending transparent UTXOs belonging to different
diversified addresses in the same transaction, thereby linking
those addresses, and consequently any diversified shielded
receivers of unified addresses containing those transparent
receivers.

This change limits selection of transparent UTXOs to only those
explicitly associated with the unified receivers of the provided
source UA. Also, if a UA is provided as the source, the behavior
is maintained that only shielded notes in pools corresponding to
the receivers of that UA will be selected.
2022-03-17 11:38:44 -06:00
str4d 2e77745c4e
Merge pull request #5607 from zcash/zcash_script_v5_api
Add `zcash_script` APIs for handling v5 transactions
2022-03-16 22:04:47 +00:00
str4d 12b37d5a11
Merge pull request #5665 from str4d/5657-z_sendmany-orchard-spends
Add support for sending Orchard funds in `z_sendmany`
2022-03-16 18:12:56 +00:00
str4d 454e7640f0
Merge pull request #5680 from nuttycom/saplingmigration_explicit_change
Explicitly specify the change address in the Sapling migration.
2022-03-16 13:11:02 +00:00
Kris Nuttycombe b9b471bfa6
Merge pull request #5684 from nuttycom/feature/wallet_orchard-rpc_doc_cleanup
Address minor issues from #5605
2022-03-15 21:43:47 -06:00
Kris Nuttycombe 5c9b7ec4df
Merge pull request #5671 from therealyingtong/ua-z_getbalance
z_getbalance: Handle Unified Address case.
2022-03-15 19:29:37 -06:00
Kris Nuttycombe 860fc8886a Update documentation of z_importviewingkey and z_exportviewingkey to address unified keys. 2022-03-15 16:39:26 -06:00
Kris Nuttycombe 482f0e7e44 Return the correct error for `z_getbalance` if a UA does not correspond to an account in the wallet..
Also, update the help text for z_getbalance to suggest that the user
prefer z_getbalanceforviewingkey.

Co-authored-by: str4d <thestr4d@gmail.com>
2022-03-15 16:22:05 -06:00
ying tong 1cd92e579c Style improvements in RPC tests.
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-03-15 16:22:05 -06:00
therealyingtong c1cd8a7e2d Adapt RPC tests to use z_getbalance for UAs. 2022-03-15 16:22:05 -06:00
therealyingtong 6caec7aef1 z_getbalance: Handle Unified Address case. 2022-03-15 16:22:05 -06:00
Kris Nuttycombe 235767ba6e Document that Orchard keys are not supported in z_importkey help 2022-03-15 16:07:37 -06:00
Kris Nuttycombe 24ce54f3ed Add orchard support to z_getnotescount 2022-03-15 15:49:01 -06:00
Kris Nuttycombe 5d65952f54 Document that z_getnewaccount and z_getaddressforaccount replace z_getnewaddress. 2022-03-15 15:32:06 -06:00
Kris Nuttycombe 1b1495b7d7 Add TODO for Orchard z_listunspent integration. 2022-03-15 15:31:40 -06:00
sasha 2dcaac5fcb
Merge pull request #5574 from daira/zcashd-wallet-tool
Make a zcashd-wallet-tool executable
2022-03-15 13:41:55 -07:00
Jack Grigg 381b626a78 Set default Orchard anchor confirmations to 1
Currently we cannot use anything greater than 1, because the Orchard
witness tree cannot provide authentication paths to anything but the
current tree root. Once zcash/incrementalmerkletree#22 is implemented,
we can revert this change as part of zcash/zcash#5644.
2022-03-15 19:53:30 +00:00
Jack Grigg c2220f4eb9 Add support for sending Orchard funds in `z_sendmany`
Closes zcash/zcash#5665.
2022-03-15 19:53:30 +00:00
Kris Nuttycombe 7ba239e605 Explicitly specify the change address in the Sapling migration. 2022-03-15 10:57:19 -06:00
Jack Grigg d49c8a2865 Add Orchard to change address generation 2022-03-15 16:41:09 +00:00
Jack Grigg d4eee99060 Add Orchard cases to note selection logic 2022-03-15 16:41:09 +00:00
Jack Grigg f2ae807891 Implement opportunistic shielding in `SpendableInputs::LimitToAmount`
We now use the total funds in each available pool to figure out whether
a particular desired selection order can provide the requested amount,
and can alter the selection order each time we add another pool.
2022-03-15 16:41:09 +00:00
Jack Grigg 920de99c85 Select spendable inputs based on recipient and change pool types
This information enables us to select notes and coins in a way that
minimizes information leakage while moving funds into the shielded pool
where possible.
2022-03-15 16:41:09 +00:00
Jack Grigg 959b068468 Fix bug in `SpendableInputs::LimitToAmount`
This method selects inputs by iterating through available notes and
coins until sufficient value is found, and then dropping the remaining
notes and coins. However, the processing of each pool type was gated on
having not selected sufficient funds. This meant that if an earlier pool
had sufficient funds to fill a transaction, later pools would not be
processed at all and their notes would be left as selectable.
2022-03-15 16:41:09 +00:00
Jack Grigg 63437e4c18 Add unit tests for `SpendableInputs::LimitToAmount` 2022-03-15 16:41:09 +00:00
Jack Grigg f56a04ded2 Refactor `CWallet::GenerateChangeAddressForAccount`
There are no functional changes in this commit, but the refactor
separates the legacy account handling, and makes it easier to integrate
new pool types.

We also rename `libzcash::ChangeType` to `OutputPool`, in anticipation
of using it to track transaction recipient pools.
2022-03-15 16:41:09 +00:00
Kris Nuttycombe c3c5236cee
Merge pull request #5656 from nuttycom/feature/wallet_orchard-note_commitment_tree_ser
Add Orchard note commitment tree serialization & restore.
2022-03-14 23:16:44 -06:00
sasha 3a957618b6
Merge pull request #5662 from nuttycom/legacy_seed_to_mnemonic
Derive the mnemonic seed from the legacy HD seed, if one is available.
2022-03-14 20:32:09 -07:00
sasha 7071b41c55
Merge pull request #5578 from superbaud/powlimit-document
comment explaining the 0x0f0f[..]0f0f powLimit constant for regtest
2022-03-14 18:47:04 -07:00
Kris Nuttycombe 5a98080677 Apply suggestions from code review
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-03-14 19:13:25 -06:00
sasha f37d186e7e
Merge pull request #5600 from superbaud/use-debian-libtinfo
remove superfluous space at end of native_packages that i introduced in #5576
2022-03-14 17:06:53 -07:00
ying tong 4d7892bdde
Merge pull request #5649 from therealyingtong/conflicted-orchard-spends
Report conflicted Orchard spends
2022-03-15 07:46:02 +08:00
Kris Nuttycombe e335ff2325 Fix indentation. 2022-03-14 17:40:10 -06:00
Kris Nuttycombe d420e2efab Derive the new mnemonic seed from the legacy HD seed, if one is available.
Fixes #5572

Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-03-14 17:40:04 -06:00
Daira Hopwood 4611493c9d
Merge pull request #5663 from nuttycom/check_master_key_validity
Return std::optional<CExtKey> from CExtKey::Master
2022-03-14 17:10:24 +00:00
Daira Hopwood 340840894a Tweaks to message text.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-14 08:24:25 +00:00
Daira Hopwood 6ee6692c83 qa/zcash/full_test_suite.py: enable `test_rpath_runpath` for Rust binaries, and
reenable `test_fortify_source` for C++ binaries.

Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-14 08:05:59 +00:00
Daira Hopwood 1d31d4bc7c Include $(bin_SCRIPTS) in `check-symbols`, `check-security`, and `clean` targets.
Checking for stack canaries in `check-security` is disabled for Rust executables
(Rust does support `-Z stack-protector=all` but only for the nightly compiler).

Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-13 22:53:29 +00:00
Daira Hopwood 55d14faebf Correct the fallback instruction for how to clear the terminal on macOS: pressing Command + K
also clears scrollback.

Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-13 18:25:40 +00:00
Kris Nuttycombe 19bce27321
Merge pull request #5641 from nuttycom/feature/wallet_orchard-restore_orchard_spends
Restore Orchard spentness cache in LoadCaches
2022-03-12 14:21:38 -07:00
Kris Nuttycombe c9938d0348
Update orchard_wallet_add_notes_from_bundle documentation. 2022-03-12 11:46:48 -07:00
Daira Hopwood ccada66324 Simplify debug tracing.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-12 12:56:32 +00:00
Kris Nuttycombe 4eda33f184 Serialize the Orchard note commitment tree to the wallet.
This adds serialization of Orchard note commitment trees
to `SetBestChain` and deserialization and loading of those
trees to wallet load.
2022-03-11 22:47:37 -07:00
Kris Nuttycombe 30dd633de6 Apply suggestions from code review
Co-authored-by: str4d <thestr4d@gmail.com>
2022-03-11 22:39:06 -07:00
Kris Nuttycombe 5d17e53c5c Address suggestions from code review on #5637 2022-03-11 22:39:06 -07:00
Kris Nuttycombe f5b23b3336 Modify `join_network` in tests to skip mempool resync.
Prior to this change, calling `join_network` after a network split
only worked in the case that no new non-coinbase transactions were
created during the network partition; in the case that such a
transaction was created, `join_network` would fail when waiting
for mempool synchronization, because zcashd nodes do not.
automatically broadcast their mempool contents on restart.

This change modifies `setup_network` to wait for mempool synchronization
or not on the basis of a newly added do_mempool_sync flag. In the
case of `join_network`, this flag is set to `False`; the default value
is set to `True` to preserve previous functionality elsewhere.

Tests should explicitly use the `resendwallettransactions` RPC method
to ensure that mempool transactions are rebroadcast before attempting
to synchronize the network after a `join_network` call.
2022-03-11 22:39:06 -07:00
Kris Nuttycombe 1e1521f101 Ensure that Orchard spentness information is repopulated by LoadUnifiedCaches. 2022-03-11 22:39:06 -07:00
str4d 0f59c5b926
Merge pull request #5658 from str4d/5570-tx-builder-orchard-spends
Add Orchard sender support to the transaction builder
2022-03-12 04:33:06 +00:00
therealyingtong 3c0e1d9aa8 CWallet::GetConflictS: Handle conflicting Orchard spends.
Co-authored by: Kris Nuttycombe <kris@nutty.land>
2022-03-11 19:30:52 -07:00
Kris Nuttycombe 0c38ad9200 Return std::optional<CExtKey> from CExtKey::Master
Master key generation is fallible, but the type of master
key generation did not previously reflect that the generated
key might be invalid.
2022-03-11 18:47:01 -07:00
Jack Grigg 93797d185b Improve FFI interface documentation
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-03-12 01:33:39 +00:00
Daira Hopwood 577f695af5 Set `meta` for `-datadir` option.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-11 19:02:31 +00:00
Daira Hopwood 3e36252d9a Document that '~' cannot be used in `-datadir` (see #5661).
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-11 18:35:52 +00:00
Daira Hopwood d5b6e226f0 Ensure the buffer used in `prompt` is zeroized even on error.
Co-authored-by: Jack Grigg <str4d@electriccoin.co>
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-11 18:21:11 +00:00
Daira Hopwood 7a98644ff2 Improve error message when the config file cannot be found, taking into account -conf and -datadir.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-11 18:16:40 +00:00
Daira Hopwood cb2c89eedf Use the tracing crate for debugging.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-11 17:17:56 +00:00
Jack Grigg 7921ef1c55 wallet: Alter `OrchardWallet::GetSpendInfo` to return the spending key
In the context of `z_sendmany`, we will need both the spending key and
note spendInfo as a pair, so it makes sense to fetch those together.
2022-03-11 03:31:00 +00:00
Jack Grigg 6a7213b5d5 Add Orchard spend support to the transaction builder
Closes zcash/zcash#5570.
2022-03-11 03:30:55 +00:00
str4d 8964763bef
Merge pull request #5653 from nuttycom/feature/wallet_orchard-merge_master
Merge master branch back to `feature/wallet_orchard`
2022-03-11 03:19:18 +00:00
Daira Hopwood f6a8461047
Merge pull request #5636 from superbaud/lazy-load-parameters
only load the proof parameters in gtests for tests that need them
2022-03-10 18:11:09 +00:00
Daira Hopwood 799b46b90a Cleanups to error handling for the first invocation of zcash-cli.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-10 17:18:26 +00:00
Daira Hopwood 999b395360 Improve memory hygiene, and use -stdin to pass the recovery phrase to the child zcash-cli process.
Co-authored-by: Jack Grigg <str4d@electriccoin.co>
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-10 17:17:06 +00:00
Kris Nuttycombe 880cbe0dc4
Merge pull request #5647 from therealyingtong/orchard-nullifier-test
Update `coins_tests.cpp` to test Orchard cases.
2022-03-10 06:59:19 -07:00
Kris Nuttycombe 8bf99eaacc Merge remote-tracking branch 'upstream/master' into feature/wallet_orchard-merge_master 2022-03-09 22:17:49 -07:00
Kris Nuttycombe b21ca34d44
Merge pull request #5637 from nuttycom/feature/wallet_orchard-persist_action_ivks
Implement Orchard note persistence & restore.
2022-03-09 22:13:26 -07:00
Kris Nuttycombe f057ebb375 Apply suggestions from code review 2022-03-09 17:48:27 -07:00
Kris Nuttycombe e63d52896a Restore mined block heights when restoring decrypted notes. 2022-03-09 17:48:27 -07:00
Kris Nuttycombe ca34a97a37 Apply suggestions from code review
Co-authored-by: str4d <thestr4d@gmail.com>
2022-03-09 13:26:10 -07:00
Daira Hopwood d4405feddf Simplify extraction of recovery phrase.
Co-authored-by: Jack Grigg <str4d@electriccoin.co>
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-09 18:04:27 +00:00
Kris Nuttycombe 98ee3bd733 Update tests with rollback checks. 2022-03-09 09:26:37 -07:00
Kris Nuttycombe dc1609094d Restore decrypted notes to the wallet. 2022-03-09 09:26:37 -07:00
Kris Nuttycombe 0149451217 Persist Orchard action index/IVK mappings in CWalletTx 2022-03-09 09:26:37 -07:00
Kris Nuttycombe ecd0d24baa Update walletTx with decrypted Orchard action metadata. 2022-03-09 09:26:37 -07:00
Kris Nuttycombe 46c33c68e0
Merge pull request #5642 from nuttycom/feature/wallet_orchard-fix_confirmations
Set number of confirmations & respect maxDepth in Orchard note selection.
2022-03-09 07:58:27 -07:00
therealyingtong 26b8ec5562 coins_tests: Update tests to include Orchard case.
Co-authored-by: Jack Grigg <jack@electriccoin.co>
2022-03-09 22:52:39 +08:00
therealyingtong 663f8d434a coins_tests.cpp: Add Orchard nullifier to TxWithNullifiers().
Closes zcash/zcash#5240

Co-authored-by: Jack Grigg <jack@electriccoin.co>
2022-03-09 22:20:46 +08:00
Kris Nuttycombe 38f4afa80c Set number of confirmations for Orchard notes returned by FindSpendableInputs 2022-03-08 22:03:24 -07:00
Kris Nuttycombe 3b5fc4e583 Respect maxDepth for Orchard notes in GetFilteredNotes 2022-03-08 22:00:30 -07:00
sasha ced825edec
Merge pull request #5587 from nuttycom/feature/wallet_orchard-record_spends 2022-03-08 18:38:47 -08:00
Kris Nuttycombe 19ce19155c Apply suggestions from code review
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
Co-authored-by: str4d <thestr4d@gmail.com>
2022-03-08 16:27:06 -07:00
Kris Nuttycombe 9284c5552b Track mined-ness instead of spent-ness of notes in the Orchard wallet.
Tracking spentness would require the Orchard wallet to be aware of the
local node's mempool contents; instead, the Orchard wallet now only
tracks whether a note is known to be mined and the C++ wallet checks
selected notes to determine whether any transactions that consume those
notes exist in the mempool.

In addition:
* Locking of notes will be delegated to the C++ side.
* The type of the recipient in wallet::NoteMetadata was
  changed from `*const Address` to `*mut Address` to reflect the
  fact that it is owned memory that must be freed.
* Nullifier data is no longer cleared during rewinds.
2022-03-08 11:55:53 -07:00
str4d 7129827509
Merge pull request #5601 from str4d/feature/wallet_orchard-shielded_coinbase
Select Orchard receivers from UA miner addresses once NU5 activates
2022-03-08 15:32:32 +00:00
Kris Nuttycombe 51cb37e541 Fix missing update to `last_checkpoint` on rewind. 2022-03-07 22:02:19 -07:00
Charlie O'Keefe 4f04c96f7b
Merge pull request #5632 from steven-ecc/z_sendmany_uniqueness_check
Extend uniqueness check in z_sendmany to UA receivers
2022-03-07 21:56:49 -07:00
Kris Nuttycombe c3cf141936 Apply suggestions from code review
Co-authored-by: str4d <thestr4d@gmail.com>
2022-03-07 19:11:43 -07:00
Kris Nuttycombe c9ef5cd45a Add an `InPoint` type to the Orchard wallet to fix incorrect conflict data. 2022-03-07 19:05:00 -07:00
Kris Nuttycombe 97895a6f6d Apply suggestions from code review
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 19:05:00 -07:00
Kris Nuttycombe faffe2e084 Assert we never attempt to checkpoint the Orchard wallet at a negative block height.
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 19:05:00 -07:00
Kris Nuttycombe 6171f27b02 Add a test for Orchard note detection.
We can't actually test rollback via RPC tests until
wallet persistence is implemented. This implements
a rollback scenario that will should pass after wallet
persistence is implemented.
2022-03-07 19:05:00 -07:00
Kris Nuttycombe 9336c5e7f0 Remove assertions that require Orchard wallet persistence to satisfy. 2022-03-07 19:05:00 -07:00
Kris Nuttycombe 23a7314607 Check wallet latest anchor against hashFinalOrchardRoot in ChainTip.
This will cause test failures until note commitment tree persistence
is implemented.
2022-03-07 19:05:00 -07:00
Kris Nuttycombe 6b8ecc8a31 Reset Orchard wallet state when rescanning from below NU5 activation. 2022-03-07 19:05:00 -07:00
Kris Nuttycombe b78c2732ab Record when notes are detected as being spent in the Orchard wallet. 2022-03-07 19:04:58 -07:00
Kris Nuttycombe 5c049b6b13 Fix missing locks for GenerateNewUnifiedSpendingKey tests. 2022-03-07 19:03:02 -07:00
Kris Nuttycombe 8bc4c2aad8 Split LoadWalletTx from AddToWallet
The `CWallet::AddToWallet` method had completely divergent
behavior depending upon the value of the fFromLoadWallet
flag, and `pwalletdb` was unused when this flag was set
to `true`, so this is better represented as two distinct
methods on CWallet.
2022-03-07 19:03:00 -07:00
Kris Nuttycombe f7006e9a33
Merge pull request #5592 from superbaud/gtest-ordering-fixes
fix ordering issues with gtests: WalletZkeysTest, ContextualCheckBlockTest, CheckBlock, Metrics.GetLocalSolPS
2022-03-07 18:54:16 -07:00
Kris Nuttycombe 237384cf2b Update incrementalmerkletree version 2022-03-07 18:32:58 -07:00
Kris Nuttycombe 5a6ef3467e Update MSRV for lints. 2022-03-07 18:21:37 -07:00
Daira Hopwood 9b20105413 Tweak the wording of the fallback messages when the terminal cannot be automatically cleared.
Co-authored-by: Kris Nuttycombe <kris@electriccoin.co>
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 19:34:31 +00:00
Daira Hopwood 6fb943d0f6 Refactor use of `export_path` as suggested.
Co-authored-by: Sean Bowe <sean@electriccoin.co>
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 19:33:27 +00:00
Daira Hopwood e940829313 The recovery phrase confirmation and `zcashd-wallet-tool` are being introduced in zcashd v4.7.0.
Co-authored-by: Kris Nuttycombe <kris@electriccoin.co>
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 19:32:51 +00:00
Daira Hopwood 6f5efcbb0f Improved error handling.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 18:39:23 +00:00
Daira Hopwood 25792cba93 Move `wallet_tool.rs` from `src/rust/src` into `src/rust/bin`.
Also add a brief description of `zcashd-wallet-tool` to `src/rust/README.md`.

Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 18:39:23 +00:00
Daira Hopwood 8ec0d854b9 Add some text about choosing location of the physical backup.
Add TODO for better handling of file not found and permission errors.

Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 18:39:23 +00:00
Daira Hopwood de933c1cb5 Attempt to fix linking problem on ARM.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 18:39:23 +00:00
Daira Hopwood a36fceca70 Make a zcashd-wallet-tool executable.
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
2022-03-07 18:39:23 +00:00
Steven Smith 7f7d9e6e1f Extend uniqueness check in z_sendmany to UA receivers 2022-03-06 21:24:14 -08:00
sasha f1d652fc78 Call LoadProofParameters() in gtests that need proofs 2022-03-05 21:51:27 -08:00
sasha 8f68166c24 move proof parameter loading out of gtest/main.cpp and into utiltest.cpp 2022-03-05 21:51:27 -08:00
sasha 0e6b004a64 make librustzcash_init_zksnark_params idempotent 2022-03-05 21:51:27 -08:00
str4d 13351ff31d
Merge pull request #5609 from str4d/bump-deps-4.7.0-rc1
Bump dependencies ahead of 4.7.0-rc1
2022-03-05 13:37:28 +00:00
Jack Grigg fdb5709e7e depends: Revert to `libc++ 13.0.0-3` for Windows cross-compile
The 13.0.1-1 MSYS2 binaries cause linker errors due to missing `new` and
`delete` symbols. This commit partially reverts the LLVM 13.0.1 upgrade:
Windows cross-compilation still uses `clang 13.0.1`, but is compiled
against `libc++ 13.0.0`.
2022-03-05 03:53:13 +00:00
Jack Grigg 9d5d781a15 libzcash_script: Add more granular errors to the new APIs 2022-03-04 21:36:24 +00:00
str4d 414e74f70a
Merge pull request #5629 from nuttycom/feature/wallet_orchard-minconf_filter
Respect minDepth argument for Orchard notes in GetFilteredNotes
2022-03-04 03:18:52 +00:00
Kris Nuttycombe 6540694845 Respect minDepth argument for Orchard notes in GetFilteredNotes
Fixes #5566
2022-03-03 17:41:30 -07:00
Kris Nuttycombe cf4d0f3d2d Add test for Orchard contribution to z_gettotalbalance 2022-03-03 17:41:30 -07:00
Charlie O'Keefe e33178615c
Merge pull request #5581 from LarryRuane/2022-02-log-getblocktemplate
Log calls to getblocktemplate
2022-03-03 15:56:34 -07:00
Charlie O'Keefe c2b5bf0c42
Merge pull request #5622 from nuttycom/logging/sapling_note_witnesses
Log outpoint for failed Sapling witness lookup.
2022-03-03 15:21:10 -07:00
str4d d4a019e89b
Merge pull request #5621 from nuttycom/test/orchard_tree_ser_roundtrip
Add a roundtrip test for Orchard merkle frontier serialization from the C++ side.
2022-03-03 21:56:41 +00:00
Jack Grigg 8b90d32972 rpc: Handle keypool exhaustion separately in `generate` RPC
A prior commit moved the `IsValidMinerAddress()` call before the keypool
exhaustion check, but `IsValidMinerAddress()` internally handles keypool
exhaustion. We now check it explicitly first to retain the prior error
behaviour (as checked in the `keypool` RPC test).
2022-03-03 21:10:44 +00:00
Kris Nuttycombe 8b44cc7072 Add a roundtrip test for Orchard merkle frontier serialization from the C++ side.
Co-authored-by: str4d <thestr4d@gmail.com>
2022-03-03 11:04:40 -07:00
Kris Nuttycombe ceff068e20
Merge pull request #5625 from therealyingtong/orchard-remove-with-anchor
Orchard: invalidate mempool transactions that use orphaned anchors.
2022-03-03 10:58:08 -07:00
Jack Grigg 613534bbb3 miner: Manually add dummy Orchard output with Orchard miner address
`orchard::Builder` pads to two Actions, but does so using a "no OVK"
policy for dummy outputs, which violates coinbase rules requiring all
shielded outputs to be recoverable. We manually add a dummy output to
sidestep this issue.
2022-03-03 15:57:40 +00:00
Jack Grigg 3fa58149b0 Select Orchard receivers from UA miner addresses once NU5 activates
Closes zcash/zcash#5023.
2022-03-03 15:57:40 +00:00
Jack Grigg 1c31a1c7d8 Use `std::optional` in `CValidationInterface::GetAddressForMining`
The initial value for the provided return value is now `std::nullopt`
instead of the default value of the `MinerAddress` variant. This makes
it easier to distinguish "no miner address was provided" from "invalid
miner address".
2022-03-03 15:57:40 +00:00
Kris Nuttycombe 1732a50957
Merge pull request #5624 from str4d/fix-nu5-testnet-bug
Test and fix for chain forks on NU5 testnet
2022-03-03 08:29:40 -07:00
Daira Hopwood 5990853de3
Merge pull request #5623 from str4d/nu5-fix-orchard-commitment-tree-bugs
Fix for chain forks on NU5 testnet
2022-03-03 15:21:58 +00:00
Kris Nuttycombe fd2f5bdc28
Merge pull request #5610 from therealyingtong/debug-any-taddr
Ignore expired transactions in `CWallet::FindSpendableInputs`.
2022-03-03 08:08:06 -07:00
Kris Nuttycombe 65b5b7509c
Merge pull request #5612 from ebfull/feature/persist_orchard_keys
Recover orchard keys and mappings during wallet load
2022-03-03 07:47:16 -07:00
Jack Grigg 35b8c8648f Merge branch 'nu5-fix-orchard-commitment-tree-bugs' into fix-nu5-testnet-bug 2022-03-03 13:36:45 +00:00
Jack Grigg 9a60cdeed9 Add RPC test for the Orchard commitment tree bug on first NU5 testnet 2022-03-03 13:36:42 +00:00
Jack Grigg ae3d2a3525 Add missing `view.PopAnchor(_, ORCHARD)` in `DisconnectBlock`
By forgetting to pop the tree, we were leaving the view in an
inconsistent state, where it believed the best Orchard anchor was for a
tree that didn't correspond to the rest of the view. This would then
propagate in subsequent block connections, and the chain history
commitments to Orchard tree roots eventually result in inconsistent
`blockcommitments` values in subsequent blocks.
2022-03-03 13:26:48 +00:00
Jack Grigg 2230ba912f Ensure the view's best Orchard anchor matches the previous block
`ConnectBlock` was already checking that the given view's "best block"
was the previous block. However, it then assumed the view was correct on
its claimed best anchors.

For Orchard, we know that `hashFinalOrchardRoot` field of `CBlockIndex`
will only ever be set when a block is (attempted to be) connected to the
main chain, and so we can instead add assertions around its value and
ensure the view is consistent with the previous block.
2022-03-03 13:26:48 +00:00
therealyingtong dbb7e027c8 Orchard: invalidate mempool transactions that use orphaned anchors. 2022-03-03 17:37:07 +08:00
Sean Bowe bd137561f2
wallet: add logging for failure cases in unified cache loading 2022-03-02 16:24:08 -07:00
Kris Nuttycombe 681505e9d6 Log outpoint for failed Sapling witness lookup. 2022-03-02 16:00:15 -07:00
sasha 2e52c1623d gtests ordering: clean up wallet files before each WalletZkeysTest
This allows us to use --gtest_repeat= on the WalletZkeysTest group,
otherwise, each test will reuse its previous iteration's wallet file,
causing assertions to be invalidated.

We put the RemoveDb at the beginning of each test because putting it
at the end of the test means it doesn't get run if the test fails.
This would lead to a single failed test causing every instance of that
test thereafter to fail (since it uses the same temporary directory).

The dummyWallet.LoadWallet() ensures that CDB is initialized, otherwise
bitdb.RemoveDb causes the error:
"BDB1565 DB_ENV->dbremove: method not permitted before handle's open
method"
2022-03-02 13:24:55 -08:00
sasha 902187368e gtests ordering: make Metrics.GetLocalSolPS idempotent
we use our new AtomicTimer::zeroize() to reset the miningTimer and
solutionTargetChecks.decrement() thrice to get it back to original value
2022-03-02 11:29:09 -08:00
sasha 0e13aba1b7 implement AtomicTimer::zeroize() that resets start_time and total_time 2022-03-02 11:29:09 -08:00
Sean Bowe c5b87ba163
wallet: rather than assert, error in case of inconsistency between FVK and address 2022-03-02 11:05:04 -07:00
therealyingtong cedf2b5ecb wallet_sendmany_any_taddr.py: Expect expired tx to be ignored. 2022-03-03 01:31:32 +08:00
therealyingtong 454c1eed46 FindSpendableInputs: Add nDepth < 0 check.
Co-authored-by: Jack Grigg <jack@electriccoin.co>
2022-03-03 01:31:32 +08:00
therealyingtong 98cd4bab0b wallet_sendmany_any_taddr.py: Test sending output from expired tx. 2022-03-03 01:31:32 +08:00
str4d 1c38c9833c
Merge pull request #5608 from str4d/5385-orchard-circuit-changes
Migrate to `orchard` crate revision with circuit changes
2022-03-02 15:26:37 +00:00
Sean Bowe 3665f69a48
wallet: restore orchard address to IVK mappings during wallet loading 2022-03-02 08:11:47 -07:00
Sean Bowe f7824e8cec
wallet: restore Orchard secret keys from mnemonic seed 2022-03-02 08:11:47 -07:00
Sean Bowe 49ce03ed79
wallet: consolidate unified key/address/account map reconstruction 2022-03-02 08:11:47 -07:00
Kris Nuttycombe 5141c2971c
Merge pull request #5594 from LarryRuane/2022-02-getblocktemplate-locking
Fix csBestBlock/cvBlockChange waiting in rpc/mining
2022-03-01 20:08:29 -07:00
Kris Nuttycombe 68d6c3ddc3
Merge pull request #5615 from nuttycom/feature/wallet_orchard-merge_master
Merge master back to feature/wallet_orchard
2022-03-01 19:57:39 -07:00
Kris Nuttycombe 1389eea5c2
Merge pull request #5614 from nuttycom/feature/wallet_orchard-fix_orchard_minconf
Filter returned Orchard notes by minimum confirmations.
2022-03-01 19:57:05 -07:00
Larry Ruane e170c3abd6 document global variables 2022-03-01 17:43:43 -07:00
Pieter Wuille c079a518c0 Modernize best block mutex/cv/hash variable naming
(cherry picked from commit bitcoin/bitcoin@4a6c0e3dcf)
2022-03-01 17:09:19 -07:00
Pieter Wuille 4693f8165f Fix csBestBlock/cvBlockChange waiting in rpc/mining
(cherry picked from commit bitcoin/bitcoin@45dd135039)
2022-03-01 17:09:19 -07:00
Kris Nuttycombe 783e269a7b Merge remote-tracking branch 'upstream/master' into feature/wallet_orchard-merge_master 2022-03-01 16:54:05 -07:00
Kris Nuttycombe bd50fbbb94 Filter returned Orchard notes by minimum confirmations. 2022-03-01 16:30:29 -07:00
therealyingtong 4a8bdabb2f wallet_sendmany_any_taddr.py: Test sending from a change taddr. 2022-03-01 11:33:54 +08:00
sasha 0f06774e94 gtests ordering: CheckBlock.VersionTooLow calls SelectParams(MAIN) 2022-02-28 17:59:50 -08:00
sasha 3a7b4eca87 gtests ordering: ContextualCheckBlockTest's TearDown deactivates Blossom 2022-02-28 17:58:00 -08:00
sasha 489c025f81 gtests ordering: change wallet filename in WriteZkeyDirectToDb 2022-02-28 17:57:10 -08:00
Jack Grigg b5ce94d16c rust: Fix clippy lint 2022-03-01 01:35:46 +00:00
Jack Grigg 7b7dddba0c cargo update 2022-03-01 00:34:02 +00:00
Jack Grigg 2f1fbcc81f depends: Update Clang / libcxx to LLVM 13.0.1 2022-03-01 00:30:32 +00:00
Jack Grigg 21430b13ac depends: Update Rust to 1.59.0 2022-03-01 00:09:18 +00:00
Jack Grigg adb7d074d5 qa: Postpone recent CCache release 2022-03-01 00:05:01 +00:00
Jack Grigg 4c49af7750 qa: Bump all postponed dependencies
We have a pending PR to address the `native_ccache` and `googletest`
dependencies, and we aren't going to touch `bdb`.
2022-03-01 00:03:42 +00:00
Jack Grigg 9f5f36e94c Migrate to `orchard` crate revision with circuit changes
Part of zcash/zcash#5385.
2022-02-28 23:52:49 +00:00
Kris Nuttycombe 18f443db1c
Merge pull request #5604 from nuttycom/fix_missing_wallet_lock
GenerateNewKey must be guarded by a cs_wallet lock
2022-02-27 09:20:44 -07:00
Kris Nuttycombe 399ffa7f5c GenerateNewKey must be guarded by a cs_wallet lock 2022-02-26 20:13:38 -07:00
sasha 077f5550d4 remove superfluous space at end of native_packages line 2022-02-25 14:43:13 -08:00
Kris Nuttycombe f9380572c1
Merge pull request #5597 from nuttycom/feature/wallet_orchard-find_spendable_inputs
Select Orchard notes in FindSpendableInputs
2022-02-25 08:36:19 -07:00
Kris Nuttycombe 044d2de463
Merge pull request #5596 from str4d/feature/wallet_orchard-orchard_sendmany
z_sendmany: Send to Orchard receivers of UA recipients
2022-02-25 08:27:02 -07:00
Kris Nuttycombe 41ed311afa
Merge pull request #5595 from str4d/feature/wallet_orchard-ua_updates
Merge current master into Orchard wallet feature branch
2022-02-24 21:42:13 -07:00
Kris Nuttycombe d9cddac773 Select Orchard notes in FindSpendableInputs 2022-02-24 18:16:53 -07:00
Jack Grigg 701a7875a7 Regenerate `TxDigests` after rebuilding tx with Orchard bundle
In zcash/zcash/pull/5544 we added an FFI method specific to the Orchard
part of the transaction builder, to enable passing the Orchard bundle on
the Rust side directly instead of using a partial serialization. We
passed the remainder of the transaction to the Rust side by serializing,
as we similarly do in `SignatureHash`. We then decomposed the parsed
transaction on the Rust side to insert the Orchard bundle. However, we
then used the `TxDigests` that was derived from the parsed transaction,
not the reconstructed one, which meant we produced an invalid sighash
when building Orchard-containing transactions.
2022-02-25 00:52:52 +00:00
Jack Grigg d4078c8d25 Select Orchard receivers preferentially from UAs
The addition of `OrchardRawAddress` to `RecipientAddress` drives most of
the changes in this commit, which enable `z_sendmany` to send funds to
addresses in the Orchard pool once NU5 activates.
2022-02-25 00:52:52 +00:00
Jack Grigg 85a8784b38 Fix semantic merge conflicts
- We added `OrchardRawAddress` to the `Receiver` variant, which prevents
  it from having a default constructor.
- We added `ReceiverType::Orchard`, which Clang warns is unused when we
  use `ReceiverType` in a `switch` statement.
2022-02-25 00:48:17 +00:00
Jack Grigg bb4e792c84 Merge branch 'master' into feature/wallet_orchard-orchard_sendmany 2022-02-25 00:33:28 +00:00
Taylor Hornby 7d9dda4b7e
Merge pull request #5576 from superbaud/use-debian-libtinfo
fix breakage on Arch by using Debian libtinfo5_6.0 to satisfy clang
2022-02-24 16:33:13 -07:00
str4d 48bf0241e6
Merge pull request #5586 from str4d/feature/wallet_orchard-orchard_receivers
Add Orchard to default UA receiver types
2022-02-24 15:52:31 +00:00
Steven 82c33596b9
Merge pull request #5580 from steven-ecc/update-transaction-size-estimation
Update transaction size estimation to include V5 transactions
2022-02-23 20:16:58 -08:00
Jack Grigg b0769e3f1d Add Orchard to default UA receiver types
We also add Orchard-specific information to several RPCs in order for
tests to pass:

- `z_listunifiedreceivers`
- `z_getbalanceforaccount`
- `z_getbalanceforviewingkey`
2022-02-24 02:17:52 +00:00
Jack Grigg 235fde5193 Add mappings from Orchard receivers to IVKs to the wallet
We add the Orchard change address mapping on account creation, as we
only ever generate a single change address internally. External Orchard
receivers are added to the map when `z_getaddressforaccount` is called.

This commit also fixes two bugs in the handling of Orchard internal IVKs.
When adding a full viewing key to the Orchard (Rust) wallet, we need to
derive both the internal and external IVKs (and map them to the external
FVK so we effectively never expose the internal FVK). We were only
deriving the external IVK, meaning that trying to add the Orchard change
address to the wallet should have failed. However, in a separate bug the
C++ internal IVK derivation method was calling the Rust external IVK
derivation function, meaning that we were effectively using the external
Orchard address at index 0 _as_ the change Orchard address.
2022-02-24 02:17:19 +00:00
Steven Smith a0740650c3 Update transaction size estimation to include V5 transactions 2022-02-23 16:49:01 -08:00
Kris Nuttycombe 49065bee59
Merge pull request #5569 from str4d/feature/wallet_orchard-ua_integration
Integrate Orchard into Unified Address types
2022-02-23 11:20:49 -07:00
John Newbery 4942020624 Log calls to getblocktemplate
(cherry picked from commit bitcoin/bitcoin@1352092dbd)
2022-02-22 22:14:36 -07:00
Jack Grigg 3179e45467 Trial-decrypt candidate Sapling receivers with all wallet UFVKs
This ensures we have the same semantics for detecting Orchard and
Sapling receivers in UAs, even if the UA they are part of was derived
outside of the zcashd wallet.
2022-02-23 02:23:45 +00:00
Jack Grigg d099e469e1 Rename to `ZcashdUnifiedSpendingKey::GetSaplingKey` for consistency 2022-02-22 21:08:10 +00:00
Jack Grigg c2a47b8d97 Return UFVK from `CWallet::GenerateNewUnifiedSpendingKey`
The only place we were using the returned Unified Spending Key was in a
test, where we immediately converted it to a UFVK.

Co-authored-by: Kris Nuttycombe <kris@nutty.land>
2022-02-22 21:08:10 +00:00
Kris Nuttycombe 3c84ed604e Remove OrchardSpendingKey serialization code
Orchard spending keys now live exclusively on the Rust side, and will be
serialized there as part of the Orchard wallet serialization.
2022-02-22 21:08:10 +00:00
Kris Nuttycombe 225c78f3ab Add Orchard components to unified spending keys
Co-authored-by: Jack Grigg <jack@z.cash>
2022-02-22 21:08:10 +00:00
Kris Nuttycombe 258f0fc72f Add Orchard components to unified full viewing keys
Co-authored-by: Jack Grigg <jack@z.cash>
2022-02-22 21:08:10 +00:00
sasha eab7efcae5 explain the 0x0f0f[..]0f0f powLimit constant for regtest 2022-02-22 11:21:37 -08:00
Kris Nuttycombe bb91c9fbc3
Merge pull request #5549 from therealyingtong/cache-ua
Cache UA recipients passed to `z_sendmany`
2022-02-22 09:05:13 -07:00
sasha 2d6dcd4750 on Arch only, use Debian's libtinfo5_6.0 to satisfy clang 2022-02-21 20:28:03 -08:00
therealyingtong 1dae16a41c WriteRecipientMapping: Check that receiver exists in UA.
Co-authored-by: Kris Nuttycombe <kris@nutty.land>
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-02-22 11:53:23 +08:00
ying tong 2943e13c8b
Apply docfixes from code review
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-02-22 08:44:09 +08:00
Kris Nuttycombe 1a1522a4f1 Add Orchard components to unified address
Co-authored-by: Jack Grigg <jack@z.cash>
2022-02-21 17:04:27 +00:00
Kris Nuttycombe 3e3ee1bf3b Add various bits of documentation 2022-02-19 15:29:19 +00:00
Kris Nuttycombe 98ab4bcadb
Merge pull request #5552 from nuttycom/feature/wallet_orchard-add_orchard
Add Rust backend for the Orchard components of the zcashd wallet.
2022-02-19 07:52:15 -07:00
Kris Nuttycombe 1969add228 Apply suggestions from code review
Co-authored-by: Daira Hopwood <daira@jacaranda.org>
2022-02-18 17:45:50 -07:00
Kris Nuttycombe 66890866d6 Remove txid field from TxNotes 2022-02-18 17:07:47 -07:00
therealyingtong 108e9d4658 CSerializeRecipientAddress: add Read method and make constructor private.
Co-authored-by: Kris Nuttycombe <kris@nutty.land>
2022-02-18 16:39:43 +08:00
therealyingtong 99a69182cd Docfixes. 2022-02-18 13:55:58 +08:00
therealyingtong 4c4ee558d6 ShieldToAddress: Factor out static shieldToAddress() helper. 2022-02-18 13:55:23 +08:00
str4d 6cd5b8792b
Merge pull request #5560 from steven-ecc/z_gettreestate_update_for_orchard
Add Orchard support to the z_gettreestate RPC
2022-02-18 03:17:05 +00:00
Kris Nuttycombe d08c992b5e Move parsing of unified addresses to UnifiedAddress.
In cases where we want to be able to decode a string that
is known to be a unified address, it doesn't make sense to have
to route through KeyIO::DecodePaymentAddress and then return
an error depending upon the result type, when it's possible to
provide unified address parsing more directly.

KeyIO::DecodePaymentAddress has been modified to delegate
to UnifiedAddress::Parse
2022-02-17 18:27:15 -07:00
Kris Nuttycombe dcf7f46260 Add test for Orchard wallet note detection.
This tests the following operations:
* add a spending key to the wallet
* add notes from the Orchard bundle component of a transaction to the
  wallet
* detect notes in the wallet that correspond to the spending key
* verify that notes that are not ours are not mistakenly labeled
  as ours
2022-02-17 17:50:17 -07:00
Kris Nuttycombe 4c53499f11 Add GetFilteredNotes to Orchard wallet. 2022-02-17 17:50:17 -07:00
Kris Nuttycombe 6587b2ed86 Add Rust backend for Orchard components of the wallet.
Co-authored-by: str4d <thestr4d@gmail.com>
2022-02-17 17:50:17 -07:00
Larry Ruane ba9e020fcd add functional test 2022-02-17 16:47:59 -07:00
Kris Nuttycombe 73a75a37fe Make SaveRecipientMappings polymorphic in RecipientMapping type. 2022-02-17 08:55:59 -07:00
Steven Smith 59c6b028f7 Add Orchard support to the z_gettreestate RPC 2022-02-17 06:14:21 -08:00
therealyingtong 79c0514919 z_sendmany: Only get ua if decoded is ua variant. 2022-02-17 19:38:02 +08:00
Steven ffdc11c378
Merge pull request #5555 from steven-ecc/deprecate_rpcs
Mark z_gettotalbalance and dumpwallet as deprecated
2022-02-16 15:58:46 -08:00
Daira Hopwood 816649db2a
Cosmetic whitespace change 2022-02-16 22:19:59 +00:00
Steven Smith 512c862a5f Mark z_gettotalbalance and dumpwallet as deprecated 2022-02-16 08:47:34 -08:00
str4d d0d6d47aa7
Merge pull request #5545 from LarryRuane/2022-02-z_listreceivedbyaddress-reject-ua-component
z_listreceivedbyaddress: reject UA component addr (#5537)
2022-02-16 16:30:34 +00:00
Kris Nuttycombe f6a3c68b09
Merge pull request #5544 from str4d/feature/wallet_orchard-builder_recipients
Add Orchard recipient support to the transaction builder
2022-02-16 08:22:43 -07:00
Jack Grigg da755c2ae8 Make `TransactionBuilder::AddOrchardOutput` memo optional
This replaces the previous use of the `NO_MEMO` constant, instead
passing the optionality through to Rust for handling.
2022-02-16 13:39:40 +00:00
therealyingtong 94ab8e4c77 Implement read and write for (txid, recipient) -> ua mapping.
Co-authored-by: Kris Nuttycombe <kris@nutty.land>
Co-authored-by: Jack Grigg <jack@z.cash>
2022-02-16 20:15:07 +08:00
Jack Grigg 2da0856e6f Add Orchard recipient support to the transaction builder
Co-authored-by: Kris Nuttycombe <kris@nutty.land>
2022-02-15 23:28:55 +00:00
Taylor Hornby a2c647d4bf Untested, not working yet, use libtinfo from the debian packages 2022-02-15 14:01:54 -07:00
Larry Ruane 698f7ba770 z_listreceivedbyaddress: reject UA component addr (#5537) 2022-02-15 09:19:09 -07:00
therealyingtong c7346e802e SendTransaction: Introduce recipients argument. 2022-02-15 09:50:00 +08:00
therealyingtong 6436230562 Move SendManyRecipient to wallet.h and introduce optional ua field. 2022-02-15 09:50:00 +08:00
Marius Kjærstad bf1cc206c0
Update copyright year to 2022 2021-12-27 00:30:51 +01:00
152 changed files with 11477 additions and 1786 deletions

View File

@ -1,9 +1,17 @@
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[source.crates-io]
replace-with = "vendored-sources"
[source."https://github.com/zcash/librustzcash.git"]
git = "https://github.com/zcash/librustzcash.git"
rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7"
rev = "9c1ed86c5aa8ae3b6d6dcc1478f2d6ba1264488f"
replace-with = "vendored-sources"
[source."https://github.com/nuttycom/hdwallet.git"]
git = "https://github.com/nuttycom/hdwallet.git"
rev = "576683b9f2865f1118c309017ff36e01f84420c9"
replace-with = "vendored-sources"
[source.vendored-sources]

View File

@ -70,19 +70,19 @@ jobs:
if: always()
rust-clippy:
name: Clippy (1.54.0)
name: Clippy (1.59.0)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.54.0
toolchain: 1.59.0
components: clippy
override: true
- name: Run clippy
uses: actions-rs/clippy-check@v1
with:
name: Clippy (1.54.0)
name: Clippy (1.59.0)
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features --all-targets -- -D warnings
@ -93,7 +93,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.54.0
toolchain: 1.59.0
override: true
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
*.exe
src/bitcoin
src/zcashd
src/zcashd-wallet-tool
src/zcash-cli
src/zcash-gtest
src/zcash-tx

View File

@ -1,6 +1,6 @@
Copyright (c) 2016-2021 The Zcash developers
Copyright (c) 2009-2021 The Bitcoin Core developers
Copyright (c) 2009-2021 Bitcoin Developers
Copyright (c) 2016-2022 The Zcash developers
Copyright (c) 2009-2022 The Bitcoin Core developers
Copyright (c) 2009-2022 Bitcoin Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

759
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -18,13 +18,17 @@ repository = "https://github.com/zcash/zcash"
readme = "README.md"
license = "MIT OR Apache-2.0"
edition = "2018"
rust-version = "1.56"
rust-version = "1.59"
[lib]
name = "rustzcash"
path = "src/rust/src/rustzcash.rs"
crate-type = ["staticlib"]
[[bin]]
name = "zcashd-wallet-tool"
path = "src/rust/bin/wallet_tool.rs"
[dependencies]
bellman = "0.11"
blake2b_simd = "1"
@ -32,12 +36,12 @@ blake2s_simd = "1"
bls12_381 = "0.6"
byteorder = "1"
group = "0.11"
incrementalmerkletree = "0.2"
incrementalmerkletree = "=0.3.0-beta.1"
libc = "0.2"
jubjub = "0.8"
memuse = "0.2"
nonempty = "0.7"
orchard = "=0.1.0-beta.1"
orchard = "=0.1.0-beta.2"
secp256k1 = "0.20"
subtle = "2.2"
rand_core = "0.6"
@ -61,10 +65,20 @@ metrics-exporter-prometheus = "0.6"
thiserror = "1"
tokio = { version = "1.0", features = ["rt", "net", "time", "macros"] }
# Wallet tool
# (also depends on thiserror, tracing, and tracing-subscriber with "env-filter" and "fmt" features)
anyhow = "1.0"
backtrace = "0.3"
clearscreen = "1.0"
gumdrop = "0.8"
rand = "0.8"
secrecy = "0.8"
time = { version = "0.3", features = ["formatting", "macros"] }
[dependencies.tracing-subscriber]
version = "0.3"
default-features = false
features = ["ansi", "env-filter", "time"]
features = ["ansi", "env-filter", "fmt", "time"]
[profile.release]
lto = true
@ -73,9 +87,9 @@ codegen-units = 1
[patch.crates-io]
hdwallet = { git = "https://github.com/nuttycom/hdwallet", rev = "576683b9f2865f1118c309017ff36e01f84420c9" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "9c1ed86c5aa8ae3b6d6dcc1478f2d6ba1264488f" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "9c1ed86c5aa8ae3b6d6dcc1478f2d6ba1264488f" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "9c1ed86c5aa8ae3b6d6dcc1478f2d6ba1264488f" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "9c1ed86c5aa8ae3b6d6dcc1478f2d6ba1264488f" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "9c1ed86c5aa8ae3b6d6dcc1478f2d6ba1264488f" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "9c1ed86c5aa8ae3b6d6dcc1478f2d6ba1264488f" }

View File

@ -48,6 +48,12 @@ $(BITCOIND_BIN): FORCE
$(BITCOIN_CLI_BIN): FORCE
$(MAKE) -C src $(@F)
check-security: FORCE
$(MAKE) -C src check-security
check-symbols: FORCE
$(MAKE) -C src check-symbols
if USE_LCOV
baseline.info:

View File

@ -1,4 +1,4 @@
Zcash 4.6.0-1
Zcash 4.7.0-rc1
<img align="right" width="120" height="80" src="doc/imgs/logo.png">
===========

View File

@ -1,13 +1,13 @@
dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 4)
define(_CLIENT_VERSION_MINOR, 6)
define(_CLIENT_VERSION_MINOR, 7)
define(_CLIENT_VERSION_REVISION, 0)
define(_CLIENT_VERSION_BUILD, 51)
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)
define(_COPYRIGHT_YEAR, 2021)
define(_COPYRIGHT_YEAR, 2022)
AC_INIT([Zcash],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_SUFFIX(_ZC_BUILD_VAL)],[https://github.com/zcash/zcash/issues],[zcash])
AC_CONFIG_SRCDIR([src/main.cpp])
AC_CONFIG_HEADERS([src/config/bitcoin-config.h])

View File

@ -1,3 +1,9 @@
zcash (4.7.0~rc1) stable; urgency=medium
* 4.7.0-rc1 release.
-- Electric Coin Company <team@electriccoin.co> Tue, 22 Mar 2022 21:14:02 +0000
zcash (4.6.0+1) stable; urgency=medium
* 4.6.0-1 release.

View File

@ -4,9 +4,9 @@ Upstream-Contact: Electric Coin Company <team@electriccoin.co>
Source: https://github.com/zcash/zcash
Files: *
Copyright: 2016-2021, The Zcash developers
2009-2021, Bitcoin Core developers
2009-2021, Bitcoin Developers
Copyright: 2016-2022, The Zcash developers
2009-2022, Bitcoin Core developers
2009-2022, Bitcoin Developers
License: Expat
Comment: The Bitcoin Core developers encompasses the current developers listed on
bitcoin.org, as well as the numerous contributors to the project.

View File

@ -189,8 +189,12 @@ def identify_executable(executable):
return None
if __name__ == '__main__':
args = sys.argv[1:]
allow_no_canary = "--allow-no-canary" in args
files = [arg for arg in args if not arg.startswith("--")]
retval = 0
for filename in sys.argv[1:]:
for filename in files:
try:
etype = identify_executable(filename)
if etype is None:
@ -201,6 +205,8 @@ if __name__ == '__main__':
failed = []
warning = []
for (name, func) in CHECKS[etype]:
if name == "Canary" and allow_no_canary:
continue
if not func(filename):
if name in NONFATAL:
warning.append(name)

View File

@ -1,10 +1,10 @@
---
name: "zcash-4.6.0-1"
name: "zcash-4.7.0-rc1"
enable_cache: true
distro: "debian"
suites:
- "stretch"
- "buster"
- "bullseye"
architectures:
- "amd64"
packages:

View File

@ -8,10 +8,10 @@ ifneq ($(host_os),mingw32)
$(package)_download_path=$(native_clang_download_path)
$(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz
$(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz
$(package)_sha256_hash_aarch64_linux=968d65d2593850ee9b37fcda074fb7641529bd45d2f976af6c8197de3c22612f
$(package)_download_file_linux=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz
$(package)_file_name_linux=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz
$(package)_sha256_hash_linux=76d0bf002ede7a893f69d9ad2c4e101d15a8f4186fbfe24e74856c8449acd7c1
$(package)_sha256_hash_aarch64_linux=15ff2db12683e69e552b6668f7ca49edaa01ce32cb1cbc8f8ed2e887ab291069
$(package)_download_file_linux=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
$(package)_file_name_linux=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
$(package)_sha256_hash_linux=84a54c69781ad90615d1b0276a83ff87daaeded99fbc64457c350679df7b4ff0
define $(package)_stage_cmds
mkdir -p $($(package)_staging_prefix_dir)/lib && \
@ -21,6 +21,7 @@ endef
else
# For Windows cross-compilation, use the MSYS2 binaries.
# Using 13.0.0-3 because 13.0.1-1 has missing `new` and `delete` symbols.
$(package)_download_path=https://repo.msys2.org/mingw/x86_64
$(package)_download_file=mingw-w64-x86_64-libc++-13.0.0-3-any.pkg.tar.zst
$(package)_file_name=mingw-w64-x86_64-libcxx-13.0.0-3-any.pkg.tar.zst

View File

@ -1,22 +1,26 @@
package=native_clang
$(package)_major_version=13
$(package)_version=13.0.0
$(package)_version=13.0.1
$(package)_download_path=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version)
$(package)_download_path_linux=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version)
$(package)_download_file_linux=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz
$(package)_file_name_linux=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-16.04.tar.xz
$(package)_sha256_hash_linux=76d0bf002ede7a893f69d9ad2c4e101d15a8f4186fbfe24e74856c8449acd7c1
$(package)_download_path_darwin=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_major_version).0.0
$(package)_download_file_darwin=clang+llvm-$($(package)_major_version).0.0-x86_64-apple-darwin.tar.xz
$(package)_file_name_darwin=clang-llvm-$($(package)_major_version).0.0-x86_64-apple-darwin.tar.xz
$(package)_sha256_hash_darwin=d051234eca1db1f5e4bc08c64937c879c7098900f7a0370f3ceb7544816a8b09
$(package)_download_file_linux=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
$(package)_file_name_linux=clang-llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz
$(package)_sha256_hash_linux=84a54c69781ad90615d1b0276a83ff87daaeded99fbc64457c350679df7b4ff0
$(package)_download_path_darwin=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version)
$(package)_download_file_darwin=clang+llvm-$($(package)_version)-x86_64-apple-darwin.tar.xz
$(package)_file_name_darwin=clang-llvm-$($(package)_version)-x86_64-apple-darwin.tar.xz
$(package)_sha256_hash_darwin=dec02d17698514d0fc7ace8869c38937851c542b02adf102c4e898f027145a4d
$(package)_download_path_freebsd=https://github.com/llvm/llvm-project/releases/download/llvmorg-$($(package)_version)
$(package)_download_file_freebsd=clang+llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz
$(package)_file_name_freebsd=clang-llvm-$($(package)_version)-amd64-unknown-freebsd12.tar.xz
$(package)_sha256_hash_freebsd=e579747a36ff78aa0a5533fe43bc1ed1f8ed449c9bfec43c358d953ffbbdcf76
$(package)_sha256_hash_freebsd=8101c8d3a920bf930b33987ada5373f43537c5de8c194be0ea10530fd0ad5617
$(package)_download_file_aarch64_linux=clang+llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz
$(package)_file_name_aarch64_linux=clang-llvm-$($(package)_version)-aarch64-linux-gnu.tar.xz
$(package)_sha256_hash_aarch64_linux=968d65d2593850ee9b37fcda074fb7641529bd45d2f976af6c8197de3c22612f
$(package)_sha256_hash_aarch64_linux=15ff2db12683e69e552b6668f7ca49edaa01ce32cb1cbc8f8ed2e887ab291069
ifneq (,$(wildcard /etc/arch-release))
$(package)_dependencies=native_libtinfo
endif
# Ensure we have clang native to the builder, not the target host
ifneq ($(canonical_host),$(build))

View File

@ -0,0 +1,22 @@
package=native_tinfo
$(package)_version=5.6.0
$(package)_download_path_linux=http://ftp.debian.org/debian/pool/main/n/ncurses/
$(package)_download_file_linux=libtinfo5_6.0+20161126-1+deb9u2_amd64.deb
$(package)_file_name_linux=libtinfo5_6.0+20161126-1+deb9u2_amd64.deb
$(package)_sha256_hash_linux=1d249a3193568b5ef785ad8993b9ba6d6fdca0eb359204c2355532b82d25e9f5
define $(package)_extract_cmds
mkdir -p $($(package)_extract_dir) && \
echo "$($(package)_sha256_hash) $($(package)_source)" > $($(package)_extract_dir)/.$($(package)_file_name).hash && \
$(build_SHA256SUM) -c $($(package)_extract_dir)/.$($(package)_file_name).hash && \
mkdir -p libtinfo5 && \
ar x --output libtinfo5 $($(package)_source_dir)/$($(package)_file_name) && \
cd libtinfo5 && \
tar xf data.tar.xz
endef
define $(package)_stage_cmds
pwd && \
mkdir -p $($(package)_staging_prefix_dir)/lib && \
cp libtinfo5/lib/x86_64-linux-gnu/libtinfo.so.5.9 $($(package)_staging_prefix_dir)/lib/libtinfo.so.5
endef

View File

@ -1,14 +1,14 @@
package=native_rust
$(package)_version=1.57.0
$(package)_version=1.59.0
$(package)_download_path=https://static.rust-lang.org/dist
$(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz
$(package)_sha256_hash_linux=ea0253784b2e5c22659ff148d492a68d2e11da734491714ebc61cc93896efcda
$(package)_sha256_hash_linux=0c1c2da3fa26372e5178123aa5bb0fdcd4933fbad9bfb268ffbd71807182ecae
$(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz
$(package)_sha256_hash_darwin=15ceffc4743434c19d08f73fb4edd6642b7fd8162ed7101d3e6ca2c691fcb699
$(package)_sha256_hash_darwin=d82204f536af0c7bfd2ea2213dc46b99911860cfc5517f7321244412ae96f159
$(package)_file_name_freebsd=rust-$($(package)_version)-x86_64-unknown-freebsd.tar.gz
$(package)_sha256_hash_freebsd=ebe96fa1f15e8d70c91e81aab7e0c341717b909225029f37d52fbdfa506e3fab
$(package)_sha256_hash_freebsd=83f9c49b6e9025b712fc5d65e49f1b6ad959966534cd39c8dc2ce2c85a6ca484
$(package)_file_name_aarch64_linux=rust-$($(package)_version)-aarch64-unknown-linux-gnu.tar.gz
$(package)_sha256_hash_aarch64_linux=d66847f7cf7b548ecb328c400ac4f691ee2aea6ff5cd9286ad8733239569556c
$(package)_sha256_hash_aarch64_linux=ab5da30a3de5433e26cbc74c56b9d97b569769fc2e456fc54378adc8baaee4f0
# Mapping from GCC canonical hosts to Rust targets
# If a mapping is not present, we assume they are identical, unless $host_os is
@ -17,10 +17,10 @@ $(package)_rust_target_x86_64-pc-linux-gnu=x86_64-unknown-linux-gnu
$(package)_rust_target_x86_64-w64-mingw32=x86_64-pc-windows-gnu
# Mapping from Rust targets to SHA-256 hashes
$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=4c70901d1cbddec9ea99fbd62b20f454d30e1ffbb48a21169ac823b3f02a1fbc
$(package)_rust_std_sha256_hash_x86_64-apple-darwin=c1eb892ddb50ebeed288b7aa8171ad46d62362bb26b2d82d2b463dfd45606dc2
$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=75c910899ed36a90b155e3a01c21b863000675867efc56f2b68c44edd4b7e18c
$(package)_rust_std_sha256_hash_x86_64-unknown-freebsd=1528a4bc7e3ba42da164bcc7b952dfa73048333c5b9254ce2d03db6bab6081e8
$(package)_rust_std_sha256_hash_aarch64-unknown-linux-gnu=81dbd37919f631f962ac0798111803eb8f06ffde608f0e5dd3682d701cf5566d
$(package)_rust_std_sha256_hash_x86_64-apple-darwin=959af8bafbc9f3916a1d1111d7378fdd7aa459410cdd2d3bbfc2d9d9a6db0683
$(package)_rust_std_sha256_hash_x86_64-pc-windows-gnu=9a67ae84e9e75efb57eeeab617e32379a555de336a30bb74a476e575cd38f63a
$(package)_rust_std_sha256_hash_x86_64-unknown-freebsd=cf5e4303dd7c3b70a738a2336097c9f2189c8b702a89a8c453d83ac0dee4602c
define rust_target
$(if $($(1)_rust_target_$(2)),$($(1)_rust_target_$(2)),$(if $(findstring darwin,$(3)),x86_64-apple-darwin,$(if $(findstring freebsd,$(3)),x86_64-unknown-freebsd,$(2))))

View File

@ -2,6 +2,10 @@ zcash_packages := libsodium utfcpp
packages := boost libevent zeromq $(zcash_packages) googletest
native_packages := native_clang native_ccache native_rust
ifneq (,$(wildcard /etc/arch-release))
native_packages += native_libtinfo
endif
wallet_packages=bdb
$(host_arch)_$(host_os)_native_packages += native_b2

View File

@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
.TH ZCASH-CLI "1" "January 2022" "zcash-cli v4.6.0-1" "User Commands"
.TH ZCASH-CLI "1" "March 2022" "zcash-cli v4.7.0-rc1" "User Commands"
.SH NAME
zcash-cli \- manual page for zcash-cli v4.6.0-1
zcash-cli \- manual page for zcash-cli v4.7.0-rc1
.SH DESCRIPTION
Zcash RPC client version v4.6.0\-1
Zcash RPC client version v4.7.0\-rc1
.PP
In order to ensure you are adequately protecting your privacy when using Zcash,
please see <https://z.cash/support/security/>.
@ -29,7 +29,7 @@ Specify configuration file (default: zcash.conf)
.HP
\fB\-datadir=\fR<dir>
.IP
Specify data directory
Specify data directory (this path cannot use '~')
.HP
\fB\-stdin\fR
.IP
@ -79,8 +79,8 @@ Timeout in seconds during HTTP requests, or 0 for no timeout. (default:
In order to ensure you are adequately protecting your privacy when using Zcash,
please see <https://z.cash/support/security/>.
Copyright (C) 2009-2021 The Bitcoin Core Developers
Copyright (C) 2015-2021 The Zcash Developers
Copyright (C) 2009-2022 The Bitcoin Core Developers
Copyright (C) 2015-2022 The Zcash Developers
This is experimental software.

View File

@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
.TH ZCASH-TX "1" "January 2022" "zcash-tx v4.6.0-1" "User Commands"
.TH ZCASH-TX "1" "March 2022" "zcash-tx v4.7.0-rc1" "User Commands"
.SH NAME
zcash-tx \- manual page for zcash-tx v4.6.0-1
zcash-tx \- manual page for zcash-tx v4.7.0-rc1
.SH DESCRIPTION
Zcash zcash\-tx utility version v4.6.0\-1
Zcash zcash\-tx utility version v4.7.0\-rc1
.SS "Usage:"
.TP
zcash\-tx [options] <hex\-tx> [commands]
@ -91,8 +91,8 @@ Set register NAME to given JSON\-STRING
In order to ensure you are adequately protecting your privacy when using Zcash,
please see <https://z.cash/support/security/>.
Copyright (C) 2009-2021 The Bitcoin Core Developers
Copyright (C) 2015-2021 The Zcash Developers
Copyright (C) 2009-2022 The Bitcoin Core Developers
Copyright (C) 2015-2022 The Zcash Developers
This is experimental software.

View File

@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.13.
.TH ZCASHD "1" "January 2022" "zcashd v4.6.0-1" "User Commands"
.TH ZCASHD "1" "March 2022" "zcashd v4.7.0-rc1" "User Commands"
.SH NAME
zcashd \- manual page for zcashd v4.6.0-1
zcashd \- manual page for zcashd v4.7.0-rc1
.SH DESCRIPTION
Zcash Daemon version v4.6.0\-1
Zcash Daemon version v4.7.0\-rc1
.PP
In order to ensure you are adequately protecting your privacy when using Zcash,
please see <https://z.cash/support/security/>.
@ -49,7 +49,7 @@ Run in the background as a daemon and accept commands
.HP
\fB\-datadir=\fR<dir>
.IP
Specify data directory
Specify data directory (this path cannot use '~')
.HP
\fB\-paramsdir=\fR<dir>
.IP
@ -352,6 +352,14 @@ by TxID)
Delete all wallet transactions and only recover those parts of the
blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g.
account owner and payment request information, 2 = drop tx meta data)
.HP
\fB\-walletrequirebackup=\fR<bool>
.IP
By default, the wallet will not allow generation of new spending keys &
addresses from the mnemonic seed until the backup of that seed has been
confirmed with the `zcashd\-wallet\-tool` utility. A user may start zcashd
with `\-walletrequirebackup=false` to allow generation of spending keys
even if the backup has not yet been confirmed.
.PP
ZeroMQ notification options:
.HP
@ -558,8 +566,8 @@ console, 600 otherwise)
In order to ensure you are adequately protecting your privacy when using Zcash,
please see <https://z.cash/support/security/>.
Copyright (C) 2009-2021 The Bitcoin Core Developers
Copyright (C) 2015-2021 The Zcash Developers
Copyright (C) 2009-2022 The Bitcoin Core Developers
Copyright (C) 2015-2022 The Zcash Developers
This is experimental software.

View File

@ -13,23 +13,23 @@ be generated on load of the wallet, or the first time the wallet is unlocked,
and is available via the `z_exportwallet` RPC call. All new addresses produced
by the wallet are now derived from this seed using the HD wallet functionality
described in ZIP 32 and ZIP 316. For users upgrading an existing Zcashd wallet,
it is recommended that the wallet be backed up prior to upgrading to the 4.5.2
it is recommended that the wallet be backed up prior to upgrading to the 4.7.0
Zcashd release.
Following the upgrade to 4.5.2, Zcashd will require that the user confirm that
Following the upgrade to 4.7.0, Zcashd will require that the user confirm that
they have backed up their new emergency recovery phrase, which may be obtained
from the output of the `z_exportwallet` RPC call. This confirmation can be
performed manually using the `zcashd-wallet-tool` utility that is supplied with
this release. The wallet will not allow the generation of new addresses until
this confirmation has been performed. It is recommended that after this
upgrade, that funds tied to preexisting addresses be migrated to newly
generated addresses so that all wallet funds are recoverable using the
emergency recovery phrase going forward. If you choose not to migrate funds in
this fashion, you will continue to need to securely back up the entire
`wallet.dat` file to ensure that you do not lose access to existing funds;
EXISTING FUNDS WILL NOT BE RECOVERABLE USING THE EMERGENCY RECOVERY PHRASE
UNLESS THEY HAVE BEEN MOVED TO A NEWLY GENERATED ADDRESS FOLLOWING THE 4.5.2
UPGRADE.
performed manually using the `zcashd-wallet-tool` utility that is supplied
with this release (built or installed in the same directory as `zcashd`).
The wallet will not allow the generation of new addresses until this
confirmation has been performed. It is recommended that after this upgrade,
that funds tied to preexisting addresses be migrated to newly generated
addresses so that all wallet funds are recoverable using the emergency
recovery phrase going forward. If you choose not to migrate funds in this
fashion, you will continue to need to securely back up the entire `wallet.dat`
file to ensure that you do not lose access to existing funds; EXISTING FUNDS
WILL NOT BE RECOVERABLE USING THE EMERGENCY RECOVERY PHRASE UNLESS THEY HAVE
BEEN MOVED TO A NEWLY GENERATED ADDRESS FOLLOWING THE 4.7.0 UPGRADE.
New RPC Methods
---------------
@ -81,11 +81,15 @@ Wallet
pool boundaries, they must be explicitly enabled via a parameter to
the 'z_sendmany' call.
- A new boolean parameter, `allowRevealedAmounts`, has been added to the
list of arguments accepted by 'z_sendmany'. This parameter defaults to
`false` and is only required when the transaction being constructed
would reveal transaction amounts as a consequence of ZEC value crossing
shielded pool boundaries via the turnstile.
- A new string parameter, `privacyPolicy`, has been added to the list of
arguments accepted by `z_sendmany`. This parameter enables the caller to
control what kind of information they permit `zcashd` to reveal when creating
the transaction. If the transaction can only be created by revealing more
information than the given strategy permits, `z_sendmany` will return an
error. The parameter defaults to `LegacyCompat`, which applies the most
restrictive strategy `FullPrivacy` when a Unified Address is present as the
sender or a recipient, and otherwise preserves existing behaviour (which
corresponds to the `AllowFullyTransparent` policy).
- Since Sprout outputs are no longer created (with the exception of change)
'z_sendmany' no longer generates payment disclosures (which were only

View File

@ -0,0 +1,538 @@
Notable changes
===============
Mnemonic Recovery Phrases
-------------------------
The zcashd wallet has been modified to support BIP 39, which describes how to
derive the wallet's HD seed from a mnemonic phrase. The mnemonic phrase will
be generated on load of the wallet, or the first time the wallet is unlocked,
and is available via the `z_exportwallet` RPC call. All new addresses produced
by the wallet are now derived from this seed using the HD wallet functionality
described in ZIP 32 and ZIP 316. For users upgrading an existing Zcashd wallet,
it is recommended that the wallet be backed up prior to upgrading to the 4.7.0
Zcashd release.
Following the upgrade to 4.7.0, Zcashd will require that the user confirm that
they have backed up their new emergency recovery phrase, which may be obtained
from the output of the `z_exportwallet` RPC call. This confirmation can be
performed manually using the `zcashd-wallet-tool` utility that is supplied
with this release (built or installed in the same directory as `zcashd`).
The wallet will not allow the generation of new addresses until this
confirmation has been performed. It is recommended that after this upgrade,
that funds tied to preexisting addresses be migrated to newly generated
addresses so that all wallet funds are recoverable using the emergency
recovery phrase going forward. If you choose not to migrate funds in this
fashion, you will continue to need to securely back up the entire `wallet.dat`
file to ensure that you do not lose access to existing funds; EXISTING FUNDS
WILL NOT BE RECOVERABLE USING THE EMERGENCY RECOVERY PHRASE UNLESS THEY HAVE
BEEN MOVED TO A NEWLY GENERATED ADDRESS FOLLOWING THE 4.7.0 UPGRADE.
New RPC Methods
---------------
- 'walletconfirmbackup' This newly created API checks a provided emergency
recovery phrase against the wallet's emergency recovery phrase; if the phrases
match then it updates the wallet state to allow the generation of new addresses.
This backup confirmation workflow can be disabled by starting zcashd with
`-requirewalletbackup=false` but this is not recommended unless you know what
you're doing (and have otherwise backed up the wallet's recovery phrase anyway).
For security reasons, this RPC method is not intended for use via zcash-cli
but is provided to enable `zcashd-wallet-tool` and other third-party wallet
interfaces to satisfy the backup confirmation requirement. Use of the
`walletconfirmbackup` API via zcash-cli would risk that the recovery phrase
being confirmed might be leaked via the user's shell history or the system
process table; `zcashd-wallet-tool` is specifically provided to avoid this
problem.
- 'z_getbalanceforviewingkey' This newly created API allows a user to obtain
balance information for funds visible to a Sapling or Unified full
viewing key; if a Sprout viewing key is provided, this method allows
retrieval of the balance only in the case that the wallet controls the
corresponding spending key.
RPC Changes
-----------
- The results of the 'dumpwallet' and 'z_exportwallet' RPC methods have been modified
to now include the wallet's newly generated emergency recovery phrase as part of the
exported data.
- The results of the 'getwalletinfo' RPC have been modified to return two new fields:
`mnemonic_seedfp` and `legacy_seedfp`, the latter of which replaces the field that
was previously named `seedfp`.
Wallet
------
'z_sendmany'
------------
- The 'z_sendmany' RPC call no longer permits Sprout recipients in the
list of recipient addresses. Transactions spending Sprout funds will
still result in change being sent back into the Sprout pool, but no
other `Sprout->Sprout` transactions will be constructed by the Zcashd
wallet.
- The restriction that prohibited `Sprout->Sapling` transactions has been
lifted; however, since such transactions reveal the amount crossing
pool boundaries, they must be explicitly enabled via a parameter to
the 'z_sendmany' call.
- A new string parameter, `privacyPolicy`, has been added to the list of
arguments accepted by `z_sendmany`. This parameter enables the caller to
control what kind of information they permit `zcashd` to reveal when creating
the transaction. If the transaction can only be created by revealing more
information than the given strategy permits, `z_sendmany` will return an
error. The parameter defaults to `LegacyCompat`, which applies the most
restrictive strategy `FullPrivacy` when a Unified Address is present as the
sender or a recipient, and otherwise preserves existing behaviour (which
corresponds to the `AllowFullyTransparent` policy).
- Since Sprout outputs are no longer created (with the exception of change)
'z_sendmany' no longer generates payment disclosures (which were only
available for Sprout outputs) when the `-paymentdisclosure` experimental
feature flag is set.
Changelog
=========
Charlie O'Keefe (2):
Update base image used by Dockerfile from debian 10 to debian 11
Remove stretch (debian 9), add bullseye (debian 11) in gitian descriptor
Conrado Gouvea (1):
Add comment to zcash_script_new_precomputed_tx about references to input buffers
Daira Hopwood (25):
Avoid a warning by explicitly calling drop.
Replace call to drop with zeroization.
contrib/devtools/rust-deps-graph.sh: allow overriding the arguments to `cargo deps`.
Cosmetic whitespace change
Make a zcashd-wallet-tool executable.
Attempt to fix linking problem on ARM.
Add some text about choosing location of the physical backup. Add TODO for better handling of file not found and permission errors.
Move `wallet_tool.rs` from `src/rust/src` into `src/rust/bin`. Also add a brief description of `zcashd-wallet-tool` to `src/rust/README.md`.
Improved error handling.
The recovery phrase confirmation and `zcashd-wallet-tool` are being introduced in zcashd v4.7.0.
Refactor use of `export_path` as suggested.
Tweak the wording of the fallback messages when the terminal cannot be automatically cleared.
Simplify extraction of recovery phrase.
Improve memory hygiene, and use -stdin to pass the recovery phrase to the child zcash-cli process.
Cleanups to error handling for the first invocation of zcash-cli.
Use the tracing crate for debugging.
Improve error message when the config file cannot be found, taking into account -conf and -datadir.
Ensure the buffer used in `prompt` is zeroized even on error.
Document that '~' cannot be used in `-datadir` (see #5661).
Set `meta` for `-datadir` option.
Simplify debug tracing.
Correct the fallback instruction for how to clear the terminal on macOS: pressing Command + K also clears scrollback.
Include $(bin_SCRIPTS) in `check-symbols`, `check-security`, and `clean` targets. Checking for stack canaries in `check-security` is disabled for Rust executables (Rust does support `-Z stack-protector=all` but only for the nightly compiler).
qa/zcash/full_test_suite.py: enable `test_rpath_runpath` for Rust binaries, and reenable `test_fortify_source` for C++ binaries.
Tweaks to message text.
Dimitris Apostolou (1):
Fix typos
Jack Grigg (65):
wallet: Implement `z_getnewaccount`
wallet: Implement `z_getaddressforaccount`
wallet: Implement `z_listunifiedreceivers`
wallet: Show UAs owned by the wallet in `z_viewtransaction`
wallet: Reverse order of arguments to z_getaddressforaccount
rust: Add missing Orchard receiver check in UA parsing
rust: Add missing checks for shielded UFVK items
rust: Add missing checks for transparent UFVK items
wallet: Implement `z_getbalanceforaccount`
wallet: Fix account generation bug
wallet: Implement `z_getbalanceforaddress`
wallet: Don't show Sapling receivers from UAs in `z_listaddresses`
wallet: Show UAs instead of Sapling receivers in `z_listunspent`
wallet: Remove `CWallet::GetKeyFromPool`
wallet: Store internal transparent keys in the keypool
wallet: Separate counters for external and internal transparent keys
Add Orchard recipient support to the transaction builder
Make `TransactionBuilder::AddOrchardOutput` memo optional
Return UFVK from `CWallet::GenerateNewUnifiedSpendingKey`
Rename to `ZcashdUnifiedSpendingKey::GetSaplingKey` for consistency
Trial-decrypt candidate Sapling receivers with all wallet UFVKs
Add mappings from Orchard receivers to IVKs to the wallet
Add Orchard to default UA receiver types
Fix semantic merge conflicts
Select Orchard receivers preferentially from UAs
Regenerate `TxDigests` after rebuilding tx with Orchard bundle
qa: Bump all postponed dependencies
qa: Postpone recent CCache release
depends: Update Rust to 1.59.0
depends: Update Clang / libcxx to LLVM 13.0.1
cargo update
rust: Fix clippy lint
Ensure the view's best Orchard anchor matches the previous block
Add missing `view.PopAnchor(_, ORCHARD)` in `DisconnectBlock`
Add RPC test for the Orchard commitment tree bug on first NU5 testnet
Use `std::optional` in `CValidationInterface::GetAddressForMining`
Select Orchard receivers from UA miner addresses once NU5 activates
miner: Manually add dummy Orchard output with Orchard miner address
rpc: Handle keypool exhaustion separately in `generate` RPC
depends: Revert to `libc++ 13.0.0-3` for Windows cross-compile
Add Orchard spend support to the transaction builder
wallet: Alter `OrchardWallet::GetSpendInfo` to return the spending key
Improve FFI interface documentation
Refactor `CWallet::GenerateChangeAddressForAccount`
Add unit tests for `SpendableInputs::LimitToAmount`
Fix bug in `SpendableInputs::LimitToAmount`
Select spendable inputs based on recipient and change pool types
Implement opportunistic shielding in `SpendableInputs::LimitToAmount`
Add Orchard cases to note selection logic
Add Orchard to change address generation
Add support for sending Orchard funds in `z_sendmany`
Set default Orchard anchor confirmations to 1
build: Fix `zcash/address/orchard.hpp` filename in `src/Makefile.am`
z_sendmany: Replace `allowRevealedAmount` with `privacyStrategy`
z_sendmany: Expand `privacyPolicy` cases
build: Add missing `util/match.h` to `src/Makefile.am`
wallet: Add seedfp to transparent keys in dumpwallet / z_exportwallet
Fix bugs in wallet_addresses RPC test
wallet: Fix bugs in `listaddresses`
wallet: Fix Sapling address bug in `listaddresses`
wallet: Fix expected `listaddresses` sources in `rpc_wallet_tests`
qa: Exclude `native_libtinfo` from dependency update checks
cargo update
make-release.py: Versioning changes for 4.7.0-rc1.
make-release.py: Updated manpages for 4.7.0-rc1.
John Newbery (1):
Log calls to getblocktemplate
Jonas Schnelli (1):
[Wallet] add HD xpriv to dumpwallet
Kris Nuttycombe (243):
Derive random HD seeds from ZIP-339 seed phrases.
Add support for externally searching for valid Sapling diversifiers.
Adds basic unified spending key derivation.
Add unified full viewing keys and Zcash-internal unified addresses.
Use the default UA-based Sapling address for the saplingmigration tool.
Fix tests for wallet operations on legacy Sapling keys.
Remove unused forward declaration.
Update librustzcash dependency version.
Apply suggestions from code review
Derive transparent keys from mnemonic seed.
Generate legacy Sapling addresses from the mnemonic seed.
Replace account ID with ZIP-0032 key path in listaddresses output.
Use legacy address for Sapling migration until UA functionality is available to the RPC tests.
Revert change to the type of GenerateNewKey
Fix wallet import/export test
Use 0x7FFFFFFF for the legacy account ID.
Require backup of the emergency recovery phrase.
Use hardened derivation for the legacy Sapling key at the address index level.
Address comments from code review.
Restore legacy HD seed storage & retrieval tests.
Fix spurious test passage.
Move CKeyMetadata back to wallet.h
Clean up format of recovery information in the wallet dump.
Use SecureString for mnemonic phrase.
Apply suggestions from code review
Fix diversifier_index_t less than operator.
Restore FindAddress comment
Fix transparent BIP-44 keypaths.
Fix naming of unified spending & full viewing keys
Fix max transparent diversifier index.
Clean up handling of mnemonic seed metadata.
Restore legacy HDSeed encryption tests.
Style fix in BIP 32 path account parsing test.
Do not strip quotes when verifying mnemonic seed.
Apply suggestions from code review
Fix name of menmonic entropy length constant.
Fix polymorphism of string serialization.
Document mnemonic-seed-related RPC method changes & update changelog.
Minor cleanup of the code that searches for a valid transparent key.
Generalize keypath parsing over account id.
Update documentation for GenerateNewSeed.
Use MnemonicSeed instead of HDSeed where appropriate.
Add diversifier_index_t::ToTransparentChildIndex
Only maintain CKeyID for the transparent part of ZcashdUnifiedAddress
Minor naming fixes
Parameterize HD keypath parsing by coin type.
Fix error message text to refer to zcashd-wallet-tool rather than the RPC method.
Apply suggestions from code review
Minor textual fixes to release notes.
Improve documentation of the `-walletrequirebackup` zcashd wallet configuration option.
Add libzcash::AccountId type.
Adds Orchard Address, IncomingViewingKey, FullViewingKey, and SpendingKey types.
Apply suggestions from code review
Update orchard & librustzcash dependency versions.
Remove incorrect FFI method documentation.
Remove Orchard spending key equality implementation.
Refine structure of Zcash address generation.
Use CKeyID and CScriptID instead of new P2PKH/P2SHAddress classes.
Remove ZcashdUnifiedAddress in favor of UnifiedAddress
Update to ufvk zcash_address build.
Adds SaplingDiversifiableFullViewingKey
Add Rust FFI components for unified full viewing keys.
Add UnifiedFullViewingKey type.
Apply suggestions from code review
Add tests for ufvk roundtrip serialization.
Apply suggestions from code review
Apply suggestions from code review
Apply suggestions from code review
Add functions for generating BIP-44 and ZIP-32 keypaths
Check the output of zip339_phrase_to_seed in MnemonicSeed initialization.
Compute key id for UFVKs.
Add ZcashdUnifiedKeyMetadata and libzcash::ReceiverType
Add unified key components to the transparent & Sapling wallet parts.
Store ufvks to the wallet database.
Add unified address tracking to KeyStore
Load unified full viewing keys from the walletdb.
Add key metadata to walletdb.
Add unified address generation.
AddTransparentSecretKey does not need to take a CExtKey
Add newly generated transparent UA receivers to the wallet.
Add CWallet::GetUnifiedForReceiver
Add tests for keystore storage and retrieval of UFVKs.
Add test for wallet UA generation & detection.
Add test for CKeyStore::AddUnifiedAddress
Fix handling of unified full viewing key metadata.
Apply suggestions from code review
Only derive ZcashdUnifiedFullViewingKey from UnifiedFullViewingKey
Rename `ZcashdUnifiedSpendingKeyMetadata` -> `ZcashdUnifiedAccount`
Remove unused ufvkid argument from AddTransparentSecretKey
Ensure that unified address metadata is always correctly populated.
Apply suggestions from code review
Make `FindAddress` correctly check for maximum transparent child index.
Use Bip44TransparentAccountKeyPath() for Bip44AccountChains keypath construction.
Improve documentation of UFVK/UA metadata storage semantics.
Apply suggestions from code review
Fix encoding order of unified addresses.
Remove the `InvalidEncoding` type from key & address variants.
Use CKeyID and CScriptID instead of new P2PKH/P2SHAddress classes.
Remove spurious uses of HaveSpendingKeyForPaymentAddress
Add raw transparent address types to PaymentAddress
Remove spurious variant from asyncrpcoperation_sendmany
Remove `RawAddress`
Replace `DecodeDestination` in `GetMinerAddress` with `DecodePaymentAddress`
Remove uses of KeyIO::DecodeDestination
Remove a use of KeyIO::DecodeDestination in z_shieldcoinbase
Use libzcash::PaymentAddress instead of std::string in mergetoaddress
Improve error messages in the case of taddr decoding failure.
Apply suggestions from code review
Apply suggestions from code review
Use transaction builder for asyncrpcoperation_sendmany.
Transaction builder must not set consensus branch ID for V4 transactions.
Fix conditions around dust thresholds.
Require an explicit flag to allow cross-pool transfers in z_sendmany.
Apply suggestions from code review
Return z_sendmany errors synchronously when possible.
Update release notes to reflect z_sendmany changes
Move FindSpendableInputs to CWallet
Replace the badly-named `PaymentSource` with `ZTXOSelector`
Add support for unified addresses to CWallet::ToZTXOSelector
Replace `HaveSpendingKeyForAddress` with checks at ZTXOSelector construction.
Modify CWallet::FindSpendableInputs to use ZTXOSelector
Add CWallet::FindAccountForSelector
Add RecipientAddress type.
Use libzcash::RecipientAddress for z_sendmany recipients.
Apply suggestions from code review
Rename ZTXOSelector::SpendingKeysAvailable -> RequireSpendingKeys
Make `FindSpendableInputs` respect `ZTXOSelector::RequireSpendingKeys()`
Clarify documentation of CWallet::FindAccountForSelector
Use `ZTXOSelector::IncludesSapling` rather than selector internals.
Add documentation for `UnifiedAddress::GetPreferredRecipientAddress`
Add correct selection of change addresses to z_sendmany
Add a first-class type for transparent full viewing keys.
Implement OVK selection for z_sendmany.
Implement derivation of the internal Sapling spending key.
Use a BIP 44 change address for change when sending from legacy t-addrs.
Add a check for internal vs. external outputs to wallet_listreceived test.
Fix z_sendmany handling of transparent OVK derivation in the ANY_TADDR case.
Simplify determination of valid change types.
Add failing tests for z_sendmany ANY_TADDR -> UA and UA -> <legacy_taddr>
Add gtest for change address derivation.
Fix variable shadowing in change address derivation & add change IVK to the keystore.
GenerateLegacySaplingZKey only needs to return an address, not an extfvk.
Rename AddSaplingIncomingViewingKey -> AddSaplingPaymentAddress
Do not add Sapling addresses to the wallet by default when adding extfvks.
Fix a bug in the generation of addresses from UFVKs
Add accessor method for Sapling IVKs to SaplingDiversifiableFullViewingKey
Address TODOs in rpc-tests/wallet-accounts.py
Add a few additional cases to z_sendmany RPC tests.
Update librustzcash dependency.
Fix nondeterministic test error (checking for the wrong error case).
Use z_shieldcoinbase for Sprout funds in wallet_listreceived tests.
Apply suggestions from code review
Apply suggestions from code review.
Fix nondeterministic test failure in wallet_listreceivedby.py
Fix locking in z_getbalanceforaddress and z_getbalanceforaccount
Replace z_getbalanceforaddress with z_getbalanceforviewingkey
Clarify documentation of z_getbalanceforviewingkey for Sprout viewing keys.
Implement PaymentAddressBelongsToWallet for unified addresses.
Add unified address support to GetSourceForPaymentAddress
Address comments from review.
Add change field to z_listreceivedbyaddress for transparent addrs.
Fix missing std::variant header that was breaking Darwin builds.
Add test vectors for UFVK derivation
Rename sapling-specific zip32 FFI methods.
Make SaveRecipientMappings polymorphic in RecipientMapping type.
Add Rust backend for Orchard components of the wallet.
Add GetFilteredNotes to Orchard wallet.
Add test for Orchard wallet note detection.
Move parsing of unified addresses to UnifiedAddress.
Remove txid field from TxNotes
Apply suggestions from code review
Add various bits of documentation
Add Orchard components to unified address
Add Orchard components to unified full viewing keys
Add Orchard components to unified spending keys
Remove OrchardSpendingKey serialization code
Select Orchard notes in FindSpendableInputs
GenerateNewKey must be guarded by a cs_wallet lock
Filter returned Orchard notes by minimum confirmations.
Log outpoint for failed Sapling witness lookup.
Add a roundtrip test for Orchard merkle frontier serialization from the C++ side.
Add test for Orchard contribution to z_gettotalbalance
Respect minDepth argument for Orchard notes in GetFilteredNotes
Update MSRV for lints.
Update incrementalmerkletree version
Split LoadWalletTx from AddToWallet
Fix missing locks for GenerateNewUnifiedSpendingKey tests.
Record when notes are detected as being spent in the Orchard wallet.
Reset Orchard wallet state when rescanning from below NU5 activation.
Check wallet latest anchor against hashFinalOrchardRoot in ChainTip.
Remove assertions that require Orchard wallet persistence to satisfy.
Add a test for Orchard note detection.
Assert we never attempt to checkpoint the Orchard wallet at a negative block height.
Apply suggestions from code review
Add an `InPoint` type to the Orchard wallet to fix incorrect conflict data.
Apply suggestions from code review
Fix missing update to `last_checkpoint` on rewind.
Track mined-ness instead of spent-ness of notes in the Orchard wallet.
Apply suggestions from code review
Respect maxDepth for Orchard notes in GetFilteredNotes
Set number of confirmations for Orchard notes returned by FindSpendableInputs
Update walletTx with decrypted Orchard action metadata.
Persist Orchard action index/IVK mappings in CWalletTx
Restore decrypted notes to the wallet.
Update tests with rollback checks.
Apply suggestions from code review
Restore mined block heights when restoring decrypted notes.
Apply suggestions from code review
Return std::optional<CExtKey> from CExtKey::Master
Ensure that Orchard spentness information is repopulated by LoadUnifiedCaches.
Modify `join_network` in tests to skip mempool resync.
Address suggestions from code review on #5637
Apply suggestions from code review
Serialize the Orchard note commitment tree to the wallet.
Update orchard_wallet_add_notes_from_bundle documentation.
Derive the new mnemonic seed from the legacy HD seed, if one is available.
Fix indentation.
Apply suggestions from code review
Explicitly specify the change address in the Sapling migration.
Add TODO for Orchard z_listunspent integration.
Document that z_getnewaccount and z_getaddressforaccount replace z_getnewaddress.
Add orchard support to z_getnotescount
Document that Orchard keys are not supported in z_importkey help
Return the correct error for `z_getbalance` if a UA does not correspond to an account in the wallet..
Update documentation of z_importviewingkey and z_exportviewingkey to address unified keys.
Add UnifiedAddress variant to ZTXOSelector
Eliminate redundancy between the wallet and the keystore.
Simplify retrieval of unified account by address.
Add OrchardWallet::GetTxActions
Apply suggestions from code review
Update z_viewtransaction documentation to correctly represent field names.
Add debug printing for receivers and recipient addresses.
Correctly report change outputs in z_viewtransaction.
Return the default unified address if we have the UFVK but no address metadata.
Fix missing AllowRevealedSenders flag in test.
Uncomment addtional tests that depend on z_viewtransaction.
Minor rename & documentation improvement.
Add RecipientType to GetPaymentAddressForRecipient result.
Make CWallet::DefaultReceiverTypes height-dependent.
Return failure rather than asserting on WriteRecipientMapping failure.
Lock cs_main for accesses to chainActive in GetPaymentAddressForRecipient.
Fix legacy address handling in CWallet::GetPaymentAddressForRecipient
Documentation fix for UnifiedAddressForReciever (+ spelling corrections.)
Larry Ruane (12):
add ParseArbitraryInt() for diversifier index
add -orchardwallet experimental feature flag
Add new and modify existing Orchard RPCs, non-functional
mining: submitblock: log detailed equihash solution error
allow UA as z_shieldcoinbase destination
fix minconf parsing for z_getbalanceforaccount and z_getbalanceforaddress
Update z_listreceivedbyaddress to support unified addresses (5467)
fix wallet_listreceived.py, add blockdata to taddr output
z_listreceivedbyaddress: reject UA component addr (#5537)
add functional test
document global variables
update listaddresses RPC for UAs, Orchard
Marius Kjærstad (1):
Update copyright year to 2022
Pieter Wuille (2):
Fix csBestBlock/cvBlockChange waiting in rpc/mining
Modernize best block mutex/cv/hash variable naming
Sean Bowe (5):
wallet: consolidate unified key/address/account map reconstruction
wallet: restore Orchard secret keys from mnemonic seed
wallet: restore orchard address to IVK mappings during wallet loading
wallet: rather than assert, error in case of inconsistency between FVK and address
wallet: add logging for failure cases in unified cache loading
Steven Smith (8):
Lock cs_main prior to calling blockToJSON
Mark z_gettotalbalance and dumpwallet as deprecated
Add Orchard support to the z_gettreestate RPC
Update transaction size estimation to include V5 transactions
Extend uniqueness check in z_sendmany to UA receivers
Load previously persisted sent transaction recipient metadata back into the wallet.
Add Orchard & unified address support to z_viewtransaction.
Ensure z_viewtransaction returns Orchard details
Taylor Hornby (1):
Untested, not working yet, use libtinfo from the debian packages
sasha (12):
on Arch only, use Debian's libtinfo5_6.0 to satisfy clang
explain the 0x0f0f[..]0f0f powLimit constant for regtest
remove superfluous space at end of native_packages line
gtests ordering: change wallet filename in WriteZkeyDirectToDb
gtests ordering: ContextualCheckBlockTest's TearDown deactivates Blossom
gtests ordering: CheckBlock.VersionTooLow calls SelectParams(MAIN)
implement AtomicTimer::zeroize() that resets start_time and total_time
gtests ordering: make Metrics.GetLocalSolPS idempotent
gtests ordering: clean up wallet files before each WalletZkeysTest
make librustzcash_init_zksnark_params idempotent
move proof parameter loading out of gtest/main.cpp and into utiltest.cpp
Call LoadProofParameters() in gtests that need proofs
Ying Tong Lai (18):
Move SendManyRecipient to wallet.h and introduce optional ua field.
SendTransaction: Introduce recipients argument.
Implement read and write for (txid, recipient) -> ua mapping.
z_sendmany: Only get ua if decoded is ua variant.
ShieldToAddress: Factor out static shieldToAddress() helper.
Docfixes.
CSerializeRecipientAddress: add Read method and make constructor private.
WriteRecipientMapping: Check that receiver exists in UA.
wallet_sendmany_any_taddr.py: Test sending from a change taddr.
wallet_sendmany_any_taddr.py: Test sending output from expired tx.
FindSpendableInputs: Add nDepth < 0 check.
wallet_sendmany_any_taddr.py: Expect expired tx to be ignored.
Orchard: invalidate mempool transactions that use orphaned anchors.
coins_tests.cpp: Add Orchard nullifier to TxWithNullifiers().
coins_tests: Update tests to include Orchard case.
CWallet::GetConflictS: Handle conflicting Orchard spends.
z_getbalance: Handle Unified Address case.
Adapt RPC tests to use z_getbalance for UAs.
ying tong (2):
Apply docfixes from code review
Style improvements in RPC tests.
Zancas Wilcox (4):
blake2b/s is integrated into hashlib, drop external python package dependency
update doctest in gtest suite to prefer hashlib
enforce usage of the get_tests comptool interface as ComparisonTestFramework method
All implementations of ComparisonTestFramework were overriding num_nodes

View File

@ -60,6 +60,7 @@ BASE_SCRIPTS= [
'wallet_persistence.py',
'wallet_listnotes.py',
# vv Tests less than 60s vv
'orchard_reorg.py',
'fundrawtransaction.py',
'reorg_limit.py',
'mempool_limit.py',
@ -72,6 +73,7 @@ BASE_SCRIPTS= [
'wallet_changeindicator.py',
'wallet_import_export.py',
'wallet_isfromme.py',
'wallet_orchard.py',
'wallet_nullifiers.py',
'wallet_sapling.py',
'wallet_sendmany_any_taddr.py',

View File

@ -6,9 +6,11 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
NU5_BRANCH_ID,
assert_equal,
connect_nodes_bi,
get_coinbase_address,
nuparams,
start_nodes,
wait_and_assert_operationid_status,
)
@ -17,6 +19,7 @@ from decimal import Decimal
SPROUT_TREE_EMPTY_ROOT = "59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7"
SAPLING_TREE_EMPTY_ROOT = "3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb"
ORCHARD_TREE_EMPTY_ROOT = "2fd8e51a03d9bbe2dd809831b1497aeb68a6e37ddf707ced4aa2d8dff13529ae"
NULL_FIELD = "0000000000000000000000000000000000000000000000000000000000000000"
# Verify block header field 'hashFinalSaplingRoot' (returned in rpc as 'finalsaplingroot')
@ -25,17 +28,16 @@ class FinalSaplingRootTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 4
self.num_nodes = 2
self.setup_clean_chain = True
def setup_network(self, split=False):
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[
'-txindex' # Avoid JSONRPC error: No information available about transaction
'-txindex', # Avoid JSONRPC error: No information available about transaction
nuparams(NU5_BRANCH_ID, 210),
'-debug',
]] * self.num_nodes)
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
connect_nodes_bi(self.nodes,0,3)
self.is_network_split=False
self.sync_all()
@ -52,13 +54,19 @@ class FinalSaplingRootTest(BitcoinTestFramework):
assert_equal(treestate["sprout"]["commitments"]["finalRoot"], SPROUT_TREE_EMPTY_ROOT)
assert_equal(treestate["sprout"]["commitments"]["finalState"], "000000")
assert("skipHash" not in treestate["sprout"])
assert "skipHash" not in treestate["sprout"]
assert_equal(treestate["sapling"]["commitments"]["finalRoot"], NULL_FIELD)
# There is no sapling state tree yet, and trying to find it in an earlier
# block won't succeed (we're at genesis block), so skipHash is absent.
assert("finalState" not in treestate["sapling"])
assert("skipHash" not in treestate["sapling"])
assert "finalState" not in treestate["sapling"]
assert "skipHash" not in treestate["sapling"]
assert_equal(treestate["orchard"]["commitments"]["finalRoot"], NULL_FIELD)
# There is no orchard state tree yet, and trying to find it in an earlier
# block won't succeed (we're at genesis block), so skipHash is absent.
assert "finalState" not in treestate["orchard"]
assert "skipHash" not in treestate["orchard"]
# Verify all generated blocks contain the empty root of the Sapling tree.
blockcount = self.nodes[0].getblockcount()
@ -70,14 +78,18 @@ class FinalSaplingRootTest(BitcoinTestFramework):
assert_equal(treestate["height"], height)
assert_equal(treestate["hash"], self.nodes[0].getblockhash(height))
assert("skipHash" not in treestate["sprout"])
assert "skipHash" not in treestate["sprout"]
assert_equal(treestate["sprout"]["commitments"]["finalRoot"], SPROUT_TREE_EMPTY_ROOT)
assert_equal(treestate["sprout"]["commitments"]["finalState"], "000000")
assert("skipHash" not in treestate["sapling"])
assert "skipHash" not in treestate["sapling"]
assert_equal(treestate["sapling"]["commitments"]["finalRoot"], SAPLING_TREE_EMPTY_ROOT)
assert_equal(treestate["sapling"]["commitments"]["finalState"], "000000")
assert "skipHash" not in treestate["orchard"]
assert_equal(treestate["orchard"]["commitments"]["finalRoot"], NULL_FIELD)
# Node 0 shields some funds
taddr0 = get_coinbase_address(self.nodes[0])
saplingAddr0 = self.nodes[0].z_getnewaddress('sapling')
@ -93,8 +105,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
# Verify the final Sapling root has changed
blk = self.nodes[0].getblock("201")
root = blk["finalsaplingroot"]
assert(root is not SAPLING_TREE_EMPTY_ROOT)
assert(root is not NULL_FIELD)
assert root is not SAPLING_TREE_EMPTY_ROOT
assert root is not NULL_FIELD
# Verify there is a Sapling output description (its commitment was added to tree)
result = self.nodes[0].getrawtransaction(mytxid, 1)
@ -105,8 +117,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
new_treestate = self.nodes[0].z_gettreestate(str(-1))
assert_equal(new_treestate["sapling"]["commitments"]["finalRoot"], root)
assert_equal(new_treestate["sprout"], treestate["sprout"])
assert(new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"])
assert(new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"])
assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"]
assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"]
assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64)
assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 70)
treestate = new_treestate
@ -145,8 +157,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
new_treestate = self.nodes[0].z_gettreestate(str(-1))
assert_equal(new_treestate["sapling"]["commitments"]["finalRoot"], root)
assert_equal(new_treestate["sapling"], treestate["sapling"])
assert(new_treestate["sprout"]["commitments"]["finalRoot"] != treestate["sprout"]["commitments"]["finalRoot"])
assert(new_treestate["sprout"]["commitments"]["finalState"] != treestate["sprout"]["commitments"]["finalState"])
assert new_treestate["sprout"]["commitments"]["finalRoot"] != treestate["sprout"]["commitments"]["finalRoot"]
assert new_treestate["sprout"]["commitments"]["finalState"] != treestate["sprout"]["commitments"]["finalState"]
assert_equal(len(new_treestate["sprout"]["commitments"]["finalRoot"]), 64)
assert_equal(len(new_treestate["sprout"]["commitments"]["finalState"]), 134)
treestate = new_treestate
@ -164,7 +176,7 @@ class FinalSaplingRootTest(BitcoinTestFramework):
assert_equal(len(self.nodes[0].getblock("205")["tx"]), 2)
assert_equal(self.nodes[1].z_getbalance(saplingAddr1), Decimal("12.34"))
assert(root is not self.nodes[0].getblock("205")["finalsaplingroot"])
assert root is not self.nodes[0].getblock("205")["finalsaplingroot"]
# Verify there is a Sapling output description (its commitment was added to tree)
result = self.nodes[0].getrawtransaction(mytxid, 1)
@ -172,8 +184,8 @@ class FinalSaplingRootTest(BitcoinTestFramework):
new_treestate = self.nodes[0].z_gettreestate(str(-1))
assert_equal(new_treestate["sprout"], treestate["sprout"])
assert(new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"])
assert(new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"])
assert new_treestate["sapling"]["commitments"]["finalRoot"] != treestate["sapling"]["commitments"]["finalRoot"]
assert new_treestate["sapling"]["commitments"]["finalState"] != treestate["sapling"]["commitments"]["finalState"]
assert_equal(len(new_treestate["sapling"]["commitments"]["finalRoot"]), 64)
assert_equal(len(new_treestate["sapling"]["commitments"]["finalState"]), 136)
treestate = new_treestate
@ -200,6 +212,21 @@ class FinalSaplingRootTest(BitcoinTestFramework):
assert_equal(new_treestate["sprout"], treestate["sprout"])
assert_equal(new_treestate["sapling"], treestate["sapling"])
# Activate NU5; more testing should be added once we can mine orchard transactions.
self.sync_all()
self.nodes[0].generate(4)
self.sync_all()
new_treestate = self.nodes[0].z_gettreestate(str(-1))
# sprout and sapling results should not change
assert_equal(new_treestate["sprout"], treestate["sprout"])
assert_equal(new_treestate["sapling"], treestate["sapling"])
assert "skipHash" not in treestate["orchard"]
assert_equal(new_treestate["orchard"]["commitments"]["finalRoot"], ORCHARD_TREE_EMPTY_ROOT)
assert_equal(new_treestate["orchard"]["commitments"]["finalState"], "00")
pass
if __name__ == '__main__':
FinalSaplingRootTest().main()

View File

@ -6,10 +6,12 @@
from decimal import Decimal
from test_framework.authproxy import JSONRPCException
from test_framework.test_framework import BitcoinTestFramework
from test_framework.mininode import nuparams
from test_framework.mininode import COIN, nuparams
from test_framework.util import (
BLOSSOM_BRANCH_ID,
HEARTWOOD_BRANCH_ID,
CANOPY_BRANCH_ID,
NU5_BRANCH_ID,
assert_equal,
assert_raises,
bitcoind_processes,
@ -28,8 +30,12 @@ class ShieldCoinbaseTest (BitcoinTestFramework):
def start_node_with(self, index, extra_args=[]):
args = [
'-experimentalfeatures',
'-orchardwallet',
nuparams(BLOSSOM_BRANCH_ID, 1),
nuparams(HEARTWOOD_BRANCH_ID, 10),
nuparams(CANOPY_BRANCH_ID, 20),
nuparams(NU5_BRANCH_ID, 20),
"-nurejectoldversions=false",
]
return start_node(index, self.options.tmpdir, args + extra_args)
@ -117,5 +123,50 @@ class ShieldCoinbaseTest (BitcoinTestFramework):
assert_equal(self.nodes[0].z_getbalance(node0_taddr), 2)
assert_equal(self.nodes[1].z_getbalance(node1_zaddr), 1)
# Generate a Unified Address for node 1
self.nodes[1].z_getnewaccount()
node1_addr0 = self.nodes[1].z_getaddressforaccount(0)
assert_equal(node1_addr0['account'], 0)
assert_equal(set(node1_addr0['pools']), set(['transparent', 'sapling', 'orchard']))
node1_ua = node1_addr0['unifiedaddress']
# Set node 1's miner address to the UA
self.nodes[1].stop()
bitcoind_processes[1].wait()
self.nodes[1] = self.start_node_with(1, [
"-mineraddress=%s" % node1_ua,
])
connect_nodes(self.nodes[1], 0)
# The UA starts with zero balance.
assert_equal(self.nodes[1].z_getbalanceforaccount(0)['pools'], {})
# Node 1 can mine blocks because the miner selects the Sapling receiver
# of its UA.
print("Mining block with node 1")
self.nodes[1].generate(1)
self.sync_all()
# The UA balance should show that Sapling funds were received.
assert_equal(self.nodes[1].z_getbalanceforaccount(0)['pools'], {
'sapling': {'valueZat': 5 * COIN },
})
# Activate NU5
print("Activating NU5")
self.nodes[0].generate(7)
self.sync_all()
# Now any block mined by node 1 should use the Orchard receiver of its UA.
print("Mining block with node 1")
self.nodes[1].generate(1)
self.sync_all()
assert_equal(self.nodes[1].z_getbalanceforaccount(0)['pools'], {
'sapling': {'valueZat': 5 * COIN },
# 6.25 ZEC because the FR always ends when Canopy activates, and
# regtest has no defined funding streams.
'orchard': {'valueZat': 6.25 * COIN },
})
if __name__ == '__main__':
ShieldCoinbaseTest().main()

130
qa/rpc-tests/orchard_reorg.py Executable file
View File

@ -0,0 +1,130 @@
#!/usr/bin/env python3
# Copyright (c) 2022 The Zcash developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
#
# Test the effect of reorgs on the Orchard commitment tree.
#
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
BLOSSOM_BRANCH_ID,
HEARTWOOD_BRANCH_ID,
CANOPY_BRANCH_ID,
NU5_BRANCH_ID,
assert_equal,
get_coinbase_address,
nuparams,
start_nodes,
wait_and_assert_operationid_status,
)
from finalsaplingroot import ORCHARD_TREE_EMPTY_ROOT
from decimal import Decimal
class OrchardReorgTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 4
self.setup_clean_chain = True
def setup_nodes(self):
return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[[
nuparams(BLOSSOM_BRANCH_ID, 1),
nuparams(HEARTWOOD_BRANCH_ID, 5),
nuparams(CANOPY_BRANCH_ID, 5),
nuparams(NU5_BRANCH_ID, 10),
'-nurejectoldversions=false',
'-experimentalfeatures',
'-orchardwallet',
# '-debug',
]] * self.num_nodes)
def run_test(self):
# Activate NU5 so we can test Orchard.
self.nodes[0].generate(10)
self.sync_all()
# Generate a UA with only an Orchard receiver.
account = self.nodes[0].z_getnewaccount()['account']
addr = self.nodes[0].z_getaddressforaccount(account, ['orchard'])
assert_equal(addr['account'], account)
assert_equal(set(addr['pools']), set(['orchard']))
ua = addr['unifiedaddress']
# Before mining any Orchard notes, finalorchardroot should be the empty Orchard root.
assert_equal(
ORCHARD_TREE_EMPTY_ROOT,
self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot'],
)
# finalorchardroot should not change if we mine additional blocks without Orchard notes.
self.nodes[0].generate(100)
self.sync_all()
assert_equal(
ORCHARD_TREE_EMPTY_ROOT,
self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot'],
)
# Create an Orchard note.
recipients = [{'address': ua, 'amount': Decimal('12.5')}]
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[0], opid)
# After mining a block, finalorchardroot should have changed.
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
orchardroot_oneleaf = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot']
print("Root of Orchard commitment tree with one leaf:", orchardroot_oneleaf)
assert(orchardroot_oneleaf != ORCHARD_TREE_EMPTY_ROOT)
# finalorchardroot should not change if we mine additional blocks without Orchard notes.
self.nodes[0].generate(4)
self.sync_all()
assert_equal(
orchardroot_oneleaf,
self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot'],
)
# Split the network so we can test the effect of a reorg.
print("Splitting the network")
self.split_network()
# Create another Orchard note on node 0.
recipients = [{'address': ua, 'amount': Decimal('12.5')}]
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[0], opid)
# Mine two blocks on node 0.
print("Mining 2 blocks on node 0")
self.nodes[0].generate(2)
self.sync_all()
orchardroot_twoleaf = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['finalorchardroot']
print("Root of Orchard commitment tree with two leaves:", orchardroot_twoleaf)
assert(orchardroot_twoleaf != ORCHARD_TREE_EMPTY_ROOT)
assert(orchardroot_twoleaf != orchardroot_oneleaf)
# Generate 10 blocks on node 2.
print("Mining alternate chain on node 2")
self.nodes[2].generate(10)
self.sync_all()
assert_equal(
orchardroot_oneleaf,
self.nodes[2].getblock(self.nodes[2].getbestblockhash())['finalorchardroot'],
)
# 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 latest Orchard root matches what we expect.
orchardroot_postreorg = self.nodes[0].getblock(self.nodes[2].getbestblockhash())['finalorchardroot']
print("Root of Orchard commitment tree after reorg:", orchardroot_postreorg)
assert_equal(orchardroot_postreorg, orchardroot_oneleaf)
if __name__ == '__main__':
OrchardReorgTest().main()

View File

@ -52,7 +52,7 @@ SPROUT_PROTO_VERSION = 170002 # past bip-31 for ping/pong
OVERWINTER_PROTO_VERSION = 170003
SAPLING_PROTO_VERSION = 170006
BLOSSOM_PROTO_VERSION = 170008
NU5_PROTO_VERSION = 170015
NU5_PROTO_VERSION = 170040
MY_SUBVERSION = b"/python-mininode-tester:0.0.3/"

View File

@ -52,7 +52,7 @@ class BitcoinTestFramework(object):
def setup_nodes(self):
return start_nodes(self.num_nodes, self.options.tmpdir)
def setup_network(self, split = False):
def setup_network(self, split = False, do_mempool_sync = True):
self.nodes = self.setup_nodes()
# Connect the nodes as a "chain". This allows us
@ -64,12 +64,13 @@ class BitcoinTestFramework(object):
if not split:
connect_nodes_bi(self.nodes, 1, 2)
sync_blocks(self.nodes[1:3])
sync_mempools(self.nodes[1:3])
if do_mempool_sync:
sync_mempools(self.nodes[1:3])
connect_nodes_bi(self.nodes, 0, 1)
connect_nodes_bi(self.nodes, 2, 3)
self.is_network_split = split
self.sync_all()
self.sync_all(do_mempool_sync)
def split_network(self):
"""
@ -80,15 +81,17 @@ class BitcoinTestFramework(object):
wait_bitcoinds()
self.setup_network(True)
def sync_all(self):
def sync_all(self, do_mempool_sync = True):
if self.is_network_split:
sync_blocks(self.nodes[:2])
sync_blocks(self.nodes[2:])
sync_mempools(self.nodes[:2])
sync_mempools(self.nodes[2:])
if do_mempool_sync:
sync_mempools(self.nodes[:2])
sync_mempools(self.nodes[2:])
else:
sync_blocks(self.nodes)
sync_mempools(self.nodes)
if do_mempool_sync:
sync_mempools(self.nodes)
def join_network(self):
"""
@ -97,7 +100,7 @@ class BitcoinTestFramework(object):
assert self.is_network_split
stop_nodes(self.nodes)
wait_bitcoinds()
self.setup_network(False)
self.setup_network(False, False)
def main(self):

View File

@ -40,7 +40,7 @@ SAPLING_BRANCH_ID = 0x76B809BB
BLOSSOM_BRANCH_ID = 0x2BB40E60
HEARTWOOD_BRANCH_ID = 0xF5B9230B
CANOPY_BRANCH_ID = 0xE9FF75A6
NU5_BRANCH_ID = 0x37519621
NU5_BRANCH_ID = 0xC2D6D0B4
# The maximum number of nodes a single test can spawn
MAX_NODES = 8

View File

@ -7,9 +7,12 @@ from test_framework.authproxy import JSONRPCException
from test_framework.mininode import COIN
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
NU5_BRANCH_ID,
assert_equal,
assert_raises_message,
assert_true,
get_coinbase_address,
nuparams,
start_nodes,
wait_and_assert_operationid_status,
)
@ -22,6 +25,7 @@ class WalletAccountsTest(BitcoinTestFramework):
return start_nodes(self.num_nodes, self.options.tmpdir, [[
'-experimentalfeatures',
'-orchardwallet',
nuparams(NU5_BRANCH_ID, 210),
]] * self.num_nodes)
def check_receiver_types(self, ua, expected):
@ -32,14 +36,19 @@ class WalletAccountsTest(BitcoinTestFramework):
# Remember that empty pools are omitted from the output.
def _check_balance_for_rpc(self, rpcmethod, node, account, expected, minconf):
rpc = getattr(self.nodes[node], rpcmethod)
actual = rpc(account) if minconf is None else rpc(account, minconf)
actual = rpc(account, minconf)
assert_equal(set(expected), set(actual['pools']))
total_balance = 0
for pool in expected:
assert_equal(expected[pool] * COIN, actual['pools'][pool]['valueZat'])
assert_equal(actual['minimum_confirmations'], 1 if minconf is None else minconf)
total_balance += expected[pool]
assert_equal(actual['minimum_confirmations'], minconf)
return total_balance
def check_balance(self, node, account, address, expected, minconf=None):
self._check_balance_for_rpc('z_getbalanceforaccount', node, account, expected, minconf)
def check_balance(self, node, account, address, expected, minconf=1):
acct_balance = self._check_balance_for_rpc('z_getbalanceforaccount', node, account, expected, minconf)
z_getbalance = self.nodes[node].z_getbalance(address, minconf)
assert_equal(acct_balance, z_getbalance)
fvk = self.nodes[node].z_exportviewingkey(address)
self._check_balance_for_rpc('z_getbalanceforviewingkey', node, fvk, expected, minconf)
@ -55,7 +64,7 @@ class WalletAccountsTest(BitcoinTestFramework):
# Generate the first address for account 0.
addr0 = self.nodes[0].z_getaddressforaccount(0)
assert_equal(addr0['account'], 0)
assert_equal(set(addr0['pools']), set(['transparent', 'sapling']))
assert_equal(set(addr0['pools']), set(['transparent', 'sapling', 'orchard']))
ua0 = addr0['unifiedaddress']
# We pick mnemonic phrases to ensure that we can always generate the default
@ -70,24 +79,47 @@ class WalletAccountsTest(BitcoinTestFramework):
'no address at diversifier index 0',
self.nodes[0].z_getaddressforaccount, 0, [], 0)
# The second address for account 0 is different to the first address.
addr0_2 = self.nodes[0].z_getaddressforaccount(0)
assert_equal(addr0_2['account'], 0)
assert_equal(set(addr0_2['pools']), set(['transparent', 'sapling', 'orchard']))
ua0_2 = addr0_2['unifiedaddress']
assert(ua0 != ua0_2)
# We can generate a fully-shielded address.
addr0_3 = self.nodes[0].z_getaddressforaccount(0, ['sapling', 'orchard'])
assert_equal(addr0_3['account'], 0)
assert_equal(set(addr0_3['pools']), set(['sapling', 'orchard']))
ua0_3 = addr0_3['unifiedaddress']
# We can generate an address without a Sapling receiver.
addr0_4 = self.nodes[0].z_getaddressforaccount(0, ['transparent', 'orchard'])
assert_equal(addr0_4['account'], 0)
assert_equal(set(addr0_4['pools']), set(['transparent', 'orchard']))
ua0_4 = addr0_4['unifiedaddress']
# The first address for account 1 is different to account 0.
addr1 = self.nodes[0].z_getaddressforaccount(1)
assert_equal(addr1['account'], 1)
assert_equal(set(addr1['pools']), set(['transparent', 'sapling']))
assert_equal(set(addr1['pools']), set(['transparent', 'sapling', 'orchard']))
ua1 = addr1['unifiedaddress']
assert(ua0 != ua1)
# The UA contains the expected receiver kinds.
self.check_receiver_types(ua0, ['transparent', 'sapling'])
self.check_receiver_types(ua1, ['transparent', 'sapling'])
self.check_receiver_types(ua0, ['transparent', 'sapling', 'orchard'])
self.check_receiver_types(ua0_2, ['transparent', 'sapling', 'orchard'])
self.check_receiver_types(ua0_3, [ 'sapling', 'orchard'])
self.check_receiver_types(ua0_4, ['transparent', 'orchard'])
self.check_receiver_types(ua1, ['transparent', 'sapling', 'orchard'])
# The balances of the accounts are all zero.
self.check_balance(0, 0, ua0, {})
self.check_balance(0, 1, ua1, {})
# Manually send funds to one of the receivers in the UA.
# Send coinbase funds to the UA.
print('Sending coinbase funds to account')
recipients = [{'address': ua0, 'amount': Decimal('10')}]
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0)
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders')
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
# The wallet should detect the new note as belonging to the UA.
@ -108,7 +140,8 @@ class WalletAccountsTest(BitcoinTestFramework):
# The default minconf should now detect the balance.
self.check_balance(0, 0, ua0, {'sapling': 10})
# Manually send funds from the UA receiver.
# Send Sapling funds from the UA.
print('Sending account funds to Sapling address')
node1sapling = self.nodes[1].z_getnewaddress('sapling')
recipients = [{'address': node1sapling, 'amount': Decimal('1')}]
opid = self.nodes[0].z_sendmany(ua0, recipients, 1, 0)
@ -128,6 +161,69 @@ class WalletAccountsTest(BitcoinTestFramework):
self.check_balance(0, 0, ua0, {})
self.check_balance(0, 0, ua0, {'sapling': 9}, 0)
# Activate NU5
print('Activating NU5')
self.nodes[2].generate(9)
self.sync_all()
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 210)
# Send more coinbase funds to the UA.
print('Sending coinbase funds to account')
recipients = [{'address': ua0, 'amount': Decimal('10')}]
opid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders')
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
# The wallet should detect the new note as belonging to the UA.
tx_details = self.nodes[0].z_viewtransaction(txid)
assert_equal(len(tx_details['outputs']), 1)
assert_equal(tx_details['outputs'][0]['type'], 'orchard')
assert_equal(tx_details['outputs'][0]['address'], ua0)
# The new balance should not be visible with the default minconf, but should be
# visible with minconf=0.
self.sync_all()
self.check_balance(0, 0, ua0, {'sapling': 9})
self.check_balance(0, 0, ua0, {'sapling': 9, 'orchard': 10}, 0)
# The total balance with the default minconf should be just the Sapling balance
assert_equal('9.00', self.nodes[0].z_gettotalbalance()['private'])
assert_equal('19.00', self.nodes[0].z_gettotalbalance(0)['private'])
self.nodes[2].generate(1)
self.sync_all()
# Send Orchard funds from the UA.
print('Sending account funds to Orchard-only UA')
node1account = self.nodes[1].z_getnewaccount()['account']
node1orchard = self.nodes[1].z_getaddressforaccount(node1account, ['orchard'])['unifiedaddress']
recipients = [{'address': node1orchard, 'amount': Decimal('1')}]
opid = self.nodes[0].z_sendmany(ua0, recipients, 1, 0)
txid = wait_and_assert_operationid_status(self.nodes[0], opid)
# The wallet should detect the spent note as belonging to the UA.
tx_details = self.nodes[0].z_viewtransaction(txid)
assert_equal(len(tx_details['spends']), 1)
assert_equal(tx_details['spends'][0]['type'], 'orchard')
assert_equal(tx_details['spends'][0]['address'], ua0)
assert_equal(len(tx_details['outputs']), 2)
outputs = sorted(tx_details['outputs'], key=lambda x: x['valueZat'])
assert_equal(outputs[0]['type'], 'orchard')
assert_equal(outputs[0]['address'], node1orchard)
assert_equal(outputs[0]['valueZat'], 100000000)
# outputs[1] is change
assert_equal(outputs[1]['type'], 'orchard')
assert_true('address' not in outputs[1]) #
# The balances of the account should reflect whether zero-conf transactions are
# being considered. The Sapling balance should remain at 9, while the Orchard
# balance will show either 0 (because the spent 10-ZEC note is never shown, as
# that transaction has been created and broadcast, and _might_ get mined up until
# the transaction expires), or 9 (if we include the unmined transaction).
self.sync_all()
self.check_balance(0, 0, ua0, {'sapling': 9})
self.check_balance(0, 0, ua0, {'sapling': 9, 'orchard': 9}, 0)
if __name__ == '__main__':
WalletAccountsTest().main()

View File

@ -4,53 +4,162 @@
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from test_framework.util import assert_equal, start_nodes, connect_nodes_bi, NU5_BRANCH_ID
from test_framework.mininode import nuparams
# Test wallet address behaviour across network upgrades
class WalletAddressesTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
# need 2 nodes to import addresses
self.num_nodes = 2
self.setup_clean_chain = True
def run_test(self):
def addr_checks(default_type):
# Check default type, as well as explicit types
types_and_addresses = [
(default_type, self.nodes[0].z_getnewaddress()),
('sprout', self.nodes[0].z_getnewaddress('sprout')),
('sapling', self.nodes[0].z_getnewaddress('sapling')),
]
all_addresses = self.nodes[0].z_listaddresses()
for addr_type, addr in types_and_addresses:
res = self.nodes[0].z_validateaddress(addr)
assert(res['isvalid'])
assert(res['ismine'])
assert_equal(res['type'], addr_type)
assert(addr in all_addresses)
listed_addresses = self.nodes[0].listaddresses()
legacy_random_src = next(src for src in listed_addresses if src['source'] == 'legacy_random')
legacy_hdseed_src = next(src for src in listed_addresses if src['source'] == 'legacy_hdseed')
for addr_type, addr in types_and_addresses:
if addr_type == 'sprout':
assert(addr in legacy_random_src['sprout']['addresses'])
if addr_type == 'sapling':
assert(addr in [x for obj in legacy_hdseed_src['sapling'] for x in obj['addresses']])
# Sanity-check the test harness
assert_equal(self.nodes[0].getblockcount(), 200)
# Current height = 200 -> Sapling
# Default address type is Sapling
print("Testing height 200 (Sapling)")
addr_checks('sapling')
self.nodes[0].generate(1)
def setup_network(self):
self.nodes = start_nodes(
self.num_nodes, self.options.tmpdir,
extra_args=[['-experimentalfeatures', '-orchardwallet', nuparams(NU5_BRANCH_ID, 2),]] * self.num_nodes)
connect_nodes_bi(self.nodes, 0, 1)
self.is_network_split = False
self.sync_all()
# Current height = 201 -> Sapling
# Default address type is Sapling
print("Testing height 201 (Sapling)")
addr_checks('sapling')
def list_addresses(self, node, expected_sources):
addrs = self.nodes[node].listaddresses()
sources = [s['source'] for s in addrs]
# Sources should be unique.
assert_equal(len(set(sources)), len(sources))
assert_equal(set(sources), set(expected_sources))
# Extract a list of all addresses from the output.
all_addrs = [
source.get('transparent', {}).get('addresses', []) +
source.get('transparent', {}).get('changeAddresses', []) +
source.get('sprout', {}).get('addresses', []) +
[s['addresses'] for s in source.get('sapling', [])] +
[[a['address'] for a in s['addresses']] for s in source.get('unified', [])]
for source in addrs]
all_addrs = [a for s in all_addrs for a in s]
all_addrs = [a if type(a) == list else [a] for a in all_addrs]
all_addrs = [a for s in all_addrs for a in s]
assert_equal(len(set(all_addrs)), len(all_addrs), "Duplicates in listaddresses output: %s" % addrs)
return addrs
def run_test(self):
def get_source(listed_addresses, source):
return next(src for src in listed_addresses if src['source'] == source)
print("Testing height 1 (Sapling)")
self.nodes[0].generate(1)
self.sync_all()
assert_equal(self.nodes[0].getblockcount(), 1)
listed_addresses = self.list_addresses(0, ['mnemonic_seed'])
# There should be a single address from the coinbase, which was derived
# from the mnemonic seed.
assert 'transparent' in get_source(listed_addresses, 'mnemonic_seed')
# If we import a t-address, we should see imported_watchonly as a source.
taddr_import = self.nodes[1].getnewaddress()
self.nodes[0].importaddress(taddr_import)
listed_addresses = self.list_addresses(0, ['imported_watchonly', 'mnemonic_seed'])
imported_watchonly_src = get_source(listed_addresses, 'imported_watchonly')
assert_equal(imported_watchonly_src['transparent']['addresses'][0], taddr_import)
account = self.nodes[0].z_getnewaccount()['account']
sprout_1 = self.nodes[0].z_getnewaddress('sprout')
sapling_1 = self.nodes[0].z_getnewaddress('sapling')
unified_1 = self.nodes[0].z_getaddressforaccount(account)['unifiedaddress']
types_and_addresses = [
('sprout', sprout_1),
('sapling', sapling_1),
('unified', unified_1),
]
for addr_type, addr in types_and_addresses:
res = self.nodes[0].z_validateaddress(addr)
assert res['isvalid']
# assert res['ismine'] # this isn't present for unified addresses
assert_equal(res['type'], addr_type)
# We should see the following sources:
# - imported_watchonly (for the previously-imported t-addr)
# - legacy_random (for the new Sprout address)
# - mnemonic_seed (for the previous t-addrs and the new Sapling and Unified addrs)
listed_addresses = self.list_addresses(0, ['imported_watchonly', 'legacy_random', 'mnemonic_seed'])
legacy_random_src = get_source(listed_addresses, 'legacy_random')
mnemonic_seed_src = get_source(listed_addresses, 'mnemonic_seed')
# Check Sprout addrs
assert_equal(legacy_random_src['sprout']['addresses'], [sprout_1])
# Check Sapling addrs
assert_equal(
set([(obj['zip32KeyPath'], x) for obj in mnemonic_seed_src['sapling'] for x in obj['addresses']]),
set([("m/32'/1'/2147483647'/0'", sapling_1)]),
)
# Check Unified addrs
unified_obj = mnemonic_seed_src['unified']
assert_equal(unified_obj[0]['account'], 0)
assert_equal(unified_obj[0]['addresses'][0]['address'], unified_1)
assert 'diversifier_index' in unified_obj[0]['addresses'][0]
assert_equal(unified_obj[0]['addresses'][0]['receiver_types'], ['p2pkh', 'sapling', 'orchard'])
print("Testing height 2 (NU5)")
self.nodes[0].generate(1)
self.sync_all()
assert_equal(self.nodes[0].getblockcount(), 2)
# Sprout address generation is no longer allowed
sapling_2 = self.nodes[0].z_getnewaddress('sapling')
unified_2 = self.nodes[0].z_getaddressforaccount(account)['unifiedaddress']
types_and_addresses = [
('sapling', sapling_2),
('unified', unified_2),
]
for addr_type, addr in types_and_addresses:
res = self.nodes[0].z_validateaddress(addr)
assert res['isvalid']
# assert res['ismine'] # this isn't present for unified addresses
assert_equal(res['type'], addr_type)
# We should see the same sources (address generation does not change across the NU5 boundary).
listed_addresses = self.list_addresses(0, ['imported_watchonly', 'legacy_random', 'mnemonic_seed'])
legacy_random_src = get_source(listed_addresses, 'legacy_random')
mnemonic_seed_src = get_source(listed_addresses, 'mnemonic_seed')
# Check Sprout addrs
assert_equal(legacy_random_src['sprout']['addresses'], [sprout_1])
# Check Sapling addrs
assert_equal(
set([(obj['zip32KeyPath'], x) for obj in mnemonic_seed_src['sapling'] for x in obj['addresses']]),
set([
("m/32'/1'/2147483647'/0'", sapling_1),
("m/32'/1'/2147483647'/1'", sapling_2),
]),
)
# Check Unified addrs
unified_obj = mnemonic_seed_src['unified']
assert_equal(unified_obj[0]['account'], 0)
assert_equal(
set([addr['address'] for addr in unified_obj[0]['addresses']]),
set([unified_1, unified_2]),
)
assert 'diversifier_index' in unified_obj[0]['addresses'][0]
assert 'diversifier_index' in unified_obj[0]['addresses'][1]
assert_equal(unified_obj[0]['addresses'][0]['receiver_types'], ['p2pkh', 'sapling', 'orchard'])
assert_equal(unified_obj[0]['addresses'][1]['receiver_types'], ['p2pkh', 'sapling', 'orchard'])
print("Generate mature coinbase, spend to create and detect change")
self.nodes[0].generate(100)
self.sync_all()
self.nodes[0].sendmany('', {taddr_import: 1})
listed_addresses = self.list_addresses(0, ['imported_watchonly', 'legacy_random', 'mnemonic_seed'])
mnemonic_seed_src = get_source(listed_addresses, 'mnemonic_seed')
assert len(mnemonic_seed_src['transparent']['changeAddresses']) > 0
if __name__ == '__main__':
WalletAddressesTest().main()

View File

@ -68,7 +68,7 @@ class WalletListNotes(BitcoinTestFramework):
change_amount_2 = receive_amount_1 - receive_amount_2 - DEFAULT_FEE
assert_equal('sapling', self.nodes[0].z_validateaddress(saplingzaddr)['type'])
recipients = [{"address": saplingzaddr, "amount":receive_amount_2}]
myopid = self.nodes[0].z_sendmany(sproutzaddr, recipients, 1, DEFAULT_FEE, True)
myopid = self.nodes[0].z_sendmany(sproutzaddr, recipients, 1, DEFAULT_FEE)
txid_2 = wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
@ -107,7 +107,7 @@ class WalletListNotes(BitcoinTestFramework):
receive_amount_3 = Decimal('2.0')
change_amount_3 = change_amount_2 - receive_amount_3 - DEFAULT_FEE
recipients = [{"address": saplingzaddr2, "amount":receive_amount_3}]
myopid = self.nodes[0].z_sendmany(sproutzaddr, recipients, 1, DEFAULT_FEE, True)
myopid = self.nodes[0].z_sendmany(sproutzaddr, recipients, 1, DEFAULT_FEE)
txid_3 = wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
unspent_tx = self.nodes[0].z_listunspent(0)

View File

@ -12,8 +12,10 @@ from test_framework.util import (
assert_raises_message,
connect_nodes_bi,
get_coinbase_address,
nuparams,
DEFAULT_FEE,
DEFAULT_FEE_ZATS
DEFAULT_FEE_ZATS,
NU5_BRANCH_ID,
)
from test_framework.util import wait_and_assert_operationid_status, start_nodes
from decimal import Decimal
@ -33,7 +35,12 @@ class ListReceivedTest (BitcoinTestFramework):
def setup_network(self):
self.nodes = start_nodes(
self.num_nodes, self.options.tmpdir,
extra_args=[['-experimentalfeatures', '-orchardwallet']] * self.num_nodes)
extra_args=[[
'-experimentalfeatures',
'-orchardwallet',
nuparams(NU5_BRANCH_ID, 225),
]] * self.num_nodes
)
connect_nodes_bi(self.nodes, 0, 1)
connect_nodes_bi(self.nodes, 1, 2)
connect_nodes_bi(self.nodes, 0, 2)
@ -381,9 +388,19 @@ class ListReceivedTest (BitcoinTestFramework):
r = node.z_getaddressforaccount(account)
unified_addr = r['unifiedaddress']
receivers = node.z_listunifiedreceivers(unified_addr)
assert_equal(len(receivers), 2)
assert_equal(len(receivers), 3)
assert 'transparent' in receivers
assert 'sapling' in receivers
assert 'orchard' in receivers
assert_raises_message(
JSONRPCException,
"The provided address is a bare receiver from a Unified Address in this wallet.",
node.z_listreceivedbyaddress, receivers['transparent'], 0)
assert_raises_message(
JSONRPCException,
"The provided address is a bare receiver from a Unified Address in this wallet.",
node.z_listreceivedbyaddress, receivers['sapling'], 0)
# Wallet contains no notes
r = node.z_listreceivedbyaddress(unified_addr, 0)
assert_equal(len(r), 0, "unified_addr should have received zero notes")
@ -412,7 +429,7 @@ class ListReceivedTest (BitcoinTestFramework):
assert_equal(r[0]['blockheight'], height+5)
assert_equal(r[0]['blockindex'], 1)
assert 'blocktime' in r[0]
assert_equal(r[1]['pool'], 'transparent')
assert_equal(r[1]['txid'], txid_taddr)
assert_equal(r[1]['amount'], Decimal('0.2'))
@ -425,9 +442,116 @@ class ListReceivedTest (BitcoinTestFramework):
assert_equal(r[1]['blockindex'], -1) # not yet mined
assert 'blocktime' in r[1]
def test_received_orchard(self, height):
self.generate_and_sync(height+1)
taddr = self.nodes[1].getnewaddress()
acct1 = self.nodes[1].z_getnewaccount()['account']
acct2 = self.nodes[1].z_getnewaccount()['account']
addrResO = self.nodes[1].z_getaddressforaccount(acct1, ['orchard'])
assert_equal(addrResO['pools'], ['orchard'])
uao = addrResO['unifiedaddress']
addrResSO = self.nodes[1].z_getaddressforaccount(acct2, ['sapling', 'orchard'])
assert_equal(addrResSO['pools'], ['sapling', 'orchard'])
uaso = addrResSO['unifiedaddress']
self.nodes[0].sendtoaddress(taddr, 4.0)
self.generate_and_sync(height+2)
acct_node0 = self.nodes[0].z_getnewaccount()['account']
ua_node0 = self.nodes[0].z_getaddressforaccount(acct_node0, ['sapling', 'orchard'])['unifiedaddress']
opid = self.nodes[1].z_sendmany(taddr, [
{'address': uao, 'amount': 1, 'memo': my_memo},
{'address': uaso, 'amount': 2},
], 1, 0, 'AllowRevealedSenders')
txid0 = wait_and_assert_operationid_status(self.nodes[1], opid)
self.sync_all()
# Decrypted transaction details should be correct, even though
# the transaction is still just in the mempool
pt = self.nodes[1].z_viewtransaction(txid0)
assert_equal(pt['txid'], txid0)
assert_equal(len(pt['spends']), 0)
assert_equal(len(pt['outputs']), 2)
# Outputs are not returned in a defined order but the amounts are deterministic
outputs = sorted(pt['outputs'], key=lambda x: x['valueZat'])
assert_equal(outputs[0]['type'], 'orchard')
assert_equal(outputs[0]['address'], uao)
assert_equal(outputs[0]['value'], Decimal('1'))
assert_equal(outputs[0]['valueZat'], 100000000)
assert_equal(outputs[0]['action'], 0)
assert_equal(outputs[0]['outgoing'], False)
assert_equal(outputs[0]['memo'], my_memo)
assert_equal(outputs[0]['memoStr'], my_memo_str)
assert_equal(outputs[1]['type'], 'orchard')
assert_equal(outputs[1]['address'], uaso)
assert_equal(outputs[1]['value'], Decimal('2'))
assert_equal(outputs[1]['valueZat'], 200000000)
assert_equal(outputs[1]['action'], 1)
assert_equal(outputs[1]['outgoing'], False)
assert_equal(outputs[1]['memo'], no_memo)
assert 'memoStr' not in outputs[1]
self.generate_and_sync(height+3)
opid = self.nodes[1].z_sendmany(uao, [
{'address': uaso, 'amount': Decimal('0.3')},
{'address': ua_node0, 'amount': Decimal('0.2')}
])
txid1 = wait_and_assert_operationid_status(self.nodes[1], opid)
self.sync_all()
pt = self.nodes[1].z_viewtransaction(txid1)
assert_equal(pt['txid'], txid1)
assert_equal(len(pt['spends']), 1) # one spend we can see
assert_equal(len(pt['outputs']), 3) # one output + one change output we can see
spends = pt['spends']
assert_equal(spends[0]['type'], 'orchard')
assert_equal(spends[0]['action'], 0)
assert_equal(spends[0]['txidPrev'], txid0)
assert_equal(spends[0]['actionPrev'], 0)
assert_equal(spends[0]['address'], uao)
assert_equal(spends[0]['value'], Decimal('1.0'))
assert_equal(spends[0]['valueZat'], 100000000)
outputs = sorted(pt['outputs'], key=lambda x: x['valueZat'])
assert_equal(outputs[0]['type'], 'orchard')
assert_equal(outputs[0]['address'], ua_node0)
assert_equal(outputs[0]['value'], Decimal('0.2'))
assert_equal(outputs[0]['valueZat'], 20000000)
assert_equal(outputs[0]['outgoing'], True)
assert_equal(outputs[0]['walletInternal'], False)
assert_equal(outputs[0]['memo'], no_memo)
assert_equal(outputs[1]['type'], 'orchard')
assert_equal(outputs[1]['address'], uaso)
assert_equal(outputs[1]['value'], Decimal('0.3'))
assert_equal(outputs[1]['valueZat'], 30000000)
assert_equal(outputs[1]['outgoing'], False)
assert_equal(outputs[1]['walletInternal'], False)
assert_equal(outputs[1]['memo'], no_memo)
# Verify that we observe the change output
assert_equal(outputs[2]['type'], 'orchard')
assert_equal(outputs[2]['value'], Decimal('0.49999'))
assert_equal(outputs[2]['valueZat'], 49999000)
assert_equal(outputs[2]['outgoing'], False)
assert_equal(outputs[2]['walletInternal'], True)
assert_equal(outputs[2]['memo'], no_memo)
# The change address should have been erased
assert_true('address' not in outputs[2])
def run_test(self):
self.test_received_sprout(200)
self.test_received_sapling(214)
self.test_received_orchard(230)
if __name__ == '__main__':

181
qa/rpc-tests/wallet_orchard.py Executable file
View File

@ -0,0 +1,181 @@
#!/usr/bin/env python3
# Copyright (c) 2022 The Zcash developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
NU5_BRANCH_ID,
assert_equal,
get_coinbase_address,
nuparams,
start_nodes,
wait_and_assert_operationid_status,
)
from decimal import Decimal
# Test wallet behaviour with the Orchard protocol
class WalletOrchardTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 4
def setup_nodes(self):
return start_nodes(self.num_nodes, self.options.tmpdir, [[
'-experimentalfeatures',
'-orchardwallet',
nuparams(NU5_BRANCH_ID, 210),
]] * self.num_nodes)
def run_test(self):
# Sanity-check the test harness
assert_equal(self.nodes[0].getblockcount(), 200)
# Get a new orchard-only unified address
acct1 = self.nodes[1].z_getnewaccount()['account']
addrRes1 = self.nodes[1].z_getaddressforaccount(acct1, ['orchard'])
assert_equal(acct1, addrRes1['account'])
assert_equal(addrRes1['pools'], ['orchard'])
ua1 = addrRes1['unifiedaddress']
# Verify that we have only an Orchard component
receiver_types = self.nodes[0].z_listunifiedreceivers(ua1)
assert_equal(set(['orchard']), set(receiver_types))
# Verify balance
assert_equal({'pools': {}, 'minimum_confirmations': 1}, self.nodes[1].z_getbalanceforaccount(acct1))
# Send some sapling funds to node 2 for later spending after we split the network
acct2 = self.nodes[2].z_getnewaccount()['account']
addrRes2 = self.nodes[2].z_getaddressforaccount(acct2, ['sapling', 'orchard'])
assert_equal(acct2, addrRes2['account'])
ua2 = addrRes2['unifiedaddress']
saplingAddr2 = self.nodes[2].z_listunifiedreceivers(ua2)['sapling']
recipients = [{"address": saplingAddr2, "amount": Decimal('10')}]
myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[0], myopid)
# Mine the tx & activate NU5
self.sync_all()
self.nodes[0].generate(10)
self.sync_all()
# Check the value sent to saplingAddr2 was received in node 2's account
assert_equal(
{'pools': {'sapling': {'valueZat': Decimal('1000000000')}}, 'minimum_confirmations': 1},
self.nodes[2].z_getbalanceforaccount(acct2))
# Node 0 shields some funds
# t-coinbase -> Orchard
recipients = [{"address": ua1, "amount": Decimal('10')}]
myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[0], myopid)
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('1000000000')}}, 'minimum_confirmations': 1},
self.nodes[1].z_getbalanceforaccount(acct1))
# Split the network
self.split_network()
# Send another tx to ua1
recipients = [{"address": ua1, "amount": Decimal('10')}]
myopid = self.nodes[0].z_sendmany(get_coinbase_address(self.nodes[0]), recipients, 1, 0, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[0], myopid)
# Mine the tx & generate a majority chain on the 0/1 side of the split
self.sync_all()
self.nodes[0].generate(10)
self.sync_all()
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('2000000000')}}, 'minimum_confirmations': 1},
self.nodes[1].z_getbalanceforaccount(acct1))
# On the other side of the split, send some funds to node 3
acct3 = self.nodes[3].z_getnewaccount()['account']
addrRes3 = self.nodes[3].z_getaddressforaccount(acct3, ['sapling', 'orchard'])
assert_equal(acct3, addrRes3['account'])
ua3 = addrRes3['unifiedaddress']
recipients = [{"address": ua3, "amount": Decimal('1')}]
myopid = self.nodes[2].z_sendmany(ua2, recipients, 1, 0)
rollback_tx = wait_and_assert_operationid_status(self.nodes[2], myopid)
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
# The remaining change from ua2's Sapling note has been sent to the
# account's internal Orchard change address.
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 1},
self.nodes[2].z_getbalanceforaccount(acct2))
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('100000000')}}, 'minimum_confirmations': 1},
self.nodes[3].z_getbalanceforaccount(acct3))
# Check that the mempools are empty
for i in range(self.num_nodes):
assert_equal(set([]), set(self.nodes[i].getrawmempool()))
# Reconnect the nodes; nodes 2 and 3 will re-org to node 0's chain.
print("Re-joining the network so that nodes 2 and 3 reorg")
self.join_network()
# split 0/1's chain should have won, so their wallet balance should be consistent
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('2000000000')}}, 'minimum_confirmations': 1},
self.nodes[1].z_getbalanceforaccount(acct1))
# split 2/3's chain should have been rolled back, so their txn should have been
# un-mined and returned to the mempool
assert_equal(set([rollback_tx]), set(self.nodes[2].getrawmempool()))
# acct2's sole Orchard note is spent by a transaction in the mempool, so our
# confirmed balance is currently 0
assert_equal(
{'pools': {}, 'minimum_confirmations': 1},
self.nodes[2].z_getbalanceforaccount(acct2))
# acct2's incoming change (unconfirmed, still in the mempool) is 9 zec
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 0},
self.nodes[2].z_getbalanceforaccount(acct2, 0))
# The transaction was un-mined, so acct3 should have no confirmed balance
assert_equal(
{'pools': {}, 'minimum_confirmations': 1},
self.nodes[3].z_getbalanceforaccount(acct3))
# acct3's unconfirmed balance is 1 zec
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('100000000')}}, 'minimum_confirmations': 0},
self.nodes[3].z_getbalanceforaccount(acct3, 0))
# Manually resend the transaction in node 2's mempool
self.nodes[2].resendwallettransactions()
# Sync the network
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
# The un-mined transaction should now have been re-mined
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('900000000')}}, 'minimum_confirmations': 1},
self.nodes[2].z_getbalanceforaccount(acct2))
assert_equal(
{'pools': {'orchard': {'valueZat': Decimal('100000000')}}, 'minimum_confirmations': 1},
self.nodes[3].z_getbalanceforaccount(acct3))
if __name__ == '__main__':
WalletOrchardTest().main()

View File

@ -6,17 +6,31 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
connect_nodes_bi,
start_nodes,
sync_blocks,
wait_and_assert_operationid_status,
)
TX_EXPIRY_DELTA = 10
TX_EXPIRING_SOON_THRESHOLD = 3
# Test ANY_TADDR special string in z_sendmany
class WalletSendManyAnyTaddr(BitcoinTestFramework):
def setup_nodes(self):
return start_nodes(self.num_nodes, self.options.tmpdir,
[[
"-txexpirydelta=%d" % TX_EXPIRY_DELTA,
]] * self.num_nodes)
def run_test(self):
# Sanity-check the test harness
assert_equal(self.nodes[0].getblockcount(), 200)
# Create the addresses we will be using.
recipient = self.nodes[1].z_getnewaddress()
node2zaddr = self.nodes[2].z_getnewaddress()
node2taddr1 = self.nodes[2].getnewaddress()
node3zaddr = self.nodes[3].z_getnewaddress()
node3taddr1 = self.nodes[3].getnewaddress()
node3taddr2 = self.nodes[3].getnewaddress()
@ -67,5 +81,48 @@ class WalletSendManyAnyTaddr(BitcoinTestFramework):
assert_equal(self.nodes[3].z_getbalance(node3taddr1), 0)
assert_equal(self.nodes[3].z_getbalance(node3taddr2), 0)
# Send from a change t-address.
wait_and_assert_operationid_status(
self.nodes[3],
self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 20}]),
)
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
# The recipient has their funds!
assert_equal(self.nodes[1].z_getbalance(recipient), 120)
# Check that ANY_TADDR note selection doesn't attempt a double-spend
myopid = self.nodes[3].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 20}])
wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient funds: have 14.99998, need 20.00001")
# Create an expired transaction on node 3.
self.split_network()
expire_transparent = self.nodes[3].sendtoaddress(node2taddr1, 14)
assert(expire_transparent in self.nodes[3].getrawmempool())
self.sync_all()
assert_equal('waiting', self.nodes[2].gettransaction(expire_transparent)['status'])
self.nodes[0].generate(TX_EXPIRY_DELTA + TX_EXPIRING_SOON_THRESHOLD)
self.sync_all()
connect_nodes_bi(self.nodes, 1, 2)
sync_blocks(self.nodes[1:3])
assert_equal('expired', self.nodes[2].gettransaction(expire_transparent)['status'])
# Ensure that node 2 has no transparent funds.
self.nodes[2].generate(100) # To ensure node 2's pending coinbase is spendable
self.sync_all()
wait_and_assert_operationid_status(
self.nodes[2],
self.nodes[2].z_shieldcoinbase("*", node2zaddr, 0)['opid'],
)
self.sync_all()
assert_equal(0, self.nodes[2].getbalance())
# Check that ANY_TADDR doesn't select an expired output.
wait_and_assert_operationid_status(self.nodes[2], self.nodes[2].z_sendmany('ANY_TADDR', [{'address': recipient, 'amount': 13}]), "failed", "Insufficient funds: have 0.00, need 13.00001")
if __name__ == '__main__':
WalletSendManyAnyTaddr().main()

View File

@ -23,7 +23,8 @@ class WalletShieldCoinbaseUANU5(WalletShieldCoinbaseTest):
assert('transparent' not in balances['pools'])
assert('sprout' not in balances['pools'])
# assert('sapling' not in balances['pools'])
assert_equal(balances['pools']['sapling']['valueZat'], expected * COIN)
sapling_balance = balances['pools']['sapling']['valueZat']
assert_equal(sapling_balance, expected * COIN)
# assert_equal(balances['pools']['orchard']['valueZat'], expected * COIN)
# While we're at it, check that z_listunspent only shows outputs with
@ -35,6 +36,8 @@ class WalletShieldCoinbaseUANU5(WalletShieldCoinbaseTest):
[{'type': x['type'], 'address': x['address']} for x in unspent],
)
total_balance = node.z_getbalance(self.addr) * COIN
assert_equal(total_balance, sapling_balance)
if __name__ == '__main__':
print("Test shielding to a unified address with NU5 activated")

View File

@ -21,7 +21,8 @@ class WalletShieldCoinbaseUASapling(WalletShieldCoinbaseTest):
balances = node.z_getbalanceforaccount(self.account)
assert('transparent' not in balances['pools'])
assert('sprout' not in balances['pools'])
assert_equal(balances['pools']['sapling']['valueZat'], expected * COIN)
sapling_balance = balances['pools']['sapling']['valueZat']
assert_equal(sapling_balance, expected * COIN)
assert('orchard' not in balances['pools'])
# While we're at it, check that z_listunspent only shows outputs with
@ -33,6 +34,8 @@ class WalletShieldCoinbaseUASapling(WalletShieldCoinbaseTest):
[{'type': x['type'], 'address': x['address']} for x in unspent],
)
total_balance = node.z_getbalance(self.addr) * COIN
assert_equal(total_balance, sapling_balance)
if __name__ == '__main__':
print("Test shielding to a unified address with sapling activated (but not NU5)")

View File

@ -4,8 +4,15 @@
# file COPYING or https://www.opensource.org/licenses/mit-license.php .
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_greater_than, connect_nodes_bi, \
DEFAULT_FEE, start_nodes, wait_and_assert_operationid_status
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_message,
connect_nodes_bi,
DEFAULT_FEE,
start_nodes,
wait_and_assert_operationid_status,
)
from test_framework.authproxy import JSONRPCException
from test_framework.mininode import COIN
from decimal import Decimal
@ -27,14 +34,19 @@ class WalletZSendmanyTest(BitcoinTestFramework):
# Remember that empty pools are omitted from the output.
def _check_balance_for_rpc(self, rpcmethod, node, account, expected, minconf):
rpc = getattr(self.nodes[node], rpcmethod)
actual = rpc(account) if minconf is None else rpc(account, minconf)
actual = rpc(account, minconf)
assert_equal(set(expected), set(actual['pools']))
total_balance = 0
for pool in expected:
assert_equal(expected[pool] * COIN, actual['pools'][pool]['valueZat'])
assert_equal(actual['minimum_confirmations'], 1 if minconf is None else minconf)
total_balance += expected[pool]
assert_equal(actual['minimum_confirmations'], minconf)
return total_balance
def check_balance(self, node, account, address, expected, minconf=None):
self._check_balance_for_rpc('z_getbalanceforaccount', node, account, expected, minconf)
def check_balance(self, node, account, address, expected, minconf=1):
acct_balance = self._check_balance_for_rpc('z_getbalanceforaccount', node, account, expected, minconf)
z_getbalance = self.nodes[node].z_getbalance(address, minconf)
assert_equal(acct_balance, z_getbalance)
fvk = self.nodes[node].z_exportviewingkey(address)
self._check_balance_for_rpc('z_getbalanceforviewingkey', node, fvk, expected, minconf)
@ -159,9 +171,36 @@ class WalletZSendmanyTest(BitcoinTestFramework):
# Change went to a fresh address, so use `ANY_TADDR` which
# should hold the rest of our transparent funds.
source = 'ANY_TADDR'
recipients = []
recipients.append({"address":n0ua0, "amount":10})
opid = self.nodes[2].z_sendmany('ANY_TADDR', recipients, 1, 0)
# If we attempt to spend with the default privacy policy, z_sendmany
# fails because it needs to spend transparent coins in a transaction
# involving a Unified Address.
revealed_senders_msg = 'This transaction requires selecting transparent coins, which is not enabled by default because it will publicly reveal transaction senders and amounts. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowRevealedSenders` or weaker if you wish to allow this transaction to proceed anyway.'
opid = self.nodes[2].z_sendmany(source, recipients, 1, 0)
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', revealed_senders_msg)
# We can't create a transaction with an unknown privacy policy.
assert_raises_message(
JSONRPCException,
'Unknown privacy policy name \'ZcashIsAwesome\'',
self.nodes[2].z_sendmany,
source, recipients, 1, 0, 'ZcashIsAwesome')
# If we set any policy that does not include AllowRevealedSenders,
# z_sendmany also fails.
for policy in [
'FullPrivacy',
'AllowRevealedAmounts',
'AllowRevealedRecipients',
]:
opid = self.nodes[2].z_sendmany(source, recipients, 1, 0, policy)
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', revealed_senders_msg)
# By setting the correct policy, we can create the transaction.
opid = self.nodes[2].z_sendmany(source, recipients, 1, 0, 'AllowRevealedSenders')
wait_and_assert_operationid_status(self.nodes[2], opid)
self.nodes[2].generate(1)
@ -176,7 +215,32 @@ class WalletZSendmanyTest(BitcoinTestFramework):
# Send some funds to a specific legacy taddr that we can spend from
recipients = []
recipients.append({"address":mytaddr, "amount":5})
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, 0)
# If we attempt to spend with the default privacy policy, z_sendmany
# returns an error because it needs to create a transparent recipient in
# a transaction involving a Unified Address.
assert_raises_message(
JSONRPCException,
'AllowRevealedRecipients',
self.nodes[0].z_sendmany,
n0ua0, recipients, 1, 0)
# If we set any policy that does not include AllowRevealedRecipients,
# z_sendmany also returns an error.
for policy in [
'FullPrivacy',
'AllowRevealedAmounts',
'AllowRevealedSenders',
'AllowLinkingAccountAddresses',
]:
assert_raises_message(
JSONRPCException,
'AllowRevealedRecipients',
self.nodes[0].z_sendmany,
n0ua0, recipients, 1, 0, policy)
# By setting the correct policy, we can create the transaction.
opid = self.nodes[0].z_sendmany(n0ua0, recipients, 1, 0, 'AllowRevealedRecipients')
wait_and_assert_operationid_status(self.nodes[0], opid)
self.nodes[0].generate(1)
@ -199,10 +263,12 @@ class WalletZSendmanyTest(BitcoinTestFramework):
self.check_balance(0, 0, n0ua0, {'sapling': 2})
assert_equal(Decimal(self.nodes[2].z_getbalance(myzaddr)), zbalance)
# Send funds back from the legacy taddr to the UA
# Send funds back from the legacy taddr to the UA. This requires
# AllowRevealedSenders, but we can also use any weaker policy that
# includes it.
recipients = []
recipients.append({"address":n0ua0, "amount":4})
opid = self.nodes[2].z_sendmany(mytaddr, recipients, 1, 0)
opid = self.nodes[2].z_sendmany(mytaddr, recipients, 1, 0, 'AllowFullyTransparent')
wait_and_assert_operationid_status(self.nodes[2], opid)
self.nodes[2].generate(1)
@ -225,5 +291,99 @@ class WalletZSendmanyTest(BitcoinTestFramework):
self.check_balance(0, 0, n0ua0, {'sapling': 8})
assert_equal(Decimal(self.nodes[2].z_getbalance(myzaddr)), zbalance)
#
# Test that z_sendmany avoids UA linkability unless we allow it.
#
# Generate a new account with two new addresses.
n1account = self.nodes[1].z_getnewaccount()['account']
n1ua0 = self.nodes[1].z_getaddressforaccount(n1account)['unifiedaddress']
n1ua1 = self.nodes[1].z_getaddressforaccount(n1account)['unifiedaddress']
# Send funds to the transparent receivers of both addresses.
for ua in [n1ua0, n1ua1]:
taddr = self.nodes[1].z_listunifiedreceivers(ua)['transparent']
self.nodes[0].sendtoaddress(taddr, 2)
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
# The account should see all funds.
assert_equal(
self.nodes[1].z_getbalanceforaccount(n1account)['pools'],
{'transparent': {'valueZat': 4 * COIN}},
)
# The addresses should see only the transparent funds sent to them.
assert_equal(self.nodes[1].z_getbalance(n1ua0), 2)
assert_equal(self.nodes[1].z_getbalance(n1ua1), 2)
# If we try to send 3 ZEC from n1ua0, it will fail with too-few funds.
recipients = [{"address":n0ua0, "amount":3}]
linked_addrs_msg = 'Insufficient funds: have 2.00, need 3.00 (This transaction may require selecting transparent coins that were sent to multiple Unified Addresses, which is not enabled by default because it would create a public link between the transparent receivers of these addresses. THIS MAY AFFECT YOUR PRIVACY. Resubmit with the `privacyPolicy` parameter set to `AllowLinkingAccountAddresses` or weaker if you wish to allow this transaction to proceed anyway.)'
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0)
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', linked_addrs_msg)
# If we try it again with any policy that is too strong, it also fails.
for policy in [
'FullPrivacy',
'AllowRevealedAmounts',
'AllowRevealedRecipients',
'AllowRevealedSenders',
'AllowFullyTransparent',
]:
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0, policy)
wait_and_assert_operationid_status(self.nodes[1], opid, 'failed', linked_addrs_msg)
# Once we provide a sufficiently-weak policy, the transaction succeeds.
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0, 'AllowLinkingAccountAddresses')
wait_and_assert_operationid_status(self.nodes[1], opid)
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
# The account should see the remaining funds, and they should have been
# sent to the Sapling change address (because NU5 is not active).
assert_equal(
self.nodes[1].z_getbalanceforaccount(n1account)['pools'],
{'sapling': {'valueZat': 1 * COIN}},
)
# The addresses should both show the same balance, as they both show the
# Sapling balance.
assert_equal(self.nodes[1].z_getbalance(n1ua0), 1)
assert_equal(self.nodes[1].z_getbalance(n1ua1), 1)
#
# Test NoPrivacy policy
#
# Send some legacy transparent funds to n1ua0, creating Sapling outputs.
recipients = [{"address":n1ua0, "amount":10}]
# This requires the AllowRevealedSenders policy...
opid = self.nodes[2].z_sendmany('ANY_TADDR', recipients, 1, 0)
wait_and_assert_operationid_status(self.nodes[2], opid, 'failed', revealed_senders_msg)
# ... which we can always override with the NoPrivacy policy.
opid = self.nodes[2].z_sendmany('ANY_TADDR', recipients, 1, 0, 'NoPrivacy')
wait_and_assert_operationid_status(self.nodes[2], opid)
self.sync_all()
self.nodes[2].generate(1)
self.sync_all()
# Send some funds from node 1's account to a transparent address.
recipients = [{"address":mytaddr, "amount":5}]
# This requires the AllowRevealedRecipients policy...
assert_raises_message(
JSONRPCException,
'AllowRevealedRecipients',
self.nodes[1].z_sendmany,
n1ua0, recipients, 1, 0)
# ... which we can always override with the NoPrivacy policy.
opid = self.nodes[1].z_sendmany(n1ua0, recipients, 1, 0, 'NoPrivacy')
wait_and_assert_operationid_status(self.nodes[1], opid)
if __name__ == '__main__':
WalletZSendmanyTest().main()

View File

@ -46,6 +46,17 @@ RE_RPATH_RUNPATH = re.compile('No RPATH.*No RUNPATH')
RE_FORTIFY_AVAILABLE = re.compile('FORTIFY_SOURCE support available.*Yes')
RE_FORTIFY_USED = re.compile('Binary compiled with FORTIFY_SOURCE support.*Yes')
CXX_BINARIES = [
'src/zcashd',
'src/zcash-cli',
'src/zcash-gtest',
'src/zcash-tx',
'src/test/test_bitcoin',
]
RUST_BINARIES = [
'src/zcashd-wallet-tool',
]
def test_rpath_runpath(filename):
output = subprocess.check_output(
[repofile('qa/zcash/checksec.sh'), '--file=' + repofile(filename)]
@ -86,21 +97,14 @@ def check_security_hardening():
if not magic.startswith(b'\x7fELF'):
return ret
ret &= test_rpath_runpath('src/zcashd')
ret &= test_rpath_runpath('src/zcash-cli')
ret &= test_rpath_runpath('src/zcash-gtest')
ret &= test_rpath_runpath('src/zcash-tx')
ret &= test_rpath_runpath('src/test/test_bitcoin')
for bin in CXX_BINARIES + RUST_BINARIES:
ret &= test_rpath_runpath(bin)
# NOTE: checksec.sh does not reliably determine whether FORTIFY_SOURCE
# is enabled for the entire binary. See issue #915.
# FORTIFY_SOURCE does mostly nothing for Clang before 10, which we don't
# pin yet, so we disable these tests.
# ret &= test_fortify_source('src/zcashd')
# ret &= test_fortify_source('src/zcash-cli')
# ret &= test_fortify_source('src/zcash-gtest')
# ret &= test_fortify_source('src/zcash-tx')
# ret &= test_fortify_source('src/test/test_bitcoin')
# FORTIFY_SOURCE is not applicable to Rust binaries.
for bin in CXX_BINARIES:
ret &= test_fortify_source(bin)
return ret

View File

@ -5,23 +5,25 @@
#
# Ccache 4.0 requires adding CMake to the depends system.
native_ccache 4.0 2022-02-01
native_ccache 4.1 2022-02-01
native_ccache 4.2 2022-02-01
native_ccache 4.2.1 2022-02-01
native_ccache 4.3 2022-02-01
native_ccache 4.4 2022-02-01
native_ccache 4.4.1 2022-02-01
native_ccache 4.4.2 2022-02-01
native_ccache 4.5 2022-02-01
native_ccache 4.5.1 2022-02-01
native_ccache 4.0 2022-05-01
native_ccache 4.1 2022-05-01
native_ccache 4.2 2022-05-01
native_ccache 4.2.1 2022-05-01
native_ccache 4.3 2022-05-01
native_ccache 4.4 2022-05-01
native_ccache 4.4.1 2022-05-01
native_ccache 4.4.2 2022-05-01
native_ccache 4.5 2022-05-01
native_ccache 4.5.1 2022-05-01
native_ccache 4.6 2022-05-01
# Clang is currently pinned to LLVM 13
# Rust is currently pinned to 1.56.1
# Rust is currently pinned to 1.59.0
bdb 18.1.40 2022-02-01
# We're never updating to this version
bdb 18.1.40 2024-02-01
# Google Test 1.10.0 requires adding CMake to the depends system.
googletest 1.10.0 2022-02-01
googletest 1.11.0 2022-02-01
googletest 1.10.0 2022-05-01
googletest 1.11.0 2022-05-01

View File

@ -328,7 +328,9 @@ def main():
# packages.mk is not a dependency, it just specifies the list of them all.
"packages",
# This package doesn't have conventional version numbers
"native_cctools"
"native_cctools",
# This package is pinned specifically for Arch.
"native_libtinfo",
]
print_row("NAME", "STATUS", "CURRENT VERSION", "NEWER VERSIONS")

View File

@ -1 +1 @@
1.56.1
1.59.0

View File

@ -29,6 +29,9 @@ LIBSECP256K1=secp256k1/libsecp256k1.la
LIBUNIVALUE=univalue/libunivalue.la
LIBZCASH=libzcash.a
WALLET_TOOL_BIN=zcashd-wallet-tool$(EXEEXT)
WALLET_TOOL_BUILD=$(top_builddir)/target/$(RUST_TARGET)/release/zcashd-wallet-tool$(EXEEXT)
if ENABLE_ZMQ
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
endif
@ -47,7 +50,7 @@ endif
# - Note that this does not prevent the secp256k1-sys vendored code from being built; this
# requires https://github.com/rust-bitcoin/rust-secp256k1/issues/380 to be addressed.
RUST_ENV_VARS = RUSTC="$(RUSTC)" TERM=dumb RUSTFLAGS="--cfg=rust_secp_no_symbol_renaming"
RUST_BUILD_OPTS = --lib --release --target $(RUST_TARGET)
RUST_BUILD_OPTS = --release --target $(RUST_TARGET) --manifest-path $(top_srcdir)/Cargo.toml
rust_verbose = $(rust_verbose_@AM_V@)
rust_verbose_ = $(rust_verbose_@AM_DEFAULT_V@)
@ -72,10 +75,16 @@ $(CARGO_CONFIGURED): $(top_srcdir)/.cargo/config.offline
$(AM_V_at)touch $@
endif
cargo-build: $(CARGO_CONFIGURED)
$(RUST_ENV_VARS) $(CARGO) build $(RUST_BUILD_OPTS) $(rust_verbose) --manifest-path $(top_srcdir)/Cargo.toml
cargo-build-lib: $(CARGO_CONFIGURED)
$(RUST_ENV_VARS) $(CARGO) build --lib $(RUST_BUILD_OPTS) $(rust_verbose)
$(LIBRUSTZCASH): cargo-build
cargo-build-bins: $(CARGO_CONFIGURED)
$(RUST_ENV_VARS) $(CARGO) build --bins $(RUST_BUILD_OPTS) $(rust_verbose)
$(WALLET_TOOL_BIN): cargo-build-bins
$(AM_V_at)cp $(WALLET_TOOL_BUILD) $@
$(LIBRUSTZCASH): cargo-build-lib
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
@ -99,6 +108,7 @@ lib_LTLIBRARIES = $(LIBZCASH_SCRIPT)
bin_PROGRAMS =
noinst_PROGRAMS =
bin_SCRIPTS =
TESTS =
BENCHMARKS =
@ -108,6 +118,7 @@ endif
if BUILD_BITCOIN_UTILS
bin_PROGRAMS += zcash-cli zcash-tx
bin_SCRIPTS += $(WALLET_TOOL_BIN)
endif
LIBZCASH_H = \
@ -116,7 +127,7 @@ LIBZCASH_H = \
zcash/Address.hpp \
zcash/address/transparent.h \
zcash/address/mnemonic.h \
zcash/address/orchard.h \
zcash/address/orchard.hpp \
zcash/address/sapling.hpp \
zcash/address/sprout.hpp \
zcash/address/unified.h \
@ -129,7 +140,7 @@ LIBZCASH_H = \
zcash/util.h \
zcash/Zcash.h
.PHONY: FORCE cargo-build check-symbols check-security
.PHONY: FORCE cargo-build-lib cargo-build-bins check-symbols check-security
# bitcoin core #
BITCOIN_CORE_H = \
addrdb.h \
@ -234,6 +245,7 @@ BITCOIN_CORE_H = \
uint252.h \
undo.h \
util.h \
util/match.h \
utilmoneystr.h \
utilstrencodings.h \
utiltest.h \
@ -247,6 +259,7 @@ BITCOIN_CORE_H = \
wallet/asyncrpcoperation_shieldcoinbase.h \
wallet/crypter.h \
wallet/db.h \
wallet/orchard.h \
wallet/paymentdisclosure.h \
wallet/paymentdisclosuredb.h \
wallet/rpcwallet.h \
@ -334,6 +347,7 @@ libbitcoin_wallet_a_SOURCES = \
wallet/asyncrpcoperation_shieldcoinbase.cpp \
wallet/crypter.cpp \
wallet/db.cpp \
wallet/orchard.cpp \
wallet/paymentdisclosure.cpp \
wallet/paymentdisclosuredb.cpp \
wallet/rpcdisclosure.cpp \
@ -601,7 +615,7 @@ CTAES_DIST += crypto/ctaes/ctaes.h
CTAES_DIST += crypto/ctaes/README.md
CTAES_DIST += crypto/ctaes/test.c
CLEANFILES = *.gcda *.gcno */*.gcno wallet/*/*.gcno
CLEANFILES = *.gcda *.gcno */*.gcno wallet/*/*.gcno $(bin_SCRIPTS)
DISTCLEANFILES = obj/build.h
@ -625,16 +639,17 @@ clean-local:
$(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CXXFLAGS) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $<
check-symbols: $(bin_PROGRAMS)
check-symbols: $(bin_PROGRAMS) $(bin_SCRIPTS)
if GLIBC_BACK_COMPAT
@echo "Checking glibc back compat of [$(bin_PROGRAMS)]..."
$(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
@echo "Checking glibc back compat of [$(bin_PROGRAMS) $(bin_SCRIPTS)]..."
$(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS) $(bin_SCRIPTS)
endif
check-security: $(bin_PROGRAMS)
check-security: $(bin_PROGRAMS) $(bin_SCRIPTS)
if HARDEN
@echo "Checking binary security of [$(bin_PROGRAMS)]..."
@echo "Checking binary security of [$(bin_PROGRAMS) $(bin_SCRIPTS)]..."
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS)
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py --allow-no-canary $(bin_SCRIPTS)
endif
%.pb.cc %.pb.h: %.proto

View File

@ -54,12 +54,15 @@ zcash_gtest_SOURCES += \
gtest/test_timedata.cpp \
gtest/test_transaction.cpp \
gtest/test_transaction_builder.cpp \
gtest/test_transaction_builder.h \
gtest/test_txid.cpp \
gtest/test_upgrades.cpp \
gtest/test_validation.cpp \
gtest/test_zip32.cpp
if ENABLE_WALLET
zcash_gtest_SOURCES += \
wallet/gtest/test_note_selection.cpp \
wallet/gtest/test_orchard_wallet.cpp \
wallet/gtest/test_paymentdisclosure.cpp \
wallet/gtest/test_wallet.cpp
endif

View File

@ -39,7 +39,7 @@ std::string HelpMessageCli()
strUsage += HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("This help message"));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory (this path cannot use '~')"));
strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). If first extra argument is `walletpassphrase` then the first line(password) will not be echoed."));
AppendParamsHelpMessages(strUsage);
strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));

View File

@ -133,7 +133,7 @@ public:
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight = 1046400;
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].hashActivationBlock =
uint256S("00000000002038016f976744c369dce7419fca30e7171dfac703af5e5f7ad1d4");
consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170017;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170100;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nProtocolVersion = 0x7FFFFFFF;
@ -417,8 +417,9 @@ public:
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight = 1028500;
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].hashActivationBlock =
uint256S("01a4d7c6aada30c87762c1bf33fff5df7266b1fd7616bfdb5227fa59bd79e7a2");
consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170015;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight = 1599200;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170050;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nProtocolVersion = 0x7FFFFFFF;
consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
@ -633,7 +634,7 @@ public:
static_assert(equihash_parameters_acceptable(N, K));
consensus.nEquihashN = N;
consensus.nEquihashK = K;
consensus.powLimit = uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f");
consensus.powLimit = uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); // if this is any larger, the for loop in GetNextWorkRequired can overflow bnTot
consensus.nPowAveragingWindow = 17;
assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow);
consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down
@ -663,7 +664,7 @@ public:
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nProtocolVersion = 170012;
consensus.vUpgrades[Consensus::UPGRADE_CANOPY].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170015;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nProtocolVersion = 170040;
consensus.vUpgrades[Consensus::UPGRADE_NU5].nActivationHeight =
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
consensus.vUpgrades[Consensus::UPGRADE_ZFUTURE].nProtocolVersion = 0x7FFFFFFF;

View File

@ -1,5 +1,5 @@
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Copyright (c) 2016-2021 The Zcash developers
// Copyright (c) 2016-2022 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
@ -16,9 +16,9 @@
//! These need to be macros, as clientversion.cpp's and bitcoin*-res.rc's voodoo requires it
#define CLIENT_VERSION_MAJOR 4
#define CLIENT_VERSION_MINOR 6
#define CLIENT_VERSION_MINOR 7
#define CLIENT_VERSION_REVISION 0
#define CLIENT_VERSION_BUILD 51
#define CLIENT_VERSION_BUILD 25
//! Set to true for release, false for prerelease or test build
#define CLIENT_VERSION_IS_RELEASE true
@ -27,7 +27,7 @@
* Copyright year (2009-this)
* Todo: update this when changing our copyright comments in the source
*/
#define COPYRIGHT_YEAR 2021
#define COPYRIGHT_YEAR 2022
#endif //HAVE_CONFIG_H

View File

@ -683,6 +683,14 @@ void CCoinsViewCache::PopAnchor(const uint256 &newrt, ShieldedType type) {
hashSaplingAnchor
);
break;
case ORCHARD:
AbstractPopAnchor<OrchardMerkleFrontier, CAnchorsOrchardMap, CAnchorsOrchardCacheEntry>(
newrt,
ORCHARD,
cacheOrchardAnchors,
hashOrchardAnchor
);
break;
default:
throw std::runtime_error("Unknown shielded type");
}

View File

@ -12,6 +12,15 @@
#include "util/match.h"
namespace Consensus {
std::optional<int> Params::GetActivationHeight(Consensus::UpgradeIndex idx) const {
auto nActivationHeight = vUpgrades[idx].nActivationHeight;
if (nActivationHeight == Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) {
return std::nullopt;
} else {
return nActivationHeight;
}
}
bool Params::NetworkUpgradeActive(int nHeight, Consensus::UpgradeIndex idx) const {
return NetworkUpgradeState(nHeight, *this, idx) == UPGRADE_ACTIVE;
}

View File

@ -225,6 +225,11 @@ static const unsigned int PRE_BLOSSOM_REGTEST_HALVING_INTERVAL = 144;
* Parameters that influence chain consensus.
*/
struct Params {
/**
* Returns the activation height for the specified network upgrade, if any.
*/
std::optional<int> GetActivationHeight(Consensus::UpgradeIndex idx) const;
/**
* Returns true if the given network upgrade is active as of the given block
* height. Caller must check that the height is >= 0 (and handle unknown

View File

@ -45,7 +45,7 @@ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = {
.strInfo = "See https://z.cash/upgrade/canopy/ for details.",
},
{
.nBranchId = 0x37519621,
.nBranchId = 0xc2d6d0b4,
.strName = "NU5",
.strInfo = "See https://z.cash/upgrade/nu5/ for details.",
},

View File

@ -2,6 +2,7 @@
#include "key.h"
#include "pubkey.h"
#include "util.h"
#include "utiltest.h"
#include "librustzcash.h"
#include <sodium.h>
@ -19,26 +20,6 @@ int main(int argc, char **argv) {
assert(sodium_init() != -1);
ECC_Start();
fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params";
fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params";
fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params";
static_assert(
sizeof(fs::path::value_type) == sizeof(codeunit),
"librustzcash not configured correctly");
auto sapling_spend_str = sapling_spend.native();
auto sapling_output_str = sapling_output.native();
auto sprout_groth16_str = sprout_groth16.native();
librustzcash_init_zksnark_params(
reinterpret_cast<const codeunit*>(sapling_spend_str.c_str()),
sapling_spend_str.length(),
reinterpret_cast<const codeunit*>(sapling_output_str.c_str()),
sapling_output_str.length(),
reinterpret_cast<const codeunit*>(sprout_groth16_str.c_str()),
sprout_groth16_str.length()
);
testing::InitGoogleMock(&argc, argv);
// The "threadsafe" style is necessary for correct operation of death/exit

View File

@ -34,6 +34,9 @@ TEST(CheckBlock, VersionTooLow) {
block.nVersion = 1;
MockCValidationState state;
SelectParams(CBaseChainParams::MAIN);
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "version-too-low", false, "")).Times(1);
EXPECT_FALSE(CheckBlock(block, state, Params(), verifier, false, false, true));
}
@ -89,7 +92,7 @@ protected:
void TearDown() override {
// Revert to test default. No-op on mainnet params.
RegtestDeactivateSapling();
RegtestDeactivateBlossom();
}
// Returns a valid but empty mutable transaction at block height 1.

View File

@ -1159,6 +1159,8 @@ TEST(ChecktransactionTests, InvalidShieldedCoinbase) {
}
TEST(ChecktransactionTests, HeartwoodAcceptsShieldedCoinbase) {
LoadProofParameters();
RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto chainparams = Params();
@ -1239,6 +1241,8 @@ TEST(ChecktransactionTests, HeartwoodAcceptsShieldedCoinbase) {
// bindingSig from https://zips.z.cash/protocol/protocol.pdf#txnencoding are
// applied to coinbase transactions.
TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) {
LoadProofParameters();
RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto chainparams = Params();

View File

@ -11,6 +11,8 @@
#include "transaction_builder.h"
#include "utiltest.h"
#include <optional>
TEST(RecursiveDynamicUsageTests, TestTransactionTransparent)
{
auto consensusParams = RegtestActivateSapling();
@ -20,7 +22,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionTransparent)
auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
CTxDestination taddr = tsk.GetPubKey().GetID();
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
builder.AddTransparentOutput(taddr, 40000);
@ -48,12 +50,14 @@ TEST(RecursiveDynamicUsageTests, TestTransactionJoinSplit)
TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToSapling)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
auto testNote = GetTestSaplingNote(sk.default_address(), 50000);
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {});
@ -67,6 +71,8 @@ TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToSapling)
TEST(RecursiveDynamicUsageTests, TestTransactionTransparentToSapling)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
CBasicKeyStore keystore;
@ -74,7 +80,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionTransparentToSapling)
auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
auto sk = libzcash::SaplingSpendingKey::random();
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 40000, {});
@ -88,6 +94,8 @@ TEST(RecursiveDynamicUsageTests, TestTransactionTransparentToSapling)
TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToTransparent)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
CBasicKeyStore keystore;
@ -96,7 +104,7 @@ TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToTransparent)
auto sk = libzcash::SaplingSpendingKey::random();
auto testNote = GetTestSaplingNote(sk.default_address(), 50000);
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddTransparentOutput(taddr, 40000);

View File

@ -11,6 +11,7 @@
#include "primitives/transaction.h"
#include "proof_verifier.h"
#include "transaction_builder.h"
#include "utiltest.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
@ -309,6 +310,8 @@ void increment_note_witnesses(
TEST(Joinsplit, FullApiTest)
{
LoadProofParameters();
{
std::vector<SproutWitness> witnesses;
SproutMerkleTree tree;

View File

@ -69,34 +69,8 @@ TEST(Keys, EncodeAndDecodeSapling)
#define MAKE_STRING(x) std::string((x), (x) + sizeof(x))
namespace libzcash {
class ReceiverToString {
public:
ReceiverToString() {}
std::string operator()(const SaplingPaymentAddress &zaddr) const {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zaddr;
return tfm::format("Sapling(%s)", HexStr(ss.begin(), ss.end()));
}
std::string operator()(const CScriptID &p2sh) const {
return tfm::format("P2SH(%s)", p2sh.GetHex());
}
std::string operator()(const CKeyID &p2pkh) const {
return tfm::format("P2PKH(%s)", p2pkh.GetHex());
}
std::string operator()(const UnknownReceiver &unknown) const {
return tfm::format(
"Unknown(%x, %s)",
unknown.typecode,
HexStr(unknown.data.begin(), unknown.data.end()));
}
};
void PrintTo(const Receiver& receiver, std::ostream* os) {
*os << std::visit(ReceiverToString(), receiver);
*os << DebugPrintReceiver(receiver);
}
void PrintTo(const UnifiedAddress& ua, std::ostream* os) {
*os << "UnifiedAddress(" << testing::PrintToString(ua.GetReceiversAsParsed()) << ")";
@ -126,8 +100,11 @@ TEST(Keys, EncodeAndDecodeUnifiedAddresses)
// These were added to the UA in preference order by the Python test vectors.
if (!test[3].isNull()) {
auto data = ParseHex(test[3].get_str());
libzcash::UnknownReceiver r(0x03, data);
ua.AddReceiver(r);
CDataStream ss(
data,
SER_NETWORK,
PROTOCOL_VERSION);
ua.AddReceiver(libzcash::OrchardRawAddress::Read(ss));
}
if (!test[2].isNull()) {
auto data = ParseHex(test[2].get_str());
@ -186,6 +163,16 @@ TEST(Keys, DeriveUnifiedFullViewingKeys)
if (test.size() == 1) continue; // comment
try {
// [
// t_key_bytes,
// sapling_fvk_bytes,
// orchard_fvk_bytes,
// unknown_fvk_typecode,
// unknown_fvk_bytes,
// unified_fvk,
// root_seed,
// account,
// ],
auto seed_hex = test[6].get_str();
auto seed_data = ParseHex(seed_hex);
RawHDSeed raw_seed(seed_data.begin(), seed_data.end());
@ -235,6 +222,24 @@ TEST(Keys, DeriveUnifiedFullViewingKeys)
auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss);
EXPECT_EQ(key, saplingKey);
}
if (!test[2].isNull()) {
auto expectedHex = test[2].get_str();
// Ensure that the serialized Orchard fvk matches the test data.
auto orchardKey = ufvk.GetOrchardKey().value();
CDataStream ssEncode(SER_NETWORK, PROTOCOL_VERSION);
ssEncode << orchardKey;
EXPECT_EQ(ssEncode.size(), 96);
auto skeyHex = HexStr(ssEncode.begin(), ssEncode.end());
EXPECT_EQ(expectedHex, skeyHex);
// Ensure that parsing the test data derives the correct dfvk
auto data = ParseHex(expectedHex);
ASSERT_EQ(data.size(), 96);
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
auto key = libzcash::OrchardFullViewingKey::Read(ss);
EXPECT_EQ(key, orchardKey);
}
// Enable the following after Orchard keys are supported.
//{
// auto fvk_data = ParseHex(test[5].get_str());
@ -284,14 +289,13 @@ TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys)
auto key = libzcash::SaplingDiversifiableFullViewingKey::Read(ss);
ASSERT_TRUE(builder.AddSaplingKey(key));
}
// Orchard keys and unknown items are not yet supported; instead,
// we just test that we're able to parse the unified key string
// and that the constituent items match the elements; if no Sapling
// key is present then UFVK construction would fail because it might
// presume the UFVK to be transparent-only.
if (test[1].isNull())
continue;
if (!test[2].isNull()) {
auto data = ParseHex(test[2].get_str());
ASSERT_EQ(data.size(), 96);
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
auto key = libzcash::OrchardFullViewingKey::Read(ss);
ASSERT_TRUE(builder.AddOrchardKey(key));
}
auto built = builder.build();
ASSERT_TRUE(built.has_value());
@ -304,5 +308,6 @@ TEST(Keys, EncodeAndDecodeUnifiedFullViewingKeys)
EXPECT_EQ(decoded.value().GetTransparentKey(), built.value().GetTransparentKey());
EXPECT_EQ(decoded.value().GetSaplingKey(), built.value().GetSaplingKey());
EXPECT_EQ(decoded.value().GetOrchardKey(), built.value().GetOrchardKey());
}
}

View File

@ -557,20 +557,55 @@ TEST(KeystoreTests, StoreAndRetrieveUFVK) {
EXPECT_EQ(keyStore.GetUnifiedFullViewingKey(ufvkid).value(), zufvk);
auto addrPair = std::get<std::pair<UnifiedAddress, diversifier_index_t>>(zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::Sapling}));
EXPECT_TRUE(addrPair.first.GetSaplingReceiver().has_value());
auto saplingReceiver = addrPair.first.GetSaplingReceiver().value();
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
EXPECT_FALSE(ufvkmeta.has_value());
// We detect this even though we haven't added the Sapling address, because
// we trial-decrypt diversifiers (which also means we learn the index).
auto ufvkmetaUnadded = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
EXPECT_TRUE(ufvkmetaUnadded.has_value());
EXPECT_EQ(ufvkmetaUnadded.value().GetUFVKId(), ufvkid);
EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex().value(), addrPair.second);
// Adding the Sapling addr -> ivk map entry causes us to find the same UFVK,
// and since we trial-decrypt with both external and internal IVKs to
// verify whether it's an internal address, we learn the index.
auto saplingIvk = zufvk.GetSaplingKey().value().ToIncomingViewingKey();
keyStore.AddSaplingPaymentAddress(saplingIvk, saplingReceiver);
ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
auto ufvkmeta = keyStore.GetUFVKMetadataForReceiver(saplingReceiver);
EXPECT_TRUE(ufvkmeta.has_value());
EXPECT_EQ(ufvkmeta.value().first, ufvkid);
EXPECT_FALSE(ufvkmeta.value().second.has_value());
EXPECT_EQ(ufvkmeta.value().GetUFVKId(), ufvkid);
EXPECT_TRUE(ufvkmeta.value().GetDiversifierIndex().has_value());
}
TEST(KeystoreTests, StoreAndRetrieveUFVKByOrchard) {
SelectParams(CBaseChainParams::TESTNET);
CBasicKeyStore keyStore;
auto seed = MnemonicSeed::Random(SLIP44_TESTNET_TYPE);
auto usk = ZcashdUnifiedSpendingKey::ForAccount(seed, SLIP44_TESTNET_TYPE, 0);
EXPECT_TRUE(usk.has_value());
auto ufvk = usk.value().ToFullViewingKey();
auto zufvk = ZcashdUnifiedFullViewingKey::FromUnifiedFullViewingKey(Params(), ufvk);
auto ufvkid = zufvk.GetKeyID();
EXPECT_FALSE(keyStore.GetUnifiedFullViewingKey(ufvkid).has_value());
EXPECT_TRUE(keyStore.AddUnifiedFullViewingKey(zufvk));
EXPECT_EQ(keyStore.GetUnifiedFullViewingKey(ufvkid).value(), zufvk);
auto addrPair = std::get<std::pair<UnifiedAddress, diversifier_index_t>>(zufvk.FindAddress(diversifier_index_t(0), {ReceiverType::Orchard}));
EXPECT_TRUE(addrPair.first.GetOrchardReceiver().has_value());
auto orchardReceiver = addrPair.first.GetOrchardReceiver().value();
// We don't store Orchard addresses in CBasicKeyStore (the addr -> ivk
// mapping is stored in the Rust wallet), but we still detect this because
// we trial-decrypt diversifiers (which also means we learn the index).
auto ufvkmetaUnadded = keyStore.GetUFVKMetadataForReceiver(orchardReceiver);
EXPECT_TRUE(ufvkmetaUnadded.has_value());
EXPECT_EQ(ufvkmetaUnadded.value().GetUFVKId(), ufvkid);
EXPECT_EQ(ufvkmetaUnadded.value().GetDiversifierIndex().value(), addrPair.second);
}
TEST(KeystoreTests, AddTransparentReceiverForUnifiedAddress) {
@ -593,7 +628,7 @@ TEST(KeystoreTests, AddTransparentReceiverForUnifiedAddress) {
ufvkmeta = keyStore.GetUFVKMetadataForReceiver(addrPair.first.GetP2PKHReceiver().value());
EXPECT_TRUE(ufvkmeta.has_value());
EXPECT_EQ(ufvkmeta.value().first, ufvkid);
EXPECT_EQ(ufvkmeta.value().GetUFVKId(), ufvkid);
}

View File

@ -110,6 +110,8 @@ TEST(MempoolLimitTests, WeightedTxTreeCheckSizeAfterDropping)
TEST(MempoolLimitTests, WeightedTxInfoFromTx)
{
LoadProofParameters();
// The transaction creation is based on the test:
// test_transaction_builder.cpp/TEST(TransactionBuilder, SetFee)
auto consensusParams = RegtestActivateSapling();
@ -119,7 +121,7 @@ TEST(MempoolLimitTests, WeightedTxInfoFromTx)
// Default fee
{
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 25000, {});
@ -130,7 +132,7 @@ TEST(MempoolLimitTests, WeightedTxInfoFromTx)
// Lower than standard fee
{
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 25000, {});
static_assert(DEFAULT_FEE == 1000);
@ -143,7 +145,7 @@ TEST(MempoolLimitTests, WeightedTxInfoFromTx)
// Larger Tx
{
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(sk.expanded_spending_key(), testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {});
builder.AddSaplingOutput(sk.full_viewing_key().ovk, sk.default_address(), 5000, {});

View File

@ -310,5 +310,15 @@ TEST(orchardMerkleTree, appendBundle) {
uint256 anchor(merkle_roots_orchard[i].anchor);
ASSERT_EQ(newTree.root(), anchor);
// Sanity check roundtrip serialization of the updated tree
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << newTree;
OrchardMerkleFrontier readBack;
ss >> readBack;
EXPECT_NE(newTree.root(), OrchardMerkleFrontier::empty_root());
EXPECT_EQ(newTree.root(), readBack.root());
}
}

View File

@ -92,6 +92,12 @@ TEST(Metrics, GetLocalSolPS) {
// Increment time
SetMockTime(104);
EXPECT_EQ(1, GetLocalSolPS());
miningTimer.stop();
miningTimer.zeroize();
solutionTargetChecks.decrement();
solutionTargetChecks.decrement();
solutionTargetChecks.decrement();
}
TEST(Metrics, EstimateNetHeight) {

View File

@ -7,73 +7,20 @@
#include "pubkey.h"
#include "rpc/protocol.h"
#include "transaction_builder.h"
#include "gtest/test_transaction_builder.h"
#include "utiltest.h"
#include "zcash/Address.hpp"
#include "zcash/address/mnemonic.h"
#include <optional>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
// Fake an empty view
class TransactionBuilderCoinsViewDB : public CCoinsView {
public:
std::map<uint256, SproutMerkleTree> sproutTrees;
TransactionBuilderCoinsViewDB() {}
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
auto it = sproutTrees.find(rt);
if (it != sproutTrees.end()) {
tree = it->second;
return true;
} else {
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;
}
};
TEST(TransactionBuilder, TransparentToSapling)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
CBasicKeyStore keystore;
@ -92,7 +39,7 @@ TEST(TransactionBuilder, TransparentToSapling)
// Create a shielding transaction from transparent to Sapling
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, default fee
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddTransparentInput(COutPoint(uint256S("1234"), 0), scriptPubKey, 50000);
builder.AddSaplingOutput(fvk_from.ovk, pk, 40000, {});
auto tx = builder.Build().GetTxOrThrow();
@ -113,6 +60,8 @@ TEST(TransactionBuilder, TransparentToSapling)
}
TEST(TransactionBuilder, SaplingToSapling) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
@ -124,7 +73,7 @@ TEST(TransactionBuilder, SaplingToSapling) {
// Create a Sapling-only transaction
// 0.0004 z-ZEC in, 0.00025 z-ZEC out, default fee, 0.00005 z-ZEC change
auto builder = TransactionBuilder(consensusParams, 2);
auto builder = TransactionBuilder(consensusParams, 2, std::nullopt);
builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness());
// Check that trying to add a different anchor fails
@ -150,6 +99,8 @@ TEST(TransactionBuilder, SaplingToSapling) {
}
TEST(TransactionBuilder, SaplingToSprout) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
@ -165,7 +116,7 @@ TEST(TransactionBuilder, SaplingToSprout) {
// - 0.0004 Sapling-ZEC in - 0.00025 Sprout-ZEC out
// - 0.00005 Sapling-ZEC change
// - default t-ZEC fee
auto builder = TransactionBuilder(consensusParams, 2, nullptr);
auto builder = TransactionBuilder(consensusParams, 2, std::nullopt, nullptr);
builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSproutOutput(sproutAddr, 25000);
auto tx = builder.Build().GetTxOrThrow();
@ -188,6 +139,8 @@ TEST(TransactionBuilder, SaplingToSprout) {
}
TEST(TransactionBuilder, SproutToSproutAndSapling) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
@ -217,7 +170,7 @@ TEST(TransactionBuilder, SproutToSproutAndSapling) {
// - 0.00005 Sprout-ZEC change
// - 0.00005 Sapling-ZEC out
// - 0.00005 t-ZEC fee
auto builder = TransactionBuilder(consensusParams, 2, nullptr, &view);
auto builder = TransactionBuilder(consensusParams, 2, std::nullopt, nullptr, &view);
builder.SetFee(5000);
builder.AddSproutInput(sproutSk, sproutNote, sproutWitness);
builder.AddSproutOutput(sproutAddr, 6000);
@ -248,12 +201,60 @@ TEST(TransactionBuilder, SproutToSproutAndSapling) {
RegtestDeactivateSapling();
}
TEST(TransactionBuilder, TransparentToOrchard)
{
auto consensusParams = RegtestActivateNU5();
CBasicKeyStore keystore;
CKey tsk = AddTestCKeyToKeyStore(keystore);
auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
auto coinType = Params().BIP44CoinType();
auto seed = MnemonicSeed::Random(coinType);
auto sk = libzcash::OrchardSpendingKey::ForAccount(seed, coinType, 0);
auto fvk = sk.ToFullViewingKey();
auto ivk = fvk.ToIncomingViewingKey();
libzcash::diversifier_index_t j(0);
auto recipient = ivk.Address(j);
TransactionBuilderCoinsViewDB fakeDB;
auto orchardAnchor = fakeDB.GetBestAnchor(ShieldedType::ORCHARD);
// Create a shielding transaction from transparent to Orchard
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, default fee
auto builder = TransactionBuilder(consensusParams, 1, orchardAnchor, &keystore);
builder.AddTransparentInput(COutPoint(uint256S("1234"), 0), scriptPubKey, 50000);
builder.AddOrchardOutput(std::nullopt, recipient, 40000, std::nullopt);
auto maybeTx = builder.Build();
EXPECT_TRUE(maybeTx.IsTx());
if (maybeTx.IsError()) {
std::cerr << "Failed to build transaction: " << maybeTx.GetError() << std::endl;
GTEST_FAIL();
}
auto tx = maybeTx.GetTxOrThrow();
EXPECT_EQ(tx.vin.size(), 1);
EXPECT_EQ(tx.vout.size(), 0);
EXPECT_EQ(tx.vJoinSplit.size(), 0);
EXPECT_EQ(tx.vShieldedSpend.size(), 0);
EXPECT_EQ(tx.vShieldedOutput.size(), 0);
EXPECT_TRUE(tx.GetOrchardBundle().IsPresent());
EXPECT_EQ(tx.GetOrchardBundle().GetValueBalance(), -40000);
CValidationState state;
EXPECT_TRUE(ContextualCheckTransaction(tx, state, Params(), 2, true));
EXPECT_EQ(state.GetRejectReason(), "");
// Revert to default
RegtestDeactivateNU5();
}
TEST(TransactionBuilder, ThrowsOnTransparentInputWithoutKeyStore)
{
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& consensusParams = Params().GetConsensus();
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
ASSERT_THROW(builder.AddTransparentInput(COutPoint(), CScript(), 1), std::runtime_error);
}
@ -264,12 +265,14 @@ TEST(TransactionBuilder, RejectsInvalidTransparentOutput)
// Default CTxDestination type is an invalid address
CTxDestination taddr;
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
ASSERT_THROW(builder.AddTransparentOutput(taddr, 50), UniValue);
}
TEST(TransactionBuilder, FailsWithNegativeChange)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
// Generate dummy Sapling address
@ -289,13 +292,13 @@ TEST(TransactionBuilder, FailsWithNegativeChange)
// Fail if there is only a Sapling output
// 0.0005 z-ZEC out, default fee
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingOutput(fvk.ovk, pa, 50000, {});
EXPECT_EQ("Change cannot be negative", builder.Build().GetError());
// Fail if there is only a transparent output
// 0.0005 t-ZEC out, default fee
builder = TransactionBuilder(consensusParams, 1, &keystore);
builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddTransparentOutput(taddr, 50000);
EXPECT_EQ("Change cannot be negative", builder.Build().GetError());
@ -314,6 +317,8 @@ TEST(TransactionBuilder, FailsWithNegativeChange)
TEST(TransactionBuilder, ChangeOutput)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
// Generate dummy Sapling address
@ -336,14 +341,14 @@ TEST(TransactionBuilder, ChangeOutput)
// No change address and no Sapling spends
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
EXPECT_EQ("Could not determine change address", builder.Build().GetError());
}
// Change to the same address as the first Sapling spend
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness());
auto tx = builder.Build().GetTxOrThrow();
@ -358,7 +363,7 @@ TEST(TransactionBuilder, ChangeOutput)
// Change to a Sapling address
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
builder.SendChangeTo(zChangeAddr, fvkOut.ovk);
auto tx = builder.Build().GetTxOrThrow();
@ -373,7 +378,7 @@ TEST(TransactionBuilder, ChangeOutput)
// Change to a transparent address
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
builder.SendChangeTo(tkeyid, {});
auto tx = builder.Build().GetTxOrThrow();
@ -393,6 +398,8 @@ TEST(TransactionBuilder, ChangeOutput)
TEST(TransactionBuilder, SetFee)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
// Generate dummy Sapling address
@ -405,7 +412,7 @@ TEST(TransactionBuilder, SetFee)
// Default fee
{
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(fvk.ovk, pa, 25000, {});
auto tx = builder.Build().GetTxOrThrow();
@ -420,7 +427,7 @@ TEST(TransactionBuilder, SetFee)
// Configured fee
{
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
builder.AddSaplingSpend(expsk, testNote.note, testNote.tree.root(), testNote.tree.witness());
builder.AddSaplingOutput(fvk.ovk, pa, 25000, {});
builder.SetFee(20000);
@ -449,7 +456,7 @@ TEST(TransactionBuilder, CheckSaplingTxVersion)
auto pk = sk.default_address();
// Cannot add Sapling outputs to a non-Sapling transaction
auto builder = TransactionBuilder(consensusParams, 1);
auto builder = TransactionBuilder(consensusParams, 1, std::nullopt);
try {
builder.AddSaplingOutput(uint256(), pk, 12345, {});
} catch (std::runtime_error const & err) {

View File

@ -0,0 +1,73 @@
// Copyright (c) 2022 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_GTEST_TEST_TRANSACTION_BUILDER_H
#define ZCASH_GTEST_TEST_TRANSACTION_BUILDER_H
#include "coins.h"
// Fake an empty view
class TransactionBuilderCoinsViewDB : public CCoinsView {
public:
std::map<uint256, SproutMerkleTree> sproutTrees;
TransactionBuilderCoinsViewDB() {}
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
auto it = sproutTrees.find(rt);
if (it != sproutTrees.end()) {
tree = it->second;
return true;
} else {
return false;
}
}
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
return false;
}
bool GetOrchardAnchorAt(const uint256 &rt, OrchardMerkleFrontier &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;
}
};
#endif

View File

@ -173,7 +173,7 @@ TEST(Validation, ContextualCheckInputsDetectsOldBranchId) {
// Create a transparent transaction that spends the coin, targeting
// a height during the Overwinter epoch.
auto builder = TransactionBuilder(consensusParams, 15, &keystore);
auto builder = TransactionBuilder(consensusParams, 15, std::nullopt, &keystore);
builder.AddTransparentInput(utxo, scriptPubKey, coinValue);
builder.AddTransparentOutput(destination, 40000);
auto tx = builder.Build().GetTxOrThrow();

View File

@ -305,7 +305,7 @@ bool static Bind(const CService &addr, unsigned int flags) {
void OnRPCStopped()
{
cvBlockChange.notify_all();
g_best_block_cv.notify_all();
LogPrint("rpc", "RPC stopped.\n");
}
@ -341,7 +341,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-daemon", _("Run in the background as a daemon and accept commands"));
#endif
}
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory (this path cannot use '~')"));
strUsage += HelpMessageOpt("-paramsdir=<dir>", _("Specify Zcash network parameters directory"));
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
strUsage += HelpMessageOpt("-debuglogfile=<file>", strprintf(_("Specify location of debug log file: this can be an absolute path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE));
@ -1105,7 +1105,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifdef ENABLE_MINING
if (mapArgs.count("-mineraddress")) {
auto addr = keyIO.DecodePaymentAddress(mapArgs["-mineraddress"]);
if (!(addr.has_value() && std::visit(ExtractMinerAddress(), addr.value()).has_value())) {
if (!(addr.has_value() && std::visit(ExtractMinerAddress(chainparams.GetConsensus(), 0), addr.value()).has_value())) {
return InitError(strprintf(
_("Invalid address for -mineraddress=<addr>: '%s' (must be a Sapling or transparent P2PKH address)"),
mapArgs["-mineraddress"]));
@ -1683,7 +1683,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!zaddr.has_value()) {
return InitError(_("-mineraddress is not a valid " PACKAGE_NAME " address."));
}
auto ztxoSelector = pwalletMain->ZTXOSelectorForAddress(zaddr.value(), true);
auto ztxoSelector = pwalletMain->ZTXOSelectorForAddress(zaddr.value(), true, false);
minerAddressInLocalWallet = ztxoSelector.has_value();
}
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {

View File

@ -290,7 +290,7 @@ std::optional<CExtKey> CExtKey::Derive(unsigned int _nChild) const {
}
}
CExtKey CExtKey::Master(const unsigned char *seed, unsigned int nSeedLen) {
std::optional<CExtKey> CExtKey::Master(const unsigned char *seed, unsigned int nSeedLen) {
CExtKey xk;
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
@ -301,7 +301,11 @@ CExtKey CExtKey::Master(const unsigned char *seed, unsigned int nSeedLen) {
xk.nChild = 0;
memset(xk.vchFingerprint, 0, sizeof(xk.vchFingerprint));
return xk;
if (xk.key.IsValid()) {
return xk;
} else {
return std::nullopt;
}
}
CExtPubKey CExtKey::Neuter() const {

View File

@ -166,7 +166,7 @@ struct CExtKey {
a.key == b.key;
}
static CExtKey Master(const unsigned char* seed, unsigned int nSeedLen);
static std::optional<CExtKey> Master(const unsigned char* seed, unsigned int nSeedLen);
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);

View File

@ -56,6 +56,7 @@ class DataLenForReceiver {
public:
DataLenForReceiver() {}
size_t operator()(const libzcash::OrchardRawAddress &zaddr) const { return 43; }
size_t operator()(const libzcash::SaplingPaymentAddress &zaddr) const { return 43; }
size_t operator()(const CScriptID &p2sh) const { return 20; }
size_t operator()(const CKeyID &p2pkh) const { return 20; }
@ -76,6 +77,13 @@ class CopyDataForReceiver {
public:
CopyDataForReceiver(unsigned char* data, size_t length) : data(data), length(length) {}
void operator()(const libzcash::OrchardRawAddress &zaddr) const {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zaddr;
assert(length == ss.size());
memcpy(data, ss.data(), ss.size());
}
void operator()(const libzcash::SaplingPaymentAddress &zaddr) const {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << zaddr;
@ -247,7 +255,7 @@ const size_t ConvertedSaplingExtendedFullViewingKeySize = (ZIP32_XFVK_SIZE * 8 +
const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5;
} // namespace
CTxDestination KeyIO::DecodeDestination(const std::string& str)
CTxDestination KeyIO::DecodeDestination(const std::string& str) const
{
std::vector<unsigned char> data;
uint160 hash;
@ -271,7 +279,7 @@ CTxDestination KeyIO::DecodeDestination(const std::string& str)
return CNoDestination();
};
CKey KeyIO::DecodeSecret(const std::string& str)
CKey KeyIO::DecodeSecret(const std::string& str) const
{
CKey key;
std::vector<unsigned char> data;
@ -287,7 +295,7 @@ CKey KeyIO::DecodeSecret(const std::string& str)
return key;
}
std::string KeyIO::EncodeSecret(const CKey& key)
std::string KeyIO::EncodeSecret(const CKey& key) const
{
assert(key.IsValid());
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::SECRET_KEY);
@ -300,7 +308,7 @@ std::string KeyIO::EncodeSecret(const CKey& key)
return ret;
}
CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str)
CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str) const
{
CExtPubKey key;
std::vector<unsigned char> data;
@ -313,7 +321,7 @@ CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str)
return key;
}
std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key)
std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key) const
{
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::EXT_PUBLIC_KEY);
size_t size = data.size();
@ -323,7 +331,7 @@ std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key)
return ret;
}
CExtKey KeyIO::DecodeExtKey(const std::string& str)
CExtKey KeyIO::DecodeExtKey(const std::string& str) const
{
CExtKey key;
std::vector<unsigned char> data;
@ -336,7 +344,7 @@ CExtKey KeyIO::DecodeExtKey(const std::string& str)
return key;
}
std::string KeyIO::EncodeExtKey(const CExtKey& key)
std::string KeyIO::EncodeExtKey(const CExtKey& key) const
{
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::EXT_SECRET_KEY);
size_t size = data.size();
@ -347,17 +355,17 @@ std::string KeyIO::EncodeExtKey(const CExtKey& key)
return ret;
}
std::string KeyIO::EncodeDestination(const CTxDestination& dest)
std::string KeyIO::EncodeDestination(const CTxDestination& dest) const
{
return std::visit(DestinationEncoder(keyConstants), dest);
}
bool KeyIO::IsValidDestinationString(const std::string& str)
bool KeyIO::IsValidDestinationString(const std::string& str) const
{
return IsValidDestination(DecodeDestination(str));
}
std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) const
{
return std::visit(PaymentAddressEncoder(keyConstants), zaddr);
}
@ -433,71 +441,12 @@ std::optional<T1> DecodeAny(
return std::nullopt;
}
/**
* `raw` MUST be 43 bytes.
*/
static bool AddSaplingReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 43),
SER_NETWORK,
PROTOCOL_VERSION);
libzcash::SaplingPaymentAddress receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
/**
* `raw` MUST be 20 bytes.
*/
static bool AddP2SHReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 20),
SER_NETWORK,
PROTOCOL_VERSION);
CScriptID receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
/**
* `raw` MUST be 20 bytes.
*/
static bool AddP2PKHReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 20),
SER_NETWORK,
PROTOCOL_VERSION);
CKeyID receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char* data, size_t len)
{
libzcash::UnknownReceiver receiver(typecode, std::vector(data, data + len));
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::string& str)
std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::string& str) const
{
// Try parsing as a Unified Address.
libzcash::UnifiedAddress ua;
if (zcash_address_parse_unified(
str.c_str(),
keyConstants.NetworkIDString().c_str(),
&ua,
AddSaplingReceiver,
AddP2SHReceiver,
AddP2PKHReceiver,
AddUnknownReceiver)
) {
return ua;
auto ua = libzcash::UnifiedAddress::Parse(keyConstants, str);
if (ua.has_value()) {
return ua.value();
}
// Try parsing as a Sapling address
@ -535,16 +484,17 @@ std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::s
}, DecodeDestination(str));
}
bool KeyIO::IsValidPaymentAddressString(const std::string& str) {
bool KeyIO::IsValidPaymentAddressString(const std::string& str) const
{
return DecodePaymentAddress(str).has_value();
}
std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk)
std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk) const
{
return std::visit(ViewingKeyEncoder(keyConstants), vk);
}
std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& str)
std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& str) const
{
// Try parsing as a Unified full viewing key
auto ufvk = libzcash::UnifiedFullViewingKey::Decode(str, keyConstants);
@ -563,12 +513,12 @@ std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& s
);
}
std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey)
std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey) const
{
return std::visit(SpendingKeyEncoder(keyConstants), zkey);
}
std::optional<libzcash::SpendingKey> KeyIO::DecodeSpendingKey(const std::string& str)
std::optional<libzcash::SpendingKey> KeyIO::DecodeSpendingKey(const std::string& str) const
{
return DecodeAny<libzcash::SpendingKey,

View File

@ -23,28 +23,28 @@ private:
public:
KeyIO(const KeyConstants& keyConstants): keyConstants(keyConstants) { }
CKey DecodeSecret(const std::string& str);
std::string EncodeSecret(const CKey& key);
CKey DecodeSecret(const std::string& str) const;
std::string EncodeSecret(const CKey& key) const;
CExtKey DecodeExtKey(const std::string& str);
std::string EncodeExtKey(const CExtKey& extkey);
CExtPubKey DecodeExtPubKey(const std::string& str);
std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
CExtKey DecodeExtKey(const std::string& str) const;
std::string EncodeExtKey(const CExtKey& extkey) const;
CExtPubKey DecodeExtPubKey(const std::string& str) const;
std::string EncodeExtPubKey(const CExtPubKey& extpubkey) const;
std::string EncodeDestination(const CTxDestination& dest);
CTxDestination DecodeDestination(const std::string& str);
std::string EncodeDestination(const CTxDestination& dest) const;
CTxDestination DecodeDestination(const std::string& str) const;
bool IsValidDestinationString(const std::string& str);
bool IsValidDestinationString(const std::string& str) const;
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr);
std::optional<libzcash::PaymentAddress> DecodePaymentAddress(const std::string& str);
bool IsValidPaymentAddressString(const std::string& str);
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) const;
std::optional<libzcash::PaymentAddress> DecodePaymentAddress(const std::string& str) const;
bool IsValidPaymentAddressString(const std::string& str) const;
std::string EncodeViewingKey(const libzcash::ViewingKey& vk);
std::optional<libzcash::ViewingKey> DecodeViewingKey(const std::string& str);
std::string EncodeViewingKey(const libzcash::ViewingKey& vk) const;
std::optional<libzcash::ViewingKey> DecodeViewingKey(const std::string& str) const;
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey);
std::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str);
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey) const;
std::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str) const;
};
#endif // BITCOIN_KEY_IO_H

View File

@ -252,6 +252,10 @@ bool CBasicKeyStore::GetSproutViewingKey(
return false;
}
//
// Sapling Keys
//
bool CBasicKeyStore::GetSaplingFullViewingKey(
const libzcash::SaplingIncomingViewingKey &ivk,
libzcash::SaplingExtendedFullViewingKey &extfvkOut) const
@ -308,14 +312,26 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey(
{
LOCK(cs_KeyStore);
auto ufvkId = ufvk.GetKeyID();
// Add the Orchard component of the UFVK to the wallet.
auto orchardKey = ufvk.GetOrchardKey();
if (orchardKey.has_value()) {
auto ivk = orchardKey.value().ToIncomingViewingKey();
mapOrchardKeyUnified.insert(std::make_pair(ivk, ufvkId));
auto ivkInternal = orchardKey.value().ToInternalIncomingViewingKey();
mapOrchardKeyUnified.insert(std::make_pair(ivkInternal, ufvkId));
}
// Add the Sapling component of the UFVK to the wallet.
auto saplingKey = ufvk.GetSaplingKey();
if (saplingKey.has_value()) {
auto ivk = saplingKey.value().ToIncomingViewingKey();
mapSaplingKeyUnified.insert(std::make_pair(ivk, ufvk.GetKeyID()));
mapSaplingKeyUnified.insert(std::make_pair(ivk, ufvkId));
auto changeIvk = saplingKey.value().GetChangeIVK();
mapSaplingKeyUnified.insert(std::make_pair(changeIvk, ufvk.GetKeyID()));
mapSaplingKeyUnified.insert(std::make_pair(changeIvk, ufvkId));
}
// We can't reasonably add the transparent component here, because
@ -325,7 +341,7 @@ bool CBasicKeyStore::AddUnifiedFullViewingKey(
// transparent part of the address must be added to the keystore.
// Add the UFVK by key identifier.
mapUnifiedFullViewingKeys.insert({ufvk.GetKeyID(), ufvk});
mapUnifiedFullViewingKeys.insert({ufvkId, ufvk});
return true;
}
@ -367,12 +383,55 @@ std::optional<libzcash::ZcashdUnifiedFullViewingKey> CBasicKeyStore::GetUnifiedF
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
std::optional<AddressUFVKMetadata>
CBasicKeyStore::GetUFVKMetadataForReceiver(const libzcash::Receiver& receiver) const
{
return std::visit(FindUFVKId(*this), receiver);
}
std::optional<AddressUFVKMetadata>
CBasicKeyStore::GetUFVKMetadataForAddress(const libzcash::UnifiedAddress& addr) const
{
std::optional<libzcash::UFVKId> ufvkId;
std::optional<libzcash::diversifier_index_t> j;
bool jConflict = false;
for (const auto& receiver : addr) {
auto rmeta = GetUFVKMetadataForReceiver(receiver);
if (rmeta.has_value()) {
// We should never generate unified addresses with internal receivers
assert(rmeta.value().IsExternalAddress());
if (ufvkId.has_value()) {
// If the unified address contains receivers that are associated with
// different UFVKs, we cannot return a singular value.
if (rmeta.value().GetUFVKId() != ufvkId.value()) {
return std::nullopt;
}
if (rmeta.value().GetDiversifierIndex().has_value()) {
if (j.has_value()) {
if (rmeta.value().GetDiversifierIndex().value() != j.value()) {
jConflict = true;
j = std::nullopt;
}
} else if (!jConflict) {
j = rmeta.value().GetDiversifierIndex().value();
}
}
} else {
ufvkId = rmeta.value().GetUFVKId();
j = rmeta.value().GetDiversifierIndex();
}
}
}
if (ufvkId.has_value()) {
return AddressUFVKMetadata(ufvkId.value(), j, true);
} else {
return std::nullopt;
}
}
std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const libzcash::ViewingKey& vk) const
{
std::optional<libzcash::UFVKId> result;
@ -386,6 +445,15 @@ std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const lib
}
},
[&](const libzcash::UnifiedFullViewingKey& ufvk) {
const auto orchardFvk = ufvk.GetOrchardKey();
if (orchardFvk.has_value()) {
const auto orchardIvk = orchardFvk.value().ToIncomingViewingKey();
const auto ufvkId = mapOrchardKeyUnified.find(orchardIvk);
if (ufvkId != mapOrchardKeyUnified.end()) {
result = ufvkId->second;
return;
}
}
const auto saplingDfvk = ufvk.GetSaplingKey();
if (saplingDfvk.has_value()) {
const auto saplingIvk = saplingDfvk.value().ToIncomingViewingKey();
@ -399,39 +467,81 @@ std::optional<libzcash::UFVKId> CBasicKeyStore::GetUFVKIdForViewingKey(const lib
return result;
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
const auto saplingIvk = keystore.mapSaplingIncomingViewingKeys.find(saplingAddr);
if (saplingIvk != keystore.mapSaplingIncomingViewingKeys.end()) {
const auto ufvkId = keystore.mapSaplingKeyUnified.find(saplingIvk->second);
if (ufvkId != keystore.mapSaplingKeyUnified.end()) {
return std::make_pair(ufvkId->second, std::nullopt);
} else {
return std::nullopt;
//
// FindUFVKId :: (KeyStore, Receiver) -> std::optional<AddressUFVKMetadata>
//
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::OrchardRawAddress& orchardAddr) const {
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
auto fvk = v.GetOrchardKey();
if (fvk.has_value()) {
auto d_idx = fvk.value().DecryptDiversifier(orchardAddr);
if (d_idx.has_value()) {
return AddressUFVKMetadata(k, d_idx->first, d_idx->second);
}
}
} else {
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const CScriptID& scriptId) const {
const auto metadata = keystore.mapP2SHUnified.find(scriptId);
if (metadata != keystore.mapP2SHUnified.end()) {
return metadata->second;
} else {
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const CKeyID& keyId) const {
const auto metadata = keystore.mapP2PKHUnified.find(keyId);
if (metadata != keystore.mapP2PKHUnified.end()) {
return metadata->second;
} else {
return std::nullopt;
}
}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
FindUFVKId::operator()(const libzcash::UnknownReceiver& receiver) const {
return std::nullopt;
}
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const {
const auto saplingIvk = keystore.mapSaplingIncomingViewingKeys.find(saplingAddr);
if (saplingIvk != keystore.mapSaplingIncomingViewingKeys.end()) {
// We have either generated this as a receiver via `z_getaddressforaccount` or a
// legacy Sapling address via `z_getnewaddress`, or we have previously detected
// this via trial-decryption of a note.
const auto ufvkId = keystore.mapSaplingKeyUnified.find(saplingIvk->second);
if (ufvkId != keystore.mapSaplingKeyUnified.end()) {
// We know that we have a UFVK, and that it has a Sapling key that
// produced this address, so decrypt the diversifier to determine
// whether it was an internal or external address
auto ufvk = keystore.GetUnifiedFullViewingKey(ufvkId->second).value();
auto saplingKey = ufvk.GetSaplingKey().value();
auto d_idx = saplingKey.DecryptDiversifier(saplingAddr).value();
return AddressUFVKMetadata(ufvkId->second, d_idx.first, d_idx.second);
}
// If we have the addr -> ivk map entry but not the ivk -> UFVK map entry,
// then this is definitely a legacy Sapling address.
return std::nullopt;
}
// We haven't generated this receiver via `z_getaddressforaccount` (or this is a
// recovery from a backed-up mnemonic which doesn't store receiver types selected by
// users). Trial-decrypt the diversifier of the Sapling address with every UFVK in the
// wallet, to check directly if it belongs to any of them.
for (const auto& [k, v] : keystore.mapUnifiedFullViewingKeys) {
auto dfvk = v.GetSaplingKey();
if (dfvk.has_value()) {
auto d_idx = dfvk.value().DecryptDiversifier(saplingAddr);
if (d_idx.has_value()) {
return AddressUFVKMetadata(k, d_idx->first, d_idx->second);
}
}
}
// We definitely don't know of any UFVK linked to this Sapling address.
return std::nullopt;
}
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const CScriptID& scriptId) const {
const auto metadata = keystore.mapP2SHUnified.find(scriptId);
if (metadata != keystore.mapP2SHUnified.end()) {
// At present we never generate transparent internal addresses, so this
// must be an external address
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, true);
} else {
return std::nullopt;
}
}
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const CKeyID& keyId) const {
const auto metadata = keystore.mapP2PKHUnified.find(keyId);
if (metadata != keystore.mapP2PKHUnified.end()) {
// At present we never generate transparent internal addresses, so this
// must be an external address
return AddressUFVKMetadata(metadata->second.first, metadata->second.second, true);
} else {
return std::nullopt;
}
}
std::optional<AddressUFVKMetadata> FindUFVKId::operator()(const libzcash::UnknownReceiver& receiver) const {
return std::nullopt;
}

View File

@ -18,6 +18,20 @@
#include <boost/signals2/signal.hpp>
class AddressUFVKMetadata {
private:
libzcash::UFVKId ufvkId;
std::optional<libzcash::diversifier_index_t> j;
bool externalAddress;
public:
AddressUFVKMetadata(libzcash::UFVKId ufvkId, std::optional<libzcash::diversifier_index_t> j, bool externalAddress)
: ufvkId(ufvkId), j(j), externalAddress(externalAddress) {}
libzcash::UFVKId GetUFVKId() const { return ufvkId; }
std::optional<libzcash::diversifier_index_t> GetDiversifierIndex() const { return j; }
bool IsExternalAddress() const { return externalAddress; }
};
/** A virtual base class for key stores */
class CKeyStore
{
@ -125,18 +139,21 @@ public:
const libzcash::UnifiedAddress& ua) = 0;
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId
) const = 0;
const libzcash::UFVKId& keyId) const = 0;
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver
) const = 0;
virtual std::optional<AddressUFVKMetadata> GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver) const = 0;
virtual std::optional<libzcash::UFVKId>
GetUFVKIdForViewingKey(
const libzcash::ViewingKey& vk
) const = 0;
/**
* If all the receivers of the specified address correspond to a single
* UFVK, return that key's metadata. If all the receivers correspond to
* the same diversifier index, that diversifier index is also returned.
*/
virtual std::optional<AddressUFVKMetadata> GetUFVKMetadataForAddress(
const libzcash::UnifiedAddress& addr) const = 0;
virtual std::optional<libzcash::UFVKId> GetUFVKIdForViewingKey(
const libzcash::ViewingKey& vk) const = 0;
};
typedef std::map<CKeyID, CKey> KeyMap;
@ -185,6 +202,7 @@ protected:
std::map<CKeyID, std::pair<libzcash::UFVKId, libzcash::diversifier_index_t>> mapP2PKHUnified;
std::map<CScriptID, std::pair<libzcash::UFVKId, libzcash::diversifier_index_t>> mapP2SHUnified;
std::map<libzcash::SaplingIncomingViewingKey, libzcash::UFVKId> mapSaplingKeyUnified;
std::map<libzcash::OrchardIncomingViewingKey, libzcash::UFVKId> mapOrchardKeyUnified;
std::map<libzcash::UFVKId, libzcash::ZcashdUnifiedFullViewingKey> mapUnifiedFullViewingKeys;
friend class FindUFVKId;
@ -373,15 +391,34 @@ public:
virtual std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUnifiedFullViewingKey(
const libzcash::UFVKId& keyId) const;
virtual std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver
) const;
virtual std::optional<AddressUFVKMetadata> GetUFVKMetadataForReceiver(
const libzcash::Receiver& receiver) const;
virtual std::optional<libzcash::UFVKId>
GetUFVKIdForViewingKey(
const libzcash::ViewingKey& vk
) const;
std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUFVKForReceiver(
const libzcash::Receiver& receiver) const {
auto ufvkMeta = GetUFVKMetadataForReceiver(receiver);
if (ufvkMeta.has_value()) {
return GetUnifiedFullViewingKey(ufvkMeta.value().GetUFVKId());
} else {
return std::nullopt;
}
}
virtual std::optional<AddressUFVKMetadata> GetUFVKMetadataForAddress(
const libzcash::UnifiedAddress& addr) const;
std::optional<libzcash::ZcashdUnifiedFullViewingKey> GetUFVKForAddress(
const libzcash::UnifiedAddress& addr) const {
auto ufvkMeta = GetUFVKMetadataForAddress(addr);
if (ufvkMeta.has_value()) {
return GetUnifiedFullViewingKey(ufvkMeta.value().GetUFVKId());
} else {
return std::nullopt;
}
}
virtual std::optional<libzcash::UFVKId> GetUFVKIdForViewingKey(
const libzcash::ViewingKey& vk) const;
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
@ -398,14 +435,11 @@ private:
public:
FindUFVKId(const CBasicKeyStore& keystore): keystore(keystore) {}
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const CScriptID& scriptId) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const CKeyID& keyId) const;
std::optional<std::pair<libzcash::UFVKId, std::optional<libzcash::diversifier_index_t>>>
operator()(const libzcash::UnknownReceiver& receiver) const;
std::optional<AddressUFVKMetadata> operator()(const libzcash::OrchardRawAddress& orchardAddr) const;
std::optional<AddressUFVKMetadata> operator()(const libzcash::SaplingPaymentAddress& saplingAddr) const;
std::optional<AddressUFVKMetadata> operator()(const CScriptID& scriptId) const;
std::optional<AddressUFVKMetadata> operator()(const CKeyID& keyId) const;
std::optional<AddressUFVKMetadata> operator()(const libzcash::UnknownReceiver& receiver) const;
};
#endif // BITCOIN_KEYSTORE_H

View File

@ -66,8 +66,10 @@ BlockMap mapBlockIndex;
CChain chainActive;
CBlockIndex *pindexBestHeader = NULL;
static std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
CWaitableCriticalSection g_best_block_mutex;
CConditionVariable g_best_block_cv;
uint256 g_best_block;
int g_best_block_height;
int nScriptCheckThreads = 0;
std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
@ -2945,6 +2947,17 @@ static DisconnectResult DisconnectBlock(const CBlock& block, CValidationState& s
view.PopAnchor(SaplingMerkleTree::empty_root(), SAPLING);
}
// Set the old best Orchard anchor back. We can get this from the
// `hashFinalOrchardRoot` of the last block. However, if the last
// block was not on or after the Orchard activation height, this
// will be set to `null`. For logical consistency, in this case we
// set the last anchor to the empty root.
if (chainparams.GetConsensus().NetworkUpgradeActive(pindex->pprev->nHeight, Consensus::UPGRADE_NU5)) {
view.PopAnchor(pindex->pprev->hashFinalOrchardRoot, ORCHARD);
} else {
view.PopAnchor(OrchardMerkleFrontier::empty_root(), ORCHARD);
}
// This is guaranteed to be filled by LoadBlockIndex.
assert(pindex->nCachedBranchId);
auto consensusBranchId = pindex->nCachedBranchId.value();
@ -3172,7 +3185,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
assert(view.GetSaplingAnchorAt(view.GetBestAnchor(SAPLING), sapling_tree));
OrchardMerkleFrontier orchard_tree;
assert(view.GetOrchardAnchorAt(view.GetBestAnchor(ORCHARD), orchard_tree));
if (pindex->pprev && chainparams.GetConsensus().NetworkUpgradeActive(pindex->pprev->nHeight, Consensus::UPGRADE_NU5)) {
// Verify that the view's current state corresponds to the previous block.
assert(pindex->pprev->hashFinalOrchardRoot == view.GetBestAnchor(ORCHARD));
// We only call ConnectBlock() on top of the active chain's tip.
assert(!pindex->pprev->hashFinalOrchardRoot.IsNull());
assert(view.GetOrchardAnchorAt(pindex->pprev->hashFinalOrchardRoot, orchard_tree));
} else {
if (pindex->pprev) {
assert(pindex->pprev->hashFinalOrchardRoot.IsNull());
}
assert(view.GetOrchardAnchorAt(OrchardMerkleFrontier::empty_root(), orchard_tree));
}
// Grab the consensus branch ID for this block and its parent
auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus());
@ -3823,7 +3848,12 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
RenderPoolMetrics("sapling", saplingPool);
RenderPoolMetrics("transparent", transparentPool);
cvBlockChange.notify_all();
{
boost::unique_lock<boost::mutex> lock(g_best_block_mutex);
g_best_block = pindexNew->GetBlockHash();
g_best_block_height = pindexNew->nHeight;
g_best_block_cv.notify_all();
}
}
/**
@ -3841,6 +3871,7 @@ bool static DisconnectTip(CValidationState &state, const CChainParams& chainpara
// Apply the block atomically to the chain state.
uint256 sproutAnchorBeforeDisconnect = pcoinsTip->GetBestAnchor(SPROUT);
uint256 saplingAnchorBeforeDisconnect = pcoinsTip->GetBestAnchor(SAPLING);
uint256 orchardAnchorBeforeDisconnect = pcoinsTip->GetBestAnchor(ORCHARD);
int64_t nStart = GetTimeMicros();
{
CCoinsViewCache view(pcoinsTip);
@ -3852,6 +3883,7 @@ bool static DisconnectTip(CValidationState &state, const CChainParams& chainpara
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
uint256 sproutAnchorAfterDisconnect = pcoinsTip->GetBestAnchor(SPROUT);
uint256 saplingAnchorAfterDisconnect = pcoinsTip->GetBestAnchor(SAPLING);
uint256 orchardAnchorAfterDisconnect = pcoinsTip->GetBestAnchor(ORCHARD);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED))
return false;
@ -3875,6 +3907,11 @@ bool static DisconnectTip(CValidationState &state, const CChainParams& chainpara
// in which case we don't want to evict from the mempool yet!
mempool.removeWithAnchor(saplingAnchorBeforeDisconnect, SAPLING);
}
if (orchardAnchorBeforeDisconnect != orchardAnchorAfterDisconnect) {
// The anchor may not change between block disconnects,
// in which case we don't want to evict from the mempool yet!
mempool.removeWithAnchor(orchardAnchorBeforeDisconnect, ORCHARD);
}
}
// Update chainActive and related variables.

View File

@ -160,8 +160,19 @@ extern BlockMap mapBlockIndex;
extern std::optional<uint64_t> last_block_num_txs;
extern std::optional<uint64_t> last_block_size;
extern const std::string strMessageMagic;
extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange;
//! These four variables are used to notify getblocktemplate RPC of new tips.
//! When UpdateTip() establishes a new tip (best block), it must awaken a
//! waiting getblocktemplate RPC (if there is one) immediately. But upon waking
//! up, getblocktemplate cannot call chainActive->Tip() because it does not
//! (and cannot) hold cs_main. So the g_best_block_height and g_best_block variables
//! (protected by g_best_block_mutex) provide the needed height and block
//! hash respectively to getblocktemplate without it requiring cs_main.
extern CWaitableCriticalSection g_best_block_mutex;
extern CConditionVariable g_best_block_cv;
extern int g_best_block_height;
extern uint256 g_best_block;
extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex;
extern int nScriptCheckThreads;

View File

@ -50,6 +50,17 @@ void AtomicTimer::stop()
}
}
void AtomicTimer::zeroize()
{
std::unique_lock<std::mutex> lock(mtx);
// only zeroize it if there's no more threads (same semantics as start())
if (threads < 1) {
start_time = 0;
total_time = 0;
}
}
bool AtomicTimer::running()
{
std::unique_lock<std::mutex> lock(mtx);

View File

@ -52,6 +52,8 @@ public:
*/
void stop();
void zeroize();
bool running();
uint64_t threadCount();

View File

@ -214,37 +214,87 @@ public:
return miner_reward + nFees;
}
void ComputeBindingSig(void* ctx) const {
void ComputeBindingSig(void* saplingCtx, std::optional<orchard::UnauthorizedBundle> orchardBundle) const {
// Empty output script.
uint256 dataToBeSigned;
CScript scriptCode;
try {
// This is a shielded coinbase transaction, so the sighash is either pre-v5
// and doesn't use the allPrevOutputs field of PrecomputedTransactionData), or
// v5+ and S.2 of ZIP 244 defers to T.2, causing allPrevOutputs to be ignored.
// We therefore can set it to the empty list here.
PrecomputedTransactionData txdata(mtx, {});
dataToBeSigned = SignatureHash(
scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0,
CurrentEpochBranchId(nHeight, chainparams.GetConsensus()),
txdata);
if (orchardBundle.has_value()) {
// Orchard is only usable with v5+ transactions.
dataToBeSigned = ProduceZip244SignatureHash(mtx, {}, orchardBundle.value());
} else {
CScript scriptCode;
PrecomputedTransactionData txdata(mtx, {});
dataToBeSigned = SignatureHash(
scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0,
CurrentEpochBranchId(nHeight, chainparams.GetConsensus()),
txdata);
}
} catch (std::logic_error ex) {
librustzcash_sapling_proving_ctx_free(ctx);
librustzcash_sapling_proving_ctx_free(saplingCtx);
throw ex;
}
if (orchardBundle.has_value()) {
auto authorizedBundle = orchardBundle.value().ProveAndSign({}, dataToBeSigned);
if (authorizedBundle.has_value()) {
mtx.orchardBundle = authorizedBundle.value();
} else {
librustzcash_sapling_proving_ctx_free(saplingCtx);
throw new std::runtime_error("Failed to create Orchard proof or signatures");
}
}
bool success = librustzcash_sapling_binding_sig(
ctx,
saplingCtx,
mtx.valueBalanceSapling,
dataToBeSigned.begin(),
mtx.bindingSig.data());
if (!success) {
librustzcash_sapling_proving_ctx_free(ctx);
librustzcash_sapling_proving_ctx_free(saplingCtx);
throw new std::runtime_error("An error occurred computing the binding signature.");
}
}
// Create Orchard output
void operator()(const libzcash::OrchardRawAddress &to) const {
auto ctx = librustzcash_sapling_proving_ctx_init();
// `enableSpends` must be set to `false` for coinbase transactions. This
// means the Orchard anchor is unconstrained, so we set it to the empty
// tree root via a null (all zeroes) uint256.
uint256 orchardAnchor;
auto builder = orchard::Builder(false, true, orchardAnchor);
// Shielded coinbase outputs must be recoverable with an all-zeroes ovk.
uint256 ovk;
auto miner_reward = SetFoundersRewardAndGetMinerValue(ctx);
builder.AddOutput(ovk, to, miner_reward, std::nullopt);
// orchard::Builder pads to two Actions, but does so using a "no OVK" policy for
// dummy outputs, which violates coinbase rules requiring all shielded outputs to
// be recoverable. We manually add a dummy output to sidestep this issue.
// TODO: If/when we have funding streams going to Orchard recipients, this dummy
// output can be removed.
RawHDSeed rawSeed(32, 0);
GetRandBytes(rawSeed.data(), 32);
auto dummyTo = libzcash::OrchardSpendingKey::ForAccount(HDSeed(rawSeed), Params().BIP44CoinType(), 0)
.ToFullViewingKey()
.ToIncomingViewingKey()
.Address(0);
builder.AddOutput(ovk, dummyTo, 0, std::nullopt);
auto bundle = builder.Build();
if (!bundle.has_value()) {
librustzcash_sapling_proving_ctx_free(ctx);
throw new std::runtime_error("Failed to create shielded output for miner");
}
ComputeBindingSig(ctx, std::move(bundle));
librustzcash_sapling_proving_ctx_free(ctx);
}
// Create shielded output
void operator()(const libzcash::SaplingPaymentAddress &pa) const {
auto ctx = librustzcash_sapling_proving_ctx_init();
@ -264,7 +314,7 @@ public:
}
mtx.vShieldedOutput.push_back(odesc.value());
ComputeBindingSig(ctx);
ComputeBindingSig(ctx, std::nullopt);
librustzcash_sapling_proving_ctx_free(ctx);
}
@ -283,7 +333,7 @@ public:
mtx.vout[0] = CTxOut(value, coinbaseScript->reserveScript);
if (mtx.vShieldedOutput.size() > 0) {
ComputeBindingSig(ctx);
ComputeBindingSig(ctx, std::nullopt);
}
librustzcash_sapling_proving_ctx_free(ctx);
@ -728,23 +778,38 @@ std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::Sapl
return addr;
}
std::optional<MinerAddress> ExtractMinerAddress::operator()(const libzcash::UnifiedAddress &addr) const {
for (const auto& receiver: addr) {
if (std::holds_alternative<libzcash::SaplingPaymentAddress>(receiver)) {
return std::get<libzcash::SaplingPaymentAddress>(receiver);
}
auto preferred = addr.GetPreferredRecipientAddress(consensus, height);
if (preferred.has_value()) {
std::optional<MinerAddress> ret;
std::visit(match {
[&](const libzcash::OrchardRawAddress addr) { ret = MinerAddress(addr); },
[&](const libzcash::SaplingPaymentAddress addr) { ret = MinerAddress(addr); },
[&](const CKeyID keyID) { ret = operator()(keyID); },
[&](const auto other) { ret = std::nullopt; }
}, preferred.value());
return ret;
} else {
return std::nullopt;
}
return std::nullopt;
}
void GetMinerAddress(MinerAddress &minerAddress)
void GetMinerAddress(std::optional<MinerAddress> &minerAddress)
{
KeyIO keyIO(Params());
// If the user sets a UA miner address with an Orchard component, we want to ensure we
// start using it once we reach that height.
int height;
{
LOCK(cs_main);
height = chainActive.Height() + 1;
}
auto mAddrArg = GetArg("-mineraddress", "");
auto zaddr0 = keyIO.DecodePaymentAddress(mAddrArg);
if (zaddr0.has_value()) {
auto zaddr = std::visit(ExtractMinerAddress(), zaddr0.value());
auto zaddr = std::visit(ExtractMinerAddress(Params().GetConsensus(), height), zaddr0.value());
if (zaddr.has_value()) {
minerAddress = zaddr.value();
}
@ -815,8 +880,8 @@ void static BitcoinMiner(const CChainParams& chainparams)
// Each thread has its own counter
unsigned int nExtraNonce = 0;
MinerAddress minerAddress;
GetMainSignals().AddressForMining(minerAddress);
std::optional<MinerAddress> maybeMinerAddress;
GetMainSignals().AddressForMining(maybeMinerAddress);
unsigned int n = chainparams.GetConsensus().nEquihashN;
unsigned int k = chainparams.GetConsensus().nEquihashK;
@ -837,9 +902,10 @@ void static BitcoinMiner(const CChainParams& chainparams)
try {
// Throw an error if no address valid for mining was provided.
if (!std::visit(IsValidMinerAddress(), minerAddress)) {
if (!(maybeMinerAddress.has_value() && std::visit(IsValidMinerAddress(), maybeMinerAddress.value()))) {
throw std::runtime_error("No miner address available (mining requires a wallet or -mineraddress)");
}
auto minerAddress = maybeMinerAddress.value();
while (true) {
if (chainparams.MiningRequiresPeers()) {

View File

@ -24,13 +24,18 @@ static const int DEFAULT_GENERATE_THREADS = 1;
static const bool DEFAULT_PRINTPRIORITY = false;
typedef std::variant<
libzcash::OrchardRawAddress,
libzcash::SaplingPaymentAddress,
boost::shared_ptr<CReserveScript>> MinerAddress;
class ExtractMinerAddress
{
const Consensus::Params& consensus;
int height;
public:
ExtractMinerAddress() {}
ExtractMinerAddress(const Consensus::Params& consensus, int height) :
consensus(consensus), height(height) {}
std::optional<MinerAddress> operator()(const CKeyID &keyID) const;
std::optional<MinerAddress> operator()(const CScriptID &addr) const;
@ -44,6 +49,7 @@ class KeepMinerAddress
public:
KeepMinerAddress() {}
void operator()(const libzcash::OrchardRawAddress &addr) const {}
void operator()(const libzcash::SaplingPaymentAddress &pa) const {}
void operator()(const boost::shared_ptr<CReserveScript> &coinbaseScript) const {
coinbaseScript->KeepScript();
@ -57,6 +63,9 @@ class IsValidMinerAddress
public:
IsValidMinerAddress() {}
bool operator()(const libzcash::OrchardRawAddress &addr) const {
return true;
}
bool operator()(const libzcash::SaplingPaymentAddress &pa) const {
return true;
}
@ -89,7 +98,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre
#ifdef ENABLE_MINING
/** Get -mineraddress */
void GetMinerAddress(MinerAddress &minerAddress);
void GetMinerAddress(std::optional<MinerAddress> &minerAddress);
/** Modify the extranonce in a block */
void IncrementExtraNonce(
CBlockTemplate* pblocktemplate,

View File

@ -9,11 +9,15 @@
#include <amount.h>
#include <rust/orchard.h>
#include <rust/orchard/wallet.h>
#include "zcash/address/orchard.hpp"
class OrchardMerkleFrontier;
class OrchardWallet;
namespace orchard { class UnauthorizedBundle; }
/**
* The Orchard component of a transaction.
* The Orchard component of an authorized transaction.
*/
class OrchardBundle
{
@ -22,7 +26,11 @@ private:
/// Memory is allocated by Rust.
std::unique_ptr<OrchardBundlePtr, decltype(&orchard_bundle_free)> inner;
OrchardBundle(OrchardBundlePtr* bundle) : inner(bundle, orchard_bundle_free) {}
friend class OrchardMerkleFrontier;
friend class OrchardWallet;
friend class orchard::UnauthorizedBundle;
public:
OrchardBundle() : inner(nullptr, orchard_bundle_free) {}

View File

@ -81,6 +81,11 @@ std::string SaplingOutPoint::ToString() const
return strprintf("SaplingOutPoint(%s, %u)", hash.ToString().substr(0, 10), n);
}
std::string OrchardOutPoint::ToString() const
{
return strprintf("OrchardOutPoint(%s, %u)", hash.ToString().substr(0, 10), n);
}
CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn)
{
prevout = prevoutIn;

View File

@ -523,6 +523,16 @@ public:
std::string ToString() const;
};
/** An outpoint - a combination of a txid and an index n into its orchard
* actions */
class OrchardOutPoint : public BaseOutPoint
{
public:
OrchardOutPoint() : BaseOutPoint() {};
OrchardOutPoint(uint256 hashIn, uint32_t nIn) : BaseOutPoint(hashIn, nIn) {};
std::string ToString() const;
};
/** An input of a transaction. It contains the location of the previous
* transaction's output that it claims and a signature that matches the
* output's public key.

View File

@ -1247,6 +1247,13 @@ UniValue z_gettreestate(const UniValue& params, bool fHelp)
" \"finalRoot\": \"hex\", (string)\n"
" \"finalState\": \"hex\" (string)\n"
" }\n"
" },\n"
" \"orchard\": {\n"
" \"skipHash\": \"hash\", (string) hash of most recent block with more information\n"
" \"commitments\": {\n"
" \"finalRoot\": \"hex\", (string)\n"
" \"finalState\": \"hex\" (string)\n"
" }\n"
" }\n"
"}\n"
"\nExamples:\n"
@ -1327,6 +1334,31 @@ UniValue z_gettreestate(const UniValue& params, bool fHelp)
res.pushKV("sapling", sapling_result);
}
// orchard
{
UniValue orchard_result(UniValue::VOBJ);
UniValue orchard_commitments(UniValue::VOBJ);
orchard_commitments.pushKV("finalRoot", pindex->hashFinalOrchardRoot.GetHex());
bool need_skiphash = false;
OrchardMerkleFrontier tree;
if (pcoinsTip->GetOrchardAnchorAt(pindex->hashFinalOrchardRoot, tree)) {
CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
s << tree;
orchard_commitments.pushKV("finalState", HexStr(s.begin(), s.end()));
} else {
// Set skipHash to the most recent block that has a finalState.
const CBlockIndex* pindex_skip = pindex->pprev;
while (pindex_skip && !pcoinsTip->GetOrchardAnchorAt(pindex_skip->hashFinalOrchardRoot, tree)) {
pindex_skip = pindex_skip->pprev;
}
if (pindex_skip) {
orchard_result.pushKV("skipHash", pindex_skip->GetBlockHash().GetHex());
}
}
orchard_result.pushKV("commitments", orchard_commitments);
res.pushKV("orchard", orchard_result);
}
return res;
}

View File

@ -134,7 +134,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "z_sendmany", 1},
{ "z_sendmany", 2},
{ "z_sendmany", 3},
{ "z_sendmany", 4},
{ "z_shieldcoinbase", 2},
{ "z_shieldcoinbase", 3},
{ "z_getoperationstatus", 0},

View File

@ -186,19 +186,25 @@ UniValue generate(const UniValue& params, bool fHelp)
int nHeight = 0;
int nGenerate = params[0].get_int();
MinerAddress minerAddress;
GetMainSignals().AddressForMining(minerAddress);
// If the keypool is exhausted, no script is returned at all. Catch this.
auto resv = std::get_if<boost::shared_ptr<CReserveScript>>(&minerAddress);
if (resv && !resv->get()) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
std::optional<MinerAddress> maybeMinerAddress;
GetMainSignals().AddressForMining(maybeMinerAddress);
// Throw an error if no address valid for mining was provided.
if (!std::visit(IsValidMinerAddress(), minerAddress)) {
if (!maybeMinerAddress.has_value()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "No miner address available (mining requires a wallet or -mineraddress)");
} else {
// Detect and handle keypool exhaustion separately from IsValidMinerAddress().
auto resv = std::get_if<boost::shared_ptr<CReserveScript>>(&maybeMinerAddress.value());
if (resv && !resv->get()) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
// Catch any other invalid miner address issues.
if (!std::visit(IsValidMinerAddress(), maybeMinerAddress.value())) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Miner address is invalid");
}
}
auto minerAddress = maybeMinerAddress.value();
{ // Don't keep cs_main locked
LOCK(cs_main);
@ -569,9 +575,14 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
if (IsInitialBlockDownload(Params().GetConsensus()))
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Zcash is downloading blocks...");
std::optional<MinerAddress> maybeMinerAddress;
GetMainSignals().AddressForMining(maybeMinerAddress);
MinerAddress minerAddress;
GetMainSignals().AddressForMining(minerAddress);
// Throw an error if no address valid for mining was provided.
if (!(maybeMinerAddress.has_value() && std::visit(IsValidMinerAddress(), maybeMinerAddress.value()))) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "No miner address available (getblocktemplate requires a wallet or -mineraddress)");
}
auto minerAddress = maybeMinerAddress.value();
static unsigned int nTransactionsUpdatedLast;
static std::optional<CMutableTransaction> cached_next_cb_mtx;
@ -607,15 +618,15 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
// Release the main lock while waiting
// Don't call chainActive->Tip() without holding cs_main
LEAVE_CRITICAL_SECTION(cs_main);
{
checktxtime = boost::get_system_time() + boost::posix_time::seconds(10);
boost::unique_lock<boost::mutex> lock(csBestBlock);
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning())
boost::unique_lock<boost::mutex> lock(g_best_block_mutex);
while (g_best_block == hashWatchedChain && IsRPCRunning())
{
// Release the main lock while waiting
LEAVE_CRITICAL_SECTION(cs_main);
// Before waiting, generate the coinbase for the block following the next
// block (since this is cpu-intensive), so that when next block arrives,
// we can quickly respond with a template for following block.
@ -628,12 +639,11 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
Params(), CAmount{0}, minerAddress, cached_next_cb_height);
next_cb_mtx = cached_next_cb_mtx;
}
bool timedout = !cvBlockChange.timed_wait(lock, checktxtime);
ENTER_CRITICAL_SECTION(cs_main);
bool timedout = !g_best_block_cv.timed_wait(lock, checktxtime);
// Optimization: even if timed out, a new block may have arrived
// while waiting for cs_main; if so, don't discard next_cb_mtx.
if (chainActive.Tip()->GetBlockHash() != hashWatchedChain) break;
if (g_best_block != hashWatchedChain) break;
// Timeout: Check transactions for update
if (timedout && mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) {
@ -643,11 +653,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
}
checktxtime += boost::posix_time::seconds(10);
}
if (chainActive.Tip()->nHeight != nHeight + 1) {
if (g_best_block_height != nHeight + 1) {
// Unexpected height (reorg or >1 blocks arrived while waiting) invalidates coinbase tx.
next_cb_mtx = nullopt;
}
}
ENTER_CRITICAL_SECTION(cs_main);
if (!IsRPCRunning())
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");

View File

@ -431,8 +431,7 @@ void JSONRequest::parse(const UniValue& valRequest)
if (!valMethod.isStr())
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
if (strMethod != "getblocktemplate")
LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
// Parse params
UniValue valParams = find_value(request, "params");

View File

@ -6,6 +6,11 @@ the `zcashd` full node.
The FFI API does not have any stability guarantees, and will change as required
by `zcashd`.
# zcashd-wallet-tool
`zcashd-wallet-tool` is a command-line tool that allows confirming to a `zcashd`
node that the emergency recovery phrase of the node's wallet has been backed up.
## License
Licensed under either of

646
src/rust/bin/wallet_tool.rs Normal file
View File

@ -0,0 +1,646 @@
use std::cmp::min;
use std::env::{self, consts::EXE_EXTENSION};
use std::ffi::OsStr;
use std::fs::File;
use std::io::{self, BufRead, Stdin, Write};
use std::iter;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{self, ChildStdin, Command, Output, Stdio};
use std::str::from_utf8;
use std::time::SystemTime;
use anyhow::{self, Context};
use backtrace::Backtrace;
use gumdrop::{Options, ParsingStyle};
use rand::{thread_rng, Rng};
use secrecy::{ExposeSecret, SecretString};
use thiserror::Error;
use time::macros::format_description;
use time::OffsetDateTime;
use tracing::debug;
use tracing_subscriber::{fmt, EnvFilter};
#[derive(Debug, Options)]
struct CliOptions {
#[options(no_short, help = "Print this help output")]
help: bool,
#[options(
no_short,
help = "Specify configuration filename, relative to the data directory (default: zcash.conf)",
meta = "FILENAME"
)]
conf: Option<String>,
#[options(
no_short,
help = "Specify data directory (this path cannot use '~')",
meta = "PATH"
)]
datadir: Option<String>,
#[options(no_short, help = "Use the test chain")]
testnet: bool,
#[options(
no_short,
help = "Send commands to node running on IPADDR (default: 127.0.0.1)",
meta = "IPADDR"
)]
rpcconnect: Option<String>,
#[options(
no_short,
help = "Connect to JSON-RPC on PORT (default: 8232 or testnet: 18232)",
meta = "PORT"
)]
rpcport: Option<u16>,
#[options(
no_short,
help = "Username for JSON-RPC connections",
meta = "USERNAME"
)]
rpcuser: Option<String>,
#[options(
no_short,
help = "Password for JSON-RPC connections",
meta = "PASSWORD"
)]
rpcpassword: Option<String>,
#[options(
no_short,
help = "Timeout in seconds during HTTP requests, or 0 for no timeout. (default: 900)",
meta = "SECONDS"
)]
rpcclienttimeout: Option<u32>,
}
impl CliOptions {
fn to_zcash_cli_options(&self) -> Vec<String> {
iter::empty::<String>()
.chain(self.conf.as_ref().map(|o| format!("-conf={}", o)))
.chain(self.datadir.as_ref().map(|o| format!("-datadir={}", o)))
.chain(if self.testnet {
Some("-testnet".into())
} else {
None
})
.chain(
self.rpcconnect
.as_ref()
.map(|o| format!("-rpcconnect={}", o)),
)
.chain(self.rpcport.as_ref().map(|o| format!("-rpcport={}", o)))
.chain(self.rpcuser.as_ref().map(|o| format!("-rpcuser={}", o)))
.chain(
self.rpcpassword
.as_ref()
.map(|o| format!("-rpcpassword={}", o)),
)
.chain(
self.rpcclienttimeout
.as_ref()
.map(|o| format!("-rpcclienttimeout={}", o)),
)
.collect()
}
}
#[derive(Debug, Error)]
enum WalletToolError {
#[error("zcash-cli executable not found")]
ZcashCliNotFound,
#[error("Unexpected response from zcash-cli or zcashd")]
UnexpectedResponse,
#[error("Could not connect to zcashd")]
ZcashdConnection,
#[error("zcashd -exportdir option not set")]
ExportDirNotSet,
#[error("Could not parse a recovery phrase from the export file")]
RecoveryPhraseNotFound,
#[error("Unexpected EOF in input")]
UnexpectedEof,
}
pub fn main() {
// Log to stderr, configured by the RUST_LOG environment variable.
fmt()
.with_writer(io::stderr)
.with_env_filter(EnvFilter::from_default_env())
.init();
// Allow either Bitcoin-style or GNU-style arguments.
let mut args = env::args();
let command = args.next().expect("argv[0] should exist");
let args: Vec<_> = args
.map(|s| {
if s.starts_with('-') && !s.starts_with("--") {
format!("-{}", s)
} else {
s
}
})
.collect();
const USAGE_NOTE: &str = concat!(
"Options can be given in GNU style (`--conf=CONF` or `--conf CONF`),\n",
"or in Bitcoin style with a single hyphen (`-conf=CONF`).\n\n",
"The environment variable RUST_LOG controls debug output, e.g.\n",
"`RUST_LOG=debug`.\n",
);
let opts = CliOptions::parse_args(&args, ParsingStyle::default()).unwrap_or_else(|e| {
eprintln!(
"{}: {}\n\nUsage: {} [OPTIONS]\n\n{}\n\n{}",
command,
e,
command,
CliOptions::usage(),
USAGE_NOTE,
);
process::exit(2);
});
if opts.help_requested() {
println!(
"Usage: {} [OPTIONS]\n\n{}\n\n{}",
command,
opts.self_usage(),
USAGE_NOTE
);
process::exit(0);
}
if let Err(e) = run(&opts) {
eprintln!("{}: {}", command, e);
process::exit(1);
}
}
fn run(opts: &CliOptions) -> anyhow::Result<()> {
let cli_options: Vec<String> = opts.to_zcash_cli_options();
println!(concat!(
"To reduce the risk of loss of funds, we're going to confirm that the\n",
"zcashd wallet is backed up reliably.\n\n",
" 👛 ➜ 🗃️ \n"
));
println!("Checking that we can connect to zcashd...");
let zcash_cli = zcash_cli_path()?;
// Pass an invalid filename, "\x01", and use the error message to distinguish
// whether zcashd is running with the -exportdir option, running without that
// option, or not running / cannot connect.
let mut cli_args = cli_options.clone();
cli_args.extend_from_slice(&["z_exportwallet".to_string(), "\x01".to_string()]);
let out = exec(&zcash_cli, &cli_args, None)?;
let cli_err: Vec<_> = from_utf8(&out.stderr)
.with_context(|| "Output from zcash-cli was not UTF-8")?
.lines()
.map(|s| s.trim_end_matches('\r'))
.collect();
debug!("stderr {:?}", cli_err);
if !cli_err.is_empty() {
if cli_err[0].starts_with("Error reading configuration file") {
println!(
"\nNo, we could not read the zcashd configuration file, expected to be at\n{:?}.",
Path::new(opts.datadir.as_ref().map_or("~/.zcash", |s| &s[..])).join(Path::new(
opts.conf.as_ref().map_or("zcash.conf", |s| &s[..])
)),
);
println!(concat!(
"If it is not at that path, please try again with the '-datadir' and/or\n",
"'-conf' options set correctly (see '--help' for details). Also make sure\n",
"that the current user has permission to read the configuration file.\n",
));
return Err(WalletToolError::ZcashdConnection.into());
}
if cli_err[0].starts_with("error: couldn't connect") {
println!(concat!(
"\nNo, we could not connect. zcashd might not be running; in that case\n",
"please start it. The '-exportdir' option should be set to the absolute\n",
"path of the directory you want to save the wallet export file to.\n\n",
"(Don't forget to restart zcashd without '-exportdir' after finishing\n",
"the backup, if running it long-term with that option is not desired\n",
"or would be a security hazard in your environment.)\n\n",
"If you believe zcashd is running, it might be using an unexpected port,\n",
"address, or authentication options for the RPC interface, for example.\n",
"In that case try to connect to it using zcash-cli, and if successful,\n",
"use the same connection options for zcashd-wallet-tool (see '--help' for\n",
"accepted options) as for zcash-cli.\n"
));
return Err(WalletToolError::ZcashdConnection.into());
}
if cli_err[0] == "error code: -28" {
println!(concat!(
"\nNo, we could not connect. zcashd seems to be initializing; please try\n",
"again once it has finished.\n",
));
return Err(WalletToolError::ZcashdConnection.into());
}
}
const REMINDER_MSG: &str = concat!(
"\n\nPlease start or restart zcashd with '-exportdir' set to the absolute\n",
"path of the directory you want to save the wallet export file to.\n",
"(Don't forget to restart zcashd without '-exportdir' after finishing\n",
"the backup, if running it long-term with that option is not desired\n",
"or would be a security hazard in your environment.)\n",
);
if cli_err.len() >= 3
&& cli_err[0] == "error code: -4"
&& cli_err[2].contains("zcashd -exportdir")
{
println!(
"\nIt looks like zcashd is running without the '-exportdir' option.{}",
REMINDER_MSG
);
return Err(WalletToolError::ExportDirNotSet.into());
}
if !(cli_err.len() >= 3
&& cli_err[0] == "error code: -4"
&& cli_err[2].starts_with("Filename is invalid"))
{
println!(
"\nThere was an unexpected response from zcash-cli or zcashd:\n> {}{}",
cli_err.join("\n> "),
REMINDER_MSG,
);
return Err(WalletToolError::UnexpectedResponse.into());
}
println!("Yes, and it is running with the '-exportdir' option as required.");
let mut stdin = io::stdin();
let base = default_filename_base();
let mut r = 0u32;
let out = loop {
let default_filename = if r != 0 {
format!("{}r{}", base, r)
} else {
base.to_string()
};
println!(
concat!(
"\nEnter the filename for the wallet export file, using only characters\n",
"a-z, A-Z and 0-9 (default '{}')."
),
default_filename
);
let response = prompt(&mut stdin)?;
let response = strip(&response);
let filename = if response.is_empty() {
r = r.saturating_add(1);
&default_filename
} else {
response
};
debug!("Using filename {:?}", filename);
let mut cli_args = cli_options.clone();
cli_args.extend_from_slice(&["z_exportwallet".to_string(), filename.to_string()]);
let out = exec(&zcash_cli, &cli_args, None)?;
let cli_err: Vec<_> = from_utf8(&out.stderr)
.with_context(|| "Output from zcash-cli was not UTF-8")?
.lines()
.map(|s| s.trim_end_matches('\r'))
.collect();
debug!("stderr {:?}", cli_err);
if cli_err.len() >= 3
&& cli_err[0] == "error code: -8"
&& cli_err[1] == "error message:"
&& cli_err[2].contains("overwrite existing")
{
println!(concat!(
"That file already exists. Please pick a unique filename in the\n",
"directory specified by the '-exportdir' option to zcashd."
));
continue;
} else {
break out;
}
};
let cli_out: Vec<_> = from_utf8(&out.stdout)
.with_context(|| "Output from zcash-cli was not UTF-8")?
.lines()
.map(|s| s.trim_end_matches('\r'))
.collect();
debug!("stdout {:?}", cli_out);
if cli_out.is_empty() {
return Err(WalletToolError::UnexpectedResponse.into());
}
let export_path = cli_out[0];
println!("\nSaved the export file to '{}'.", export_path);
println!("IMPORTANT: This file contains secrets that allow spending all wallet funds.\n");
let export_file = File::open(export_path)
.with_context(|| format!("Could not open {:?} for reading", export_path))?;
// TODO: ensure the buffer will be zeroized (#5650)
let phrase_line: Vec<_> = io::BufReader::new(export_file)
.lines()
.map(|line| line.map(SecretString::new))
.filter(|s| {
s.as_ref()
.map(|t| t.expose_secret().starts_with("# - recovery_phrase=\""))
.unwrap_or(false)
})
.collect();
let phrase = match &phrase_line[..] {
[Ok(line)] => line
.expose_secret()
.trim_start_matches("# - recovery_phrase=\"")
.trim_end_matches('"'),
_ => return Err(WalletToolError::RecoveryPhraseNotFound.into()),
};
// This panic hook allows us to make a best effort to clear the screen (and then print
// another reminder about secrets in the export file) even if a panic occurs.
let old_hook = panic::take_hook();
{
let export_path = export_path.to_string();
panic::set_hook(Box::new(move |panic_info| {
clear_and_show_cautions(&export_path);
let s = panic_info.payload().downcast_ref::<&str>().unwrap_or(&"");
eprintln!("\nPanic: {}\n{:?}", s, Backtrace::new());
}));
}
let res = (|| -> anyhow::Result<()> {
println!("The recovery phrase is:\n");
const WORDS_PER_LINE: usize = 3;
let words: Vec<_> = phrase.split(' ').collect();
let max_len = words.iter().map(|w| w.len()).max().unwrap_or(0);
for (i, word) in words.iter().enumerate() {
print!("{0:2}: {1:2$}", i + 1, word, max_len + 2);
if (i + 1) % WORDS_PER_LINE == 0 {
println!();
}
}
if words.len() % WORDS_PER_LINE != 0 {
println!();
}
println!(concat!(
"\nPlease write down this phrase (including the numbering of words) on\n",
"something durable that you will keep in a secure location.\n",
"Press Enter when finished; then the phrase will disappear and you'll be\n",
"asked to re-enter a selection of words from it."
));
let mut stdin = io::stdin();
prompt(&mut stdin)?;
// The only reliable and portable way to make sure the recovery phrase
// is no longer displayed is to clear the whole terminal (including
// scrollback, if possible). The text is only printed if clearing fails.
try_to_clear(concat!(
"\n\n\n\n\n\n\n\n\n\n\n\n",
"Please adjust the terminal window so that you can't see the\n",
"recovery phrase above. After finishing the backup, close the\n",
"terminal window or clear it"
));
println!("\nNow we're going to confirm that you backed up the recovery phrase.");
let mut rng = thread_rng();
let mut unconfirmed: Vec<usize> = (0..words.len()).collect();
for _ in 0..min(3, words.len()) {
let index: usize = rng.gen_range(0..unconfirmed.len());
let n = unconfirmed[index];
unconfirmed[index] = unconfirmed[unconfirmed.len() - 1];
unconfirmed.pop().expect("should be nonempty");
loop {
println!("\nPlease enter the {} word:", ordinal(n + 1));
let line = prompt(&mut stdin)?;
if words[n] == strip(&line) {
break;
}
println!("That's not correct, please try again.");
}
}
Ok(())
})();
panic::set_hook(old_hook);
clear_and_show_cautions(export_path);
res?;
let mut cli_args = cli_options;
cli_args.extend_from_slice(&["-stdin".to_string(), "walletconfirmbackup".to_string()]);
exec(&zcash_cli, &cli_args, Some(phrase))
.and_then(|out| {
let cli_err: Vec<_> = from_utf8(&out.stderr)
.with_context(|| "Output from zcash-cli was not UTF-8")?
.lines()
.map(|s| s.trim_end_matches('\r'))
.collect();
debug!("stderr {:?}", cli_err);
if !cli_err.is_empty() {
if cli_err[0].starts_with("error: couldn't connect") {
println!("\nWe could not connect to zcashd; it may have exited.");
return Err(WalletToolError::ZcashdConnection.into());
} else {
println!(
"\nThere was an unexpected response from zcash-cli or zcashd:\n> {}",
cli_err.join("\n> "),
);
return Err(WalletToolError::UnexpectedResponse.into());
}
}
println!(concat!(
"\nThe backup of the emergency recovery phrase for the zcashd\n",
"wallet has been successfully confirmed 🙂. You can now use the\n",
"zcashd RPC methods that create keys and addresses in that wallet.\n\n",
"If you use other wallets, their recovery information will need\n",
"to be backed up separately.\n"
));
Ok(())
})
.map_err(|e| {
println!(concat!(
"\nzcash-wallet-tool was unable to communicate to zcashd that the\n",
"backup was confirmed. This can happen if zcashd stopped, in which\n",
"case you should try again. If zcashd is still running, please seek\n",
"help or try to use 'zcash-cli -stdin walletconfirmbackup' manually.\n"
));
e
})?;
Ok(())
}
const MAX_USER_INPUT_LEN: usize = 100;
fn prompt(input: &mut Stdin) -> anyhow::Result<SecretString> {
let mut buf = String::with_capacity(MAX_USER_INPUT_LEN);
let res = input
.read_line(&mut buf)
.with_context(|| "Error reading from stdin");
// Ensure the buffer is zeroized even on error.
let line = SecretString::new(buf);
res.and_then(|_| {
if line.expose_secret().ends_with('\n') {
Ok(line)
} else {
Err(WalletToolError::UnexpectedEof.into())
}
})
}
fn strip(input: &SecretString) -> &str {
input
.expose_secret()
.trim_end_matches(|c| c == '\r' || c == '\n')
.trim()
}
fn ordinal(num: usize) -> String {
let suffix = if (11..=13).contains(&(num % 100)) {
"th"
} else {
match num % 10 {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th",
}
};
format!("{}{}", num, suffix)
}
fn zcash_cli_path() -> anyhow::Result<PathBuf> {
// First look for `zcash_cli[.exe]` as a sibling of the executable.
let mut exe = env::current_exe()
.with_context(|| "Cannot determine the path of the running executable")?;
exe.set_file_name("zcash-cli");
exe.set_extension(EXE_EXTENSION);
debug!("Testing for zcash-cli at {:?}", exe);
if exe.exists() {
return Ok(exe);
}
// If not found there, look in `../src/zcash_cli[.exe]` provided
// that `src` is a sibling of `target`.
exe.pop(); // strip filename
exe.pop(); // ..
if exe.file_name() != Some(OsStr::new("target")) {
// or in `../../src/zcash_cli[.exe]` under the same proviso
exe.pop(); // ../..
if exe.file_name() != Some(OsStr::new("target")) {
return Err(WalletToolError::ZcashCliNotFound.into());
}
}
// Replace 'target/' with 'src/'.
exe.set_file_name("src");
exe.push("zcash-cli");
exe.set_extension(EXE_EXTENSION);
debug!("Testing for zcash-cli at {:?}", exe);
if !exe.exists() {
return Err(WalletToolError::ZcashCliNotFound.into());
}
Ok(exe)
}
fn exec(exe_path: &Path, args: &[String], stdin: Option<&str>) -> anyhow::Result<Output> {
debug!("Running {:?} {:?}", exe_path, args);
let mut cmd = Command::new(exe_path);
let cli = cmd.args(args);
match stdin {
None => Ok(cli.output()?),
Some(data) => {
let mut cli_process = cli
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
cli_process
.stdin
.take()
.with_context(|| "Could not open pipe to zcash-cli's stdin")
.and_then(|mut pipe: ChildStdin| -> anyhow::Result<()> {
pipe.write_all(data.as_bytes())?;
pipe.write_all("\n".as_bytes())?;
Ok(())
})
.with_context(|| "Could not write to zcash-cli's stdin")?;
Ok(cli_process.wait_with_output()?)
}
}
}
fn default_filename_base() -> String {
let format = format_description!("export[year][month][day]");
// We use the UTC date because there is a security issue in obtaining the local date
// from either `chrono` or `time`: <https://github.com/chronotope/chrono/issues/602>.
// We could use the approach in
// <https://github.com/ArekPiekarz/rusty-tax-break/commit/3aac8f0c26fd96b7365619509a544f78b59627fe>
// if it were important, but it isn't worth the dependency on `tz-rs`.
OffsetDateTime::from(SystemTime::now())
.format(&format)
.unwrap_or_else(|_| "export".to_string())
}
fn clear_and_show_cautions(export_path: &str) {
try_to_clear(concat!(
"\nCAUTION: This terminal window might be showing secrets (or have\n",
"them in the scrollback). Please copy any useful information and\n",
"then close it, or clear it"
));
println!(
concat!(
"\nIMPORTANT: Secrets that allow spending all zcashd wallet funds\n",
"have been left in the file '{}'.\n\n",
"Don't forget to restart zcashd without '-exportdir', if running it\n",
"long-term with that option is not desired or would be a security\n",
"hazard in your environment.\n\n",
"When choosing a location for the physical backup of your emergency\n",
"recovery phrase, please make sure to consider both risk of theft,\n",
"and your long-term ability to remember where it is kept."
),
export_path,
);
}
fn try_to_clear(error_blurb: &str) {
if let Err(e) = clearscreen::clear() {
eprintln!("Unable to clear screen: {}.", e);
#[cfg(target_os = "windows")]
const HOW_TO_CLEAR: &str = "using 'cls'";
#[cfg(target_os = "macos")]
const HOW_TO_CLEAR: &str = "by pressing Command + K";
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
const HOW_TO_CLEAR: &str = "using 'clear'";
println!("{} {}.", error_blurb, HOW_TO_CLEAR);
}
}

View File

@ -5,10 +5,13 @@
#ifndef ZCASH_RUST_INCLUDE_RUST_ADDRESS_H
#define ZCASH_RUST_INCLUDE_RUST_ADDRESS_H
#include "rust/orchard/keys.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef bool (*orchard_receiver_t)(void* ua, OrchardRawAddressPtr* addr);
typedef bool (*raw_to_receiver_t)(void* ua, const unsigned char* raw);
typedef bool (*unknown_receiver_t)(
void* ua,
@ -24,6 +27,7 @@ bool zcash_address_parse_unified(
const char* str,
const char* network,
void* ua,
orchard_receiver_t orchard_cb,
raw_to_receiver_t sapling_cb,
raw_to_receiver_t p2sh_cb,
raw_to_receiver_t p2pkh_cb,

View File

@ -0,0 +1,105 @@
// Copyright (c) 2022 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_RUST_INCLUDE_RUST_BUILDER_H
#define ZCASH_RUST_INCLUDE_RUST_BUILDER_H
#include "rust/orchard.h"
#include "rust/orchard/keys.h"
#include "rust/transaction.h"
#ifdef __cplusplus
extern "C" {
#endif
/// A type-safe pointer to a Rust-allocated struct containing the information
/// needed to spend an Orchard note.
struct OrchardSpendInfoPtr;
typedef struct OrchardSpendInfoPtr OrchardSpendInfoPtr;
/// Pointer to Rust-allocated Orchard bundle builder.
struct OrchardBuilderPtr;
typedef struct OrchardBuilderPtr OrchardBuilderPtr;
/// Pointer to Rust-allocated Orchard bundle without proofs
/// or authorizing data.
struct OrchardUnauthorizedBundlePtr;
typedef struct OrchardUnauthorizedBundlePtr OrchardUnauthorizedBundlePtr;
/// Frees the memory associated with an Orchard spend info struct that was
/// allocated by Rust.
void orchard_spend_info_free(OrchardSpendInfoPtr* ptr);
/// Construct a new Orchard transaction builder.
///
/// If `anchor` is `null`, the root of the empty Orchard commitment tree is used.
OrchardBuilderPtr* orchard_builder_new(
bool spends_enabled,
bool outputs_enabled,
const unsigned char* anchor);
/// Frees an Orchard builder returned from `orchard_builder_new`.
void orchard_builder_free(OrchardBuilderPtr* ptr);
/// Adds a note to be spent in this bundle.
///
/// Returns `false` if the Merkle path in `spend_info` does not have the
/// required anchor.
///
/// `spend_info` is always freed by this method, whether or not it succeeds.
bool orchard_builder_add_spend(
OrchardBuilderPtr* ptr,
OrchardSpendInfoPtr* spend_info);
/// Adds an address which will receive funds in this bundle.
///
/// `ovk` is a pointer to the outgoing viewing key to make this recipient recoverable by,
/// or `null` to make the recipient unrecoverable by the sender.
///
/// `memo` is a pointer to the 512-byte memo field encoding, or `null` for "no memo".
bool orchard_builder_add_recipient(
OrchardBuilderPtr* ptr,
const unsigned char* ovk,
const OrchardRawAddressPtr* recipient,
uint64_t value,
const unsigned char* memo);
/// Builds a bundle containing the given spent notes and recipients.
///
/// Returns `null` if an error occurs.
///
/// `builder` is always freed by this method.
OrchardUnauthorizedBundlePtr* orchard_builder_build(OrchardBuilderPtr* builder);
/// Frees an Orchard bundle returned from `orchard_bundle_build`.
void orchard_unauthorized_bundle_free(OrchardUnauthorizedBundlePtr* bundle);
/// Adds proofs and signatures to the bundle.
///
/// Returns `null` if an error occurs.
///
/// `bundle` is always freed by this method.
OrchardBundlePtr* orchard_unauthorized_bundle_prove_and_sign(
OrchardUnauthorizedBundlePtr* bundle,
const OrchardSpendingKeyPtr** keys,
size_t keys_len,
const unsigned char* sighash);
/// Calculates a ZIP 244 shielded signature digest for the given under-construction
/// transaction.
///
/// Returns `false` if any of the parameters are invalid; in this case, `sighash_ret`
/// will be unaltered.
///
/// `preTx` is always freed by this method.
bool zcash_builder_zip244_shielded_signature_digest(
PrecomputedTxParts* preTx,
const OrchardUnauthorizedBundlePtr* bundle,
unsigned char* sighash_ret);
#ifdef __cplusplus
}
#endif
#endif // ZCASH_RUST_INCLUDE_RUST_BUILDER_H

View File

@ -13,6 +13,7 @@
extern "C" {
#endif
/// Typesafe pointer to a Rust-allocated orchard::bundle::Bundle value
struct OrchardBundlePtr;
typedef struct OrchardBundlePtr OrchardBundlePtr;

View File

@ -69,7 +69,7 @@ void orchard_merkle_frontier_root(
// The total number of leaves that have been appended to obtain
// the current state of the frontier. Subtract 1 from this value
// to obtain the position of the most recently appended leaf.
size_t orchard_merkle_frontier_num_leaves(
uint64_t orchard_merkle_frontier_num_leaves(
const OrchardMerkleFrontierPtr* tree_ptr);
// Estimate the amount of memory consumed by the merkle frontier.

View File

@ -32,6 +32,43 @@ OrchardRawAddressPtr* orchard_address_clone(
*/
void orchard_address_free(OrchardRawAddressPtr* ptr);
/**
* Parses Orchard raw address bytes from the given stream.
*
* - If the key does not parse correctly, the returned pointer will be null.
*/
OrchardRawAddressPtr* orchard_raw_address_parse(
void* stream,
read_callback_t read_cb);
/**
* Serializes Orchard raw address bytes to the given stream.
*
* This will return `false` and leave the stream unmodified if
* `raw_address == nullptr`;
*/
bool orchard_raw_address_serialize(
const OrchardRawAddressPtr* raw_address,
void* stream,
write_callback_t write_cb);
/**
* Implements the "equal" operation for comparing two Orchard addresses.
*/
bool orchard_address_eq(
const OrchardRawAddressPtr* k0,
const OrchardRawAddressPtr* k1);
/**
* Implements the "less than" operation `k0 < k1` for comparing two Orchard
* addresses. This is a comparison of the raw bytes, only useful for cases
* where a semantically irrelevant ordering is needed (such as for map keys).
*/
bool orchard_address_lt(
const OrchardRawAddressPtr* k0,
const OrchardRawAddressPtr* k1);
//
// Incoming Viewing Keys
//
@ -60,6 +97,18 @@ OrchardRawAddressPtr* orchard_incoming_viewing_key_to_address(
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
const unsigned char* j);
/**
* Decrypts the diversifier component of an Orchard raw address with the
* specified IVK, and verifies that the address was derived from that IVK.
*
* Returns `false` and leaves the `j_ret` parameter unmodified if the address
* was not derived from the specified IVK.
*/
bool orchard_incoming_viewing_key_decrypt_diversifier(
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
const OrchardRawAddressPtr* addr,
uint8_t *j_ret);
/**
* Parses an Orchard incoming viewing key from the given stream.
*
@ -69,8 +118,12 @@ OrchardIncomingViewingKeyPtr* orchard_incoming_viewing_key_parse(
void* stream,
read_callback_t read_cb);
/**
* Serializes an Orchard incoming viewing key to the given stream.
*
* This will return `false` and leave the stream unmodified if
* `incoming_viewing_key == nullptr`.
*/
bool orchard_incoming_viewing_key_serialize(
const OrchardIncomingViewingKeyPtr* incoming_viewing_key,
@ -123,6 +176,9 @@ OrchardFullViewingKeyPtr* orchard_full_viewing_key_parse(
/**
* Serializes an Orchard full viewing key to the given stream.
*
* This will return `false` and leave the stream unmodified if
* `full_viewing_key == nullptr`.
*/
bool orchard_full_viewing_key_serialize(
const OrchardFullViewingKeyPtr* full_viewing_key,
@ -135,6 +191,30 @@ bool orchard_full_viewing_key_serialize(
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_incoming_viewing_key(
const OrchardFullViewingKeyPtr* key);
/**
* Returns the internal incoming viewing key for the specified full viewing key.
*/
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_internal_incoming_viewing_key(
const OrchardFullViewingKeyPtr* key);
/**
* Returns the external outgoing viewing key for the specified full viewing key.
*
* `ovk_ret` must be 32 bytes.
*/
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_external_outgoing_viewing_key(
const OrchardFullViewingKeyPtr* fvk,
uint8_t *ovk_ret);
/**
* Returns the internal outgoing viewing key for the specified full viewing key.
*
* `ovk_ret` must be 32 bytes.
*/
OrchardIncomingViewingKeyPtr* orchard_full_viewing_key_to_internal_outgoing_viewing_key(
const OrchardFullViewingKeyPtr* fvk,
uint8_t *ovk_ret);
/**
* Implements equality testing between full viewing keys.
*/
@ -173,25 +253,11 @@ OrchardSpendingKeyPtr* orchard_spending_key_clone(
*/
void orchard_spending_key_free(OrchardSpendingKeyPtr* ptr);
/**
* Parses an Orchard spending key from the given stream.
*
* - If the key does not parse correctly, the returned pointer will be null.
*/
OrchardSpendingKeyPtr* orchard_spending_key_parse(
void* stream,
read_callback_t read_cb);
/**
* Serializes an Orchard spending key to the given stream.
*/
bool orchard_spending_key_serialize(
const OrchardSpendingKeyPtr* spending_key,
void* stream,
write_callback_t write_cb);
/**
* Returns the full viewing key for the specified spending key.
*
* The resulting pointer must be ultimately freed by the caller
* using `orchard_full_viewing_key_free`.
*/
OrchardFullViewingKeyPtr* orchard_spending_key_to_full_viewing_key(
const OrchardSpendingKeyPtr* key);

View File

@ -0,0 +1,379 @@
// Copyright (c) 2021 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_RUST_INCLUDE_RUST_ORCHARD_WALLET_H
#define ZCASH_RUST_INCLUDE_RUST_ORCHARD_WALLET_H
#include "rust/orchard/keys.h"
#include "rust/builder.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* A type-safe pointer type for an Orchard wallet.
*/
struct OrchardWalletPtr;
typedef struct OrchardWalletPtr OrchardWalletPtr;
/**
* Constructs a new empty Orchard wallet and return a pointer to it.
* Memory is allocated by Rust and must be manually freed using
* `orchard_wallet_free`.
*/
OrchardWalletPtr* orchard_wallet_new();
/**
* Frees the memory associated with an Orchard wallet that was allocated
* by Rust.
*/
void orchard_wallet_free(OrchardWalletPtr* wallet);
/**
* Reset the state of the wallet to be suitable for rescan from the NU5 activation
* height. This removes all witness and spentness information from the wallet. The
* keystore is unmodified and decrypted note, nullifier, and conflict data are left
* in place with the expectation that they will be overwritten and/or updated in
* the rescan process.
*/
bool orchard_wallet_reset(OrchardWalletPtr* wallet);
/**
* Checkpoint the note commitment tree. This returns `false` and leaves the note
* commitment tree unmodified if the block height specified is not the successor
* to the last block height checkpointed.
*/
bool orchard_wallet_checkpoint(
OrchardWalletPtr* wallet,
uint32_t blockHeight
);
/**
* Returns whether or not the wallet has any checkpointed state to which it can rewind.
* If so, `blockHeightRet` will be modified to contain the last block height at which a
* checkpoint was created.
*/
bool orchard_wallet_get_last_checkpoint(
const OrchardWalletPtr* wallet,
uint32_t* blockHeightRet);
/**
* Rewinds to the most recent checkpoint, and marks as unspent any notes previously
* identified as having been spent by transactions in the latest block.
*
* The `blockHeight` argument provides the height to which the witness tree should be
* rewound, such that after the rewind this height corresponds to the latest block
* appended to the tree. The number of blocks that were removed from the witness
* tree in the rewind process is returned via `blocksRewoundRet`.
*
* Returns `true` if the rewind is successful, in which case the number of blocks that were
* removed from the witness tree in the rewind process is returned via `blocksRewoundRet`;
* this returns `false` and leaves `blocksRewoundRet` unmodified.
*/
bool orchard_wallet_rewind(
OrchardWalletPtr* wallet,
uint32_t blockHeight,
uint32_t* blocksRewoundRet
);
/**
* A C struct used to transfer action_idx/IVK pairs back from Rust across the FFI
* boundary. This must have the same in-memory representation as the `FFIActionIVK` type
* in orchard_ffi/wallet.rs.
*
* Values of the `ivk` pointer must be freed manually; the best way to do this is to
* wrap this pointer in an `OrchardIncomingViewingKey` which handles deallocation
* in the object destructor.
*/
struct RawOrchardActionIVK {
uint64_t actionIdx;
OrchardIncomingViewingKeyPtr* ivk;
};
static_assert(
sizeof(RawOrchardActionIVK) == 16,
"RawOrchardActionIVK struct should have exactly a 128-bit in-memory representation.");
static_assert(alignof(RawOrchardActionIVK) == 8, "RawOrchardActionIVK struct alignment is not 64 bits.");
typedef void (*push_action_ivk_callback_t)(void* rec, const RawOrchardActionIVK actionIvk);
typedef void (*push_spend_action_idx_callback_t)(void* rec, uint32_t actionIdx);
/**
* Searches the provided bundle for notes that are visible to the specified wallet's
* incoming viewing keys, and adds those notes to the wallet. For each note decryptable
* by one of the wallet's keys, this method will insert a `RawOrchardActionIVK` value into
* the provided `callbackReceiver` referent using the `push_cb` callback. Note that
* this callback can perform transformations on the provided RawOrchardActionIVK in this
* process. For each action spending one of the wallet's notes, this method will pass
* a `uint32_t` action index corresponding to that action to the `callbackReceiver` referent;
* using the specified callback; usually, this will push the value into a result vector owned
* by the caller.
*
* The provided bundle must be a component of the transaction from which `txid` was
* derived.
*
* Returns `true` if the bundle is involved with the wallet; i.e. if it contains
* notes spendable by the wallet, or spends any of the wallet's notes.
*/
bool orchard_wallet_add_notes_from_bundle(
OrchardWalletPtr* wallet,
const unsigned char txid[32],
const OrchardBundlePtr* bundle,
void* callbackReceiver,
push_action_ivk_callback_t push_cb,
push_spend_action_idx_callback_t spend_cb
);
/**
* Decrypts a selection of notes from the bundle with specified incoming viewing
* keys, and adds those notes to the wallet.
*
* The provided bundle must be a component of the transaction from which
* `txid` was derived.
*
* The value the `blockHeight` pointer points to be set to the height at which the
* transaction was mined, or `nullptr` if the transaction is not in the main chain.
*/
bool orchard_wallet_load_bundle(
OrchardWalletPtr* wallet,
const uint32_t* blockHeight,
const unsigned char txid[32],
const OrchardBundlePtr* bundle,
const RawOrchardActionIVK* actionIvks,
size_t actionIvksLen,
const uint32_t* actionsSpendingWalletNotes,
size_t actionsSpendingWalletNotesLen
);
/**
* Add the note commitment values for the specified bundle to the wallet's note
* commitment tree, and mark any Orchard notes that belong to the wallet so
* that we can construct authentication paths to these notes in the future.
*
* This requires the block height and the index of the block within the
* transaction in order to guarantee that note commitments are appended in the
* correct order. Returns `false` if the provided bundle is not in the correct
* position to have its note commitments appended to the note commitment tree.
*/
bool orchard_wallet_append_bundle_commitments(
OrchardWalletPtr* wallet,
const uint32_t block_height,
const size_t block_tx_idx,
const unsigned char txid[32],
const OrchardBundlePtr* bundle
);
/**
* Returns the root of the wallet's note commitment tree.
*/
void orchard_wallet_commitment_tree_root(
const OrchardWalletPtr* wallet,
unsigned char* root_ret);
/**
* Returns whether the specified transaction involves any Orchard notes that belong to
* this wallet.
*/
bool orchard_wallet_tx_involves_my_notes(
const OrchardWalletPtr* wallet,
const unsigned char txid[32]);
/**
* Add the specified spending key to the wallet's key store. This will also compute and
* add the associated full and incoming viewing keys.
*/
void orchard_wallet_add_spending_key(
OrchardWalletPtr* wallet,
const OrchardSpendingKeyPtr* sk);
/**
* Add the specified full viewing key to the wallet's key store. This will also compute
* and add the associated incoming viewing key.
*/
void orchard_wallet_add_full_viewing_key(
OrchardWalletPtr* wallet,
const OrchardFullViewingKeyPtr* fvk);
/**
* Add the specified raw address to the wallet's key store, associated with the incoming
* viewing key from which that address was derived.
*/
bool orchard_wallet_add_raw_address(
OrchardWalletPtr* wallet,
const OrchardRawAddressPtr* addr,
const OrchardIncomingViewingKeyPtr* ivk);
/**
* Returns a pointer to the Orchard spending key corresponding to the specified raw
* address, if it is known to the wallet, or `nullptr` otherwise.
*
* Memory is allocated by Rust and must be manually freed using
* `orchard_spending_key_free`.
*/
OrchardSpendingKeyPtr* orchard_wallet_get_spending_key_for_address(
const OrchardWalletPtr* wallet,
const OrchardRawAddressPtr* addr);
/**
* Returns a pointer to the Orchard incoming viewing key corresponding to the specified
* raw address, if it is known to the wallet, or `nullptr` otherwise.
*
* Memory is allocated by Rust and must be manually freed using
* `orchard_incoming_viewing_key_free`.
*/
OrchardIncomingViewingKeyPtr* orchard_wallet_get_ivk_for_address(
const OrchardWalletPtr* wallet,
const OrchardRawAddressPtr* addr);
/**
* A C struct used to transfer note metadata information across the Rust FFI boundary.
* This must have the same in-memory representation as the `FFINoteMetadata` type in
* orchard_ffi/wallet.rs.
*/
struct RawOrchardNoteMetadata {
unsigned char txid[32];
uint32_t actionIdx;
OrchardRawAddressPtr* addr;
CAmount noteValue;
unsigned char memo[512];
};
typedef void (*push_note_callback_t)(void* resultVector, const RawOrchardNoteMetadata noteMeta);
/**
* Finds notes that belong to the wallet that were sent to addresses derived from the
* specified incoming viewing key, subject to the specified flags, and uses the provided
* callback to push RawOrchardNoteMetadata values corresponding to those notes on to the
* provided result vector. Note that the push_cb callback can perform any necessary
* conversion from a RawOrchardNoteMetadata value in addition to modifying the provided
* result vector.
*
* If `ivk` is null, all notes belonging to the wallet will be returned. The
* `RawOrchardNoteMetadata::addr` pointers for values provided to the callback must be
* manually freed by the caller.
*/
void orchard_wallet_get_filtered_notes(
const OrchardWalletPtr* wallet,
const OrchardIncomingViewingKeyPtr* ivk,
bool ignoreMined,
bool requireSpendingKey,
void* resultVector,
push_note_callback_t push_cb
);
/**
* A C struct used to transfer Orchard action spend information across the FFI boundary.
* This must have the same in-memory representation as the `FFIActionSpend` type in
* orchard_ffi/wallet.rs.
*/
struct RawOrchardActionSpend {
uint32_t spendActionIdx;
unsigned char outpointTxId[32];
uint32_t outpointActionIdx;
OrchardRawAddressPtr* receivedAt;
CAmount noteValue;
};
/**
* A C struct used to transfer Orchard action output information across the FFI boundary.
* This must have the same in-memory representation as the `FFIActionOutput` type in
* orchard_ffi/wallet.rs.
*/
struct RawOrchardActionOutput {
uint32_t outputActionIdx;
OrchardRawAddressPtr* recipient;
CAmount noteValue;
unsigned char memo[512];
bool isOutgoing;
};
typedef void (*push_spend_t)(void* callbackReceiver, const RawOrchardActionSpend data);
typedef void (*push_output_t)(void* callbackReceiver, const RawOrchardActionOutput data);
/**
* Trial-decrypts the specfied Orchard bundle, and uses the provided callbacks to pass
* `RawOrchardActionSpend` and `RawOrchardActionOutput` values (corresponding to the
* actions of that bundle) to the provided result receiver.
*
* Note that the callbacks can perform any necessary conversion from a
* `RawOrchardActionSpend` or `RawOrchardActionOutput` value in addition to modifying the
* provided result receiver.
*
* `raw_ovks` must be a pointer to an array of `unsigned char[32]`.
*
* The `recipient` pointer for each `RawOrchardActionOutput` value, and the `receivedAt`
* pointer for each `RawOrchardActionSpend` value, must be freed using
* `orchard_address_free`.
*/
bool orchard_wallet_get_txdata(
const OrchardWalletPtr* wallet,
const OrchardBundlePtr* bundle,
const unsigned char* raw_ovks,
size_t raw_ovks_len,
void* callbackReceiver,
push_spend_t push_spend_cb,
push_output_t push_output_cb
);
typedef void (*push_txid_callback_t)(void* resultVector, unsigned char txid[32]);
/**
* Returns a vector of transaction IDs for transactions that have been observed as
* spending the given outpoint (transaction ID and action index) by using the `push_cb`
* callback to push transaction IDs onto the provided result vector.
*/
void orchard_wallet_get_potential_spends(
const OrchardWalletPtr* wallet,
const unsigned char txid[32],
const uint32_t action_idx,
void* resultVector,
push_txid_callback_t push_cb
);
/**
* Fetches the information needed to spend the wallet note at the given outpoint,
* relative to the current root known to the wallet of the Orchard commitment
* tree.
*
* Returns `null` if the outpoint is not known to the wallet, or the Orchard
* bundle containing the note has not been passed to
* `orchard_wallet_append_bundle_commitments`.
*/
OrchardSpendInfoPtr* orchard_wallet_get_spend_info(
const OrchardWalletPtr* wallet,
const unsigned char txid[32],
uint32_t action_idx);
/**
* Run the garbage collection operation on the wallet's note commitment
* tree.
*/
void orchard_wallet_gc_note_commitment_tree(OrchardWalletPtr* wallet);
/**
* Write the wallet's note commitment tree to the provided stream.
*/
bool orchard_wallet_write_note_commitment_tree(
const OrchardWalletPtr* wallet,
void* stream,
write_callback_t write_cb);
/**
* Read a note commitment tree from the provided stream, and update the wallet's internal
* note commitment tree state to equal the value that was read.
*/
bool orchard_wallet_load_note_commitment_tree(
OrchardWalletPtr* wallet,
void* stream,
read_callback_t read_cb);
#ifdef __cplusplus
}
#endif
#endif // ZCASH_RUST_INCLUDE_RUST_ORCHARD_WALLET_H

View File

@ -84,6 +84,22 @@ bool unified_full_viewing_key_read_sapling(
const UnifiedFullViewingKeyPtr* full_viewing_key,
unsigned char* skeyout);
/**
* Reads the Orchard component of a unified full viewing key.
*
* `skeyout` must be of length 96.
*
* Returns `true` if the UFVK contained an Orchard component, `false` otherwise.
* The bytes of the Orchard Raw Full Viewing Key, in the encoding given in
* section 5.6.4.4 of the Zcash Protocol Specification, will be copied to
* `skeyout` if `true` is returned.
*
* If `false` is returned then `skeyout` will be unchanged.
*/
bool unified_full_viewing_key_read_orchard(
const UnifiedFullViewingKeyPtr* full_viewing_key,
unsigned char* skeyout);
/**
* Constructs a unified full viewing key from the binary encodings
* of its constituent parts.
@ -101,7 +117,8 @@ bool unified_full_viewing_key_read_sapling(
*/
UnifiedFullViewingKeyPtr* unified_full_viewing_key_from_components(
const unsigned char* t_key,
const unsigned char* sapling_key);
const unsigned char* sapling_key,
const unsigned char* orchard_key);
/**
* Derive the internal and external OVKs for the binary encoding

View File

@ -12,6 +12,8 @@ use zcash_address::{
use zcash_primitives::sapling;
pub type UnifiedAddressObj = NonNull<c_void>;
pub type AddOrchardReceiverCb =
unsafe extern "C" fn(ua: Option<UnifiedAddressObj>, orchard: *const orchard::Address) -> bool;
pub type AddReceiverCb =
unsafe extern "C" fn(ua: Option<UnifiedAddressObj>, raw: *const u8) -> bool;
pub type UnknownReceiverCb = unsafe extern "C" fn(
@ -53,10 +55,12 @@ impl FromAddress for UnifiedAddressHelper {
}
impl UnifiedAddressHelper {
#[allow(clippy::too_many_arguments)]
fn into_cpp(
self,
network: Network,
ua_obj: Option<UnifiedAddressObj>,
orchard_cb: Option<AddOrchardReceiverCb>,
sapling_cb: Option<AddReceiverCb>,
p2sh_cb: Option<AddReceiverCb>,
p2pkh_cb: Option<AddReceiverCb>,
@ -79,16 +83,13 @@ impl UnifiedAddressHelper {
// ZIP 316: Consumers MUST reject Unified Addresses/Viewing Keys in
// which any constituent Item does not meet the validation
// requirements of its encoding.
if orchard::Address::from_raw_address_bytes(&data)
.is_none()
.into()
{
let addr = orchard::Address::from_raw_address_bytes(&data);
if addr.is_none().into() {
tracing::error!("Unified Address contains invalid Orchard receiver");
false
} else {
unsafe {
// TODO: Replace with Orchard support.
(unknown_cb.unwrap())(ua_obj, 0x03, data.as_ptr(), data.len())
(orchard_cb.unwrap())(ua_obj, Box::into_raw(Box::new(addr.unwrap())))
}
}
}
@ -122,6 +123,7 @@ pub extern "C" fn zcash_address_parse_unified(
encoded: *const c_char,
network: *const c_char,
ua_obj: Option<UnifiedAddressObj>,
orchard_cb: Option<AddOrchardReceiverCb>,
sapling_cb: Option<AddReceiverCb>,
p2sh_cb: Option<AddReceiverCb>,
p2pkh_cb: Option<AddReceiverCb>,
@ -149,7 +151,9 @@ pub extern "C" fn zcash_address_parse_unified(
}
};
ua.into_cpp(network, ua_obj, sapling_cb, p2sh_cb, p2pkh_cb, unknown_cb)
ua.into_cpp(
network, ua_obj, orchard_cb, sapling_cb, p2sh_cb, p2pkh_cb, unknown_cb,
)
}
#[no_mangle]
@ -171,14 +175,9 @@ pub extern "C" fn zcash_address_serialize_unified(
Ok(
match unsafe { (typecode_cb.unwrap())(ua_obj, i) }.try_into()? {
unified::Typecode::Orchard => {
// TODO: Replace with Orchard support.
let data_len = unsafe { (receiver_len_cb.unwrap())(ua_obj, i) };
let mut data = vec![0; data_len];
unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data_len) };
unified::Receiver::Unknown {
typecode: 0x03,
data,
}
let mut data = [0; 43];
unsafe { (receiver_cb.unwrap())(ua_obj, i, data.as_mut_ptr(), data.len()) };
unified::Receiver::Orchard(data)
}
unified::Typecode::Sapling => {
let mut data = [0; 43];

232
src/rust/src/builder_ffi.rs Normal file
View File

@ -0,0 +1,232 @@
use std::convert::TryInto;
use std::ptr;
use std::slice;
use incrementalmerkletree::Hashable;
use libc::size_t;
use orchard::keys::SpendingKey;
use orchard::{
builder::{Builder, InProgress, Unauthorized, Unproven},
bundle::{Authorized, Flags},
keys::{FullViewingKey, OutgoingViewingKey},
tree::{MerkleHashOrchard, MerklePath},
value::NoteValue,
Bundle, Note,
};
use rand_core::OsRng;
use tracing::error;
use zcash_primitives::transaction::{
components::{sapling, Amount},
sighash::SignableInput,
sighash_v5::v5_signature_hash,
txid::TxIdDigester,
Authorization, TransactionData, TxVersion,
};
use crate::{
transaction_ffi::{PrecomputedTxParts, TransparentAuth},
ORCHARD_PK,
};
pub struct OrchardSpendInfo {
fvk: FullViewingKey,
note: Note,
merkle_path: MerklePath,
}
impl OrchardSpendInfo {
pub fn from_parts(fvk: FullViewingKey, note: Note, merkle_path: MerklePath) -> Self {
OrchardSpendInfo {
fvk,
note,
merkle_path,
}
}
}
#[no_mangle]
pub extern "C" fn orchard_spend_info_free(spend_info: *mut OrchardSpendInfo) {
if !spend_info.is_null() {
drop(unsafe { Box::from_raw(spend_info) });
}
}
#[no_mangle]
pub extern "C" fn orchard_builder_new(
spends_enabled: bool,
outputs_enabled: bool,
anchor: *const [u8; 32],
) -> *mut Builder {
let anchor = unsafe { anchor.as_ref() }
.map(|a| orchard::Anchor::from_bytes(*a).unwrap())
.unwrap_or_else(|| MerkleHashOrchard::empty_root(32.into()).into());
Box::into_raw(Box::new(Builder::new(
Flags::from_parts(spends_enabled, outputs_enabled),
anchor,
)))
}
#[no_mangle]
pub extern "C" fn orchard_builder_add_spend(
builder: *mut Builder,
orchard_spend_info: *mut OrchardSpendInfo,
) -> bool {
let builder = unsafe { builder.as_mut() }.expect("Builder may not be null.");
let orchard_spend_info = unsafe { Box::from_raw(orchard_spend_info) };
match builder.add_spend(
orchard_spend_info.fvk,
orchard_spend_info.note,
orchard_spend_info.merkle_path,
) {
Ok(()) => true,
Err(e) => {
error!("Failed to add Orchard spend: {}", e);
false
}
}
}
#[no_mangle]
pub extern "C" fn orchard_builder_add_recipient(
builder: *mut Builder,
ovk: *const [u8; 32],
recipient: *const orchard::Address,
value: u64,
memo: *const [u8; 512],
) -> bool {
let builder = unsafe { builder.as_mut() }.expect("Builder may not be null.");
let ovk = unsafe { ovk.as_ref() }
.copied()
.map(OutgoingViewingKey::from);
let recipient = unsafe { recipient.as_ref() }.expect("Recipient may not be null.");
let value = NoteValue::from_raw(value);
let memo = unsafe { memo.as_ref() }.copied();
match builder.add_recipient(ovk, *recipient, value, memo) {
Ok(()) => true,
Err(e) => {
error!("Failed to add Orchard recipient: {}", e);
false
}
}
}
#[no_mangle]
pub extern "C" fn orchard_builder_free(builder: *mut Builder) {
if !builder.is_null() {
drop(unsafe { Box::from_raw(builder) });
}
}
#[no_mangle]
pub extern "C" fn orchard_builder_build(
builder: *mut Builder,
) -> *mut Bundle<InProgress<Unproven, Unauthorized>, Amount> {
if builder.is_null() {
error!("Called with null builder");
return ptr::null_mut();
}
let builder = unsafe { Box::from_raw(builder) };
match builder.build(OsRng) {
Ok(bundle) => Box::into_raw(Box::new(bundle)),
Err(e) => {
error!("Failed to build Orchard bundle: {:?}", e);
ptr::null_mut()
}
}
}
#[no_mangle]
pub extern "C" fn orchard_unauthorized_bundle_free(
bundle: *mut Bundle<InProgress<Unproven, Unauthorized>, Amount>,
) {
if !bundle.is_null() {
drop(unsafe { Box::from_raw(bundle) });
}
}
#[no_mangle]
pub extern "C" fn orchard_unauthorized_bundle_prove_and_sign(
bundle: *mut Bundle<InProgress<Unproven, Unauthorized>, Amount>,
keys: *const *const SpendingKey,
keys_len: size_t,
sighash: *const [u8; 32],
) -> *mut Bundle<Authorized, Amount> {
let bundle = unsafe { Box::from_raw(bundle) };
let keys = unsafe { slice::from_raw_parts(keys, keys_len) };
let sighash = unsafe { sighash.as_ref() }.expect("sighash pointer may not be null.");
let pk = unsafe { ORCHARD_PK.as_ref() }.unwrap();
let signing_keys = keys
.iter()
.map(|sk| {
unsafe { sk.as_ref() }
.expect("SpendingKey pointers must not be null")
.into()
})
.collect::<Vec<_>>();
let mut rng = OsRng;
let res = bundle
.create_proof(pk, &mut rng)
.and_then(|b| b.apply_signatures(&mut rng, *sighash, &signing_keys));
match res {
Ok(signed) => Box::into_raw(Box::new(signed)),
Err(e) => {
error!(
"An error occurred while authorizing the orchard bundle: {:?}",
e
);
std::ptr::null_mut()
}
}
}
/// Calculates a ZIP 244 shielded signature digest for the given under-construction
/// transaction.
///
/// Returns `false` if any of the parameters are invalid; in this case, `sighash_ret`
/// will be unaltered.
#[no_mangle]
pub extern "C" fn zcash_builder_zip244_shielded_signature_digest(
precomputed_tx: *mut PrecomputedTxParts,
bundle: *const Bundle<InProgress<Unproven, Unauthorized>, Amount>,
sighash_ret: *mut [u8; 32],
) -> bool {
let precomputed_tx = if !precomputed_tx.is_null() {
unsafe { Box::from_raw(precomputed_tx) }
} else {
error!("Invalid precomputed transaction");
return false;
};
if matches!(
precomputed_tx.tx.version(),
TxVersion::Sprout(_) | TxVersion::Overwinter | TxVersion::Sapling,
) {
error!("Cannot calculate ZIP 244 digest for pre-v5 transaction");
return false;
}
let bundle = unsafe { bundle.as_ref().unwrap() };
struct Signable {}
impl Authorization for Signable {
type TransparentAuth = TransparentAuth;
type SaplingAuth = sapling::Authorized;
type OrchardAuth = InProgress<Unproven, Unauthorized>;
}
let txdata: TransactionData<Signable> =
precomputed_tx
.tx
.map_bundles(|b| b, |b| b, |_| Some(bundle.clone()));
let txid_parts = txdata.digest(TxIdDigester);
let sighash = v5_signature_hash(&txdata, &SignableInput::Shielded, &txid_parts);
// `v5_signature_hash` output is always 32 bytes.
*unsafe { &mut *sighash_ret } = sighash.as_ref().try_into().unwrap();
true
}

View File

@ -0,0 +1,115 @@
use byteorder::{ReadBytesExt, WriteBytesExt};
use std::io::{self, Read, Write};
use incrementalmerkletree::{
bridgetree::{BridgeTree, Checkpoint},
Hashable,
};
use zcash_encoding::Vector;
use zcash_primitives::merkle_tree::{
incremental::{
read_bridge_v1, read_leu64_usize, read_position, write_bridge_v1, write_position,
write_usize_leu64, SER_V1,
},
HashSer,
};
pub fn write_checkpoint_v1<H: HashSer + Ord, W: Write>(
mut writer: W,
checkpoint: &Checkpoint<H>,
) -> io::Result<()> {
write_usize_leu64(&mut writer, checkpoint.bridges_len())?;
writer.write_u8(if checkpoint.is_witnessed() { 1 } else { 0 })?;
Vector::write_sized(
&mut writer,
checkpoint.forgotten().iter(),
|mut w, ((pos, leaf_value), idx)| {
write_position(&mut w, *pos)?;
leaf_value.write(&mut w)?;
write_usize_leu64(&mut w, *idx)
},
)?;
Ok(())
}
pub fn read_checkpoint_v1<H: HashSer + Ord, R: Read>(mut reader: R) -> io::Result<Checkpoint<H>> {
Ok(Checkpoint::from_parts(
read_leu64_usize(&mut reader)?,
reader.read_u8()? == 1,
Vector::read_collected(&mut reader, |mut r| {
Ok((
(read_position(&mut r)?, H::read(&mut r)?),
read_leu64_usize(&mut r)?,
))
})?,
))
}
pub fn write_tree_v1<H: Hashable + HashSer + Ord, W: Write>(
mut writer: W,
tree: &BridgeTree<H, 32>,
) -> io::Result<()> {
Vector::write(&mut writer, tree.bridges(), |w, b| write_bridge_v1(w, b))?;
Vector::write_sized(
&mut writer,
tree.witnessed_indices().iter(),
|mut w, ((pos, a), i)| {
write_position(&mut w, *pos)?;
a.write(&mut w)?;
write_usize_leu64(&mut w, *i)
},
)?;
Vector::write(&mut writer, tree.checkpoints(), |w, c| {
write_checkpoint_v1(w, c)
})?;
write_usize_leu64(&mut writer, tree.max_checkpoints())?;
Ok(())
}
#[allow(clippy::redundant_closure)]
pub fn read_tree_v1<H: Hashable + HashSer + Ord + Clone, R: Read>(
mut reader: R,
) -> io::Result<BridgeTree<H, 32>> {
BridgeTree::from_parts(
Vector::read(&mut reader, |r| read_bridge_v1(r))?,
Vector::read_collected(&mut reader, |mut r| {
Ok((
(read_position(&mut r)?, H::read(&mut r)?),
read_leu64_usize(&mut r)?,
))
})?,
Vector::read(&mut reader, |r| read_checkpoint_v1(r))?,
read_leu64_usize(&mut reader)?,
)
.map_err(|err| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"Consistency violation found when attempting to deserialize Merkle tree: {:?}",
err
),
)
})
}
pub fn write_tree<H: Hashable + HashSer + Ord, W: Write>(
mut writer: W,
tree: &BridgeTree<H, 32>,
) -> io::Result<()> {
writer.write_u8(SER_V1)?;
write_tree_v1(&mut writer, tree)
}
pub fn read_tree<H: Hashable + HashSer + Ord + Clone, R: Read>(
mut reader: R,
) -> io::Result<BridgeTree<H, 32>> {
match reader.read_u8()? {
SER_V1 => read_tree_v1(&mut reader),
flag => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unrecognized tree serialization version: {:?}", flag),
)),
}
}

View File

@ -123,13 +123,13 @@ pub extern "C" fn orchard_merkle_frontier_root(
#[no_mangle]
pub extern "C" fn orchard_merkle_frontier_num_leaves(
tree: *const bridgetree::Frontier<MerkleHashOrchard, MERKLE_DEPTH>,
) -> usize {
) -> u64 {
let tree = unsafe {
tree.as_ref()
.expect("Orchard note commitment tree pointer may not be null.")
};
tree.position().map_or(0, |p| (<u64>::from(p) + 1) as usize)
tree.position().map_or(0, |p| <u64>::from(p) + 1)
}
#[no_mangle]

View File

@ -2,10 +2,15 @@ use std::io::{Read, Write};
use std::slice;
use tracing::error;
use orchard::keys::{DiversifierIndex, FullViewingKey, IncomingViewingKey, SpendingKey};
use orchard::Address;
use orchard::{
keys::{DiversifierIndex, FullViewingKey, IncomingViewingKey, OutgoingViewingKey, SpendingKey},
Address,
};
use crate::streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb};
use crate::{
streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb},
zcashd_orchard::OrderedAddress,
};
//
// Addresses
@ -25,6 +30,63 @@ pub extern "C" fn orchard_address_free(addr: *mut Address) {
}
}
#[no_mangle]
pub extern "C" fn orchard_raw_address_parse(
stream: Option<StreamObj>,
read_cb: Option<ReadCb>,
) -> *mut Address {
let mut reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap());
let mut buf = [0u8; 43];
match reader.read_exact(&mut buf) {
Err(e) => {
error!("Stream failure reading bytes of Orchard raw address: {}", e);
std::ptr::null_mut()
}
Ok(()) => {
let read = Address::from_raw_address_bytes(&buf);
if read.is_some().into() {
Box::into_raw(Box::new(read.unwrap()))
} else {
error!("Failed to parse Orchard raw address.");
std::ptr::null_mut()
}
}
}
}
#[no_mangle]
pub extern "C" fn orchard_raw_address_serialize(
key: *const Address,
stream: Option<StreamObj>,
write_cb: Option<WriteCb>,
) -> bool {
let key = unsafe { key.as_ref() }.expect("Orchard raw address pointer may not be null.");
let mut writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap());
match writer.write_all(&key.to_raw_address_bytes()) {
Ok(()) => true,
Err(e) => {
error!("Stream failure writing Orchard raw address: {}", e);
false
}
}
}
#[no_mangle]
pub extern "C" fn orchard_address_eq(a0: *const Address, a1: *const Address) -> bool {
let a0 = unsafe { a0.as_ref() };
let a1 = unsafe { a1.as_ref() };
a0 == a1
}
#[no_mangle]
pub extern "C" fn orchard_address_lt(a0: *const Address, a1: *const Address) -> bool {
let a0 = unsafe { a0.as_ref() };
let a1 = unsafe { a1.as_ref() };
a0.map(|a| OrderedAddress::new(*a)) < a1.map(|a| OrderedAddress::new(*a))
}
//
// Incoming viewing keys
//
@ -85,6 +147,26 @@ pub extern "C" fn orchard_incoming_viewing_key_to_address(
Box::into_raw(Box::new(key.address_at(diversifier_index)))
}
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_decrypt_diversifier(
key: *const IncomingViewingKey,
addr: *const Address,
j_ret: *mut [u8; 11],
) -> bool {
let key =
unsafe { key.as_ref() }.expect("Orchard incoming viewing key pointer may not be null.");
let addr = unsafe { addr.as_ref() }.expect("Orchard raw address pointer may not be null.");
let j_ret = unsafe { j_ret.as_mut() }.expect("j_ret may not be null.");
match key.diversifier_index(addr) {
Some(j) => {
j_ret.copy_from_slice(j.to_bytes());
true
}
None => false,
}
}
#[no_mangle]
pub extern "C" fn orchard_incoming_viewing_key_serialize(
key: *const IncomingViewingKey,
@ -190,6 +272,43 @@ pub extern "C" fn orchard_full_viewing_key_to_incoming_viewing_key(
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_to_internal_incoming_viewing_key(
fvk: *const FullViewingKey,
) -> *mut IncomingViewingKey {
unsafe { fvk.as_ref() }
.map(|fvk| {
let internal_fvk = fvk.derive_internal();
Box::into_raw(Box::new(IncomingViewingKey::from(&internal_fvk)))
})
.unwrap_or(std::ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_to_external_outgoing_viewing_key(
fvk: *const FullViewingKey,
ovk_ret: *mut [u8; 32],
) {
let fvk = unsafe { fvk.as_ref() }.expect("fvk must not be null");
let ovk_ret = unsafe { ovk_ret.as_mut() }.expect("ovk_ret must not be null");
let ovk = OutgoingViewingKey::from(fvk);
*ovk_ret = *ovk.as_ref();
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_to_internal_outgoing_viewing_key(
fvk: *const FullViewingKey,
ovk_ret: *mut [u8; 32],
) {
let fvk = unsafe { fvk.as_ref() }.expect("fvk must not be null");
let ovk_ret = unsafe { ovk_ret.as_mut() }.expect("ovk_ret must not be null");
let internal_fvk = fvk.derive_internal();
let ovk = OutgoingViewingKey::from(&internal_fvk);
*ovk_ret = *ovk.as_ref();
}
#[no_mangle]
pub extern "C" fn orchard_full_viewing_key_eq(
k0: *const FullViewingKey,

View File

@ -29,6 +29,7 @@ use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::slice;
use std::sync::Once;
use subtle::CtOption;
use tracing::{error, info};
@ -69,14 +70,18 @@ mod ed25519;
mod metrics_ffi;
mod streams_ffi;
mod tracing_ffi;
mod zcashd_orchard;
mod address_ffi;
mod builder_ffi;
mod history_ffi;
mod incremental_merkle_tree;
mod incremental_merkle_tree_ffi;
mod orchard_ffi;
mod orchard_keys_ffi;
mod transaction_ffi;
mod unified_keys_ffi;
mod wallet;
mod zip339_ffi;
mod test_harness_ffi;
@ -84,6 +89,7 @@ mod test_harness_ffi;
#[cfg(test)]
mod tests;
static PROOF_PARAMETERS_LOADED: Once = Once::new();
static mut SAPLING_SPEND_VK: Option<PreparedVerifyingKey<Bls12>> = None;
static mut SAPLING_OUTPUT_VK: Option<PreparedVerifyingKey<Bls12>> = None;
static mut SPROUT_GROTH16_VK: Option<PreparedVerifyingKey<Bls12>> = None;
@ -127,64 +133,66 @@ pub extern "C" fn librustzcash_init_zksnark_params(
#[cfg(target_os = "windows")] sprout_path: *const u16,
sprout_path_len: usize,
) {
#[cfg(not(target_os = "windows"))]
let (spend_path, output_path, sprout_path) = {
(
OsStr::from_bytes(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }),
OsStr::from_bytes(unsafe { slice::from_raw_parts(output_path, output_path_len) }),
if sprout_path.is_null() {
None
} else {
Some(OsStr::from_bytes(unsafe {
slice::from_raw_parts(sprout_path, sprout_path_len)
}))
},
)
};
PROOF_PARAMETERS_LOADED.call_once(|| {
#[cfg(not(target_os = "windows"))]
let (spend_path, output_path, sprout_path) = {
(
OsStr::from_bytes(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }),
OsStr::from_bytes(unsafe { slice::from_raw_parts(output_path, output_path_len) }),
if sprout_path.is_null() {
None
} else {
Some(OsStr::from_bytes(unsafe {
slice::from_raw_parts(sprout_path, sprout_path_len)
}))
},
)
};
#[cfg(target_os = "windows")]
let (spend_path, output_path, sprout_path) = {
(
OsString::from_wide(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }),
OsString::from_wide(unsafe { slice::from_raw_parts(output_path, output_path_len) }),
if sprout_path.is_null() {
None
} else {
Some(OsString::from_wide(unsafe {
slice::from_raw_parts(sprout_path, sprout_path_len)
}))
},
)
};
#[cfg(target_os = "windows")]
let (spend_path, output_path, sprout_path) = {
(
OsString::from_wide(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }),
OsString::from_wide(unsafe { slice::from_raw_parts(output_path, output_path_len) }),
if sprout_path.is_null() {
None
} else {
Some(OsString::from_wide(unsafe {
slice::from_raw_parts(sprout_path, sprout_path_len)
}))
},
)
};
let (spend_path, output_path, sprout_path) = (
Path::new(&spend_path),
Path::new(&output_path),
sprout_path.as_ref().map(|p| Path::new(p)),
);
let (spend_path, output_path, sprout_path) = (
Path::new(&spend_path),
Path::new(&output_path),
sprout_path.as_ref().map(Path::new),
);
// Load params
let params = load_parameters(spend_path, output_path, sprout_path);
// Load params
let params = load_parameters(spend_path, output_path, sprout_path);
// Generate Orchard parameters.
info!(target: "main", "Loading Orchard parameters");
let orchard_pk = orchard::circuit::ProvingKey::build();
let orchard_vk = orchard::circuit::VerifyingKey::build();
// Generate Orchard parameters.
info!(target: "main", "Loading Orchard parameters");
let orchard_pk = orchard::circuit::ProvingKey::build();
let orchard_vk = orchard::circuit::VerifyingKey::build();
// Caller is responsible for calling this function once, so
// these global mutations are safe.
unsafe {
SAPLING_SPEND_PARAMS = Some(params.spend_params);
SAPLING_OUTPUT_PARAMS = Some(params.output_params);
SPROUT_GROTH16_PARAMS_PATH = sprout_path.map(|p| p.to_owned());
// Caller is responsible for calling this function once, so
// these global mutations are safe.
unsafe {
SAPLING_SPEND_PARAMS = Some(params.spend_params);
SAPLING_OUTPUT_PARAMS = Some(params.output_params);
SPROUT_GROTH16_PARAMS_PATH = sprout_path.map(|p| p.to_owned());
SAPLING_SPEND_VK = Some(params.spend_vk);
SAPLING_OUTPUT_VK = Some(params.output_vk);
SPROUT_GROTH16_VK = params.sprout_vk;
SAPLING_SPEND_VK = Some(params.spend_vk);
SAPLING_OUTPUT_VK = Some(params.output_vk);
SPROUT_GROTH16_VK = params.sprout_vk;
ORCHARD_PK = Some(orchard_pk);
ORCHARD_VK = Some(orchard_vk);
}
ORCHARD_PK = Some(orchard_pk);
ORCHARD_VK = Some(orchard_vk);
}
});
}
/// Writes the "uncommitted" note value for empty leaves of the Merkle tree.

Some files were not shown because too many files have changed in this diff Show More