diff --git a/src/rust/include/rust/orchard/wallet.h b/src/rust/include/rust/orchard/wallet.h index f97191c9b..f4d8dc2aa 100644 --- a/src/rust/include/rust/orchard/wallet.h +++ b/src/rust/include/rust/orchard/wallet.h @@ -95,6 +95,13 @@ bool orchard_wallet_append_bundle_commitments( 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 contains any Orchard notes that belong * to this wallet. diff --git a/src/rust/src/wallet.rs b/src/rust/src/wallet.rs index 2663165fc..624259543 100644 --- a/src/rust/src/wallet.rs +++ b/src/rust/src/wallet.rs @@ -472,6 +472,10 @@ impl Wallet { }) .collect() } + + pub fn note_commitment_tree_root(&self) -> MerkleHashOrchard { + self.witness_tree.root() + } } #[no_mangle] @@ -507,18 +511,23 @@ pub extern "C" fn orchard_wallet_is_checkpointed(wallet: *const Wallet) -> bool #[no_mangle] pub extern "C" fn orchard_wallet_rewind( - wallet: *mut Wallet, + wallet: *mut Wallet, to_height: BlockHeight, - blocks_rewound: *mut u32) -> bool { + blocks_rewound: *mut u32, +) -> bool { let wallet = unsafe { wallet.as_mut() }.expect("Wallet pointer may not be null"); - let blocks_rewound = unsafe { blocks_rewound.as_mut() }.expect("Return value pointer may not be null."); + let blocks_rewound = + unsafe { blocks_rewound.as_mut() }.expect("Return value pointer may not be null."); match wallet.rewind(to_height) { Ok(rewound) => { *blocks_rewound = rewound; true } Err(e) => { - error!("Unable to rewind the wallet to height {:?}: {:?}", to_height, e); + error!( + "Unable to rewind the wallet to height {:?}: {:?}", + to_height, e + ); false } } @@ -559,6 +568,17 @@ pub extern "C" fn orchard_wallet_append_bundle_commitments( true } +#[no_mangle] +pub extern "C" fn orchard_wallet_commitment_tree_root( + wallet: *const Wallet, + root_ret: *mut [u8; 32], +) { + let wallet = unsafe { wallet.as_ref() }.expect("Wallet pointer may not be null"); + let root_ret = unsafe { root_ret.as_mut() }.expect("Cannot return to the null pointer."); + + *root_ret = wallet.note_commitment_tree_root().to_bytes(); +} + #[no_mangle] pub extern "C" fn orchard_wallet_add_spending_key(wallet: *mut Wallet, sk: *const SpendingKey) { let wallet = unsafe { wallet.as_mut() }.expect("Wallet pointer may not be null"); diff --git a/src/wallet/orchard.h b/src/wallet/orchard.h index 574fcc1a3..5649255c5 100644 --- a/src/wallet/orchard.h +++ b/src/wallet/orchard.h @@ -135,6 +135,12 @@ public: return true; } + uint256 GetLatestAnchor() const { + uint256 value; + orchard_wallet_commitment_tree_root(inner.get(), value.begin()); + return value; + } + bool TxContainsMyNotes(const uint256& txid) { return orchard_wallet_tx_contains_my_notes( inner.get(), diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 9d15697e1..371e83b0a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2320,6 +2320,7 @@ void CWallet::IncrementNoteWitnesses( // If we're at or beyond NU5 activation, update the Orchard note commitment tree. if (performOrchardWalletUpdates && consensus.NetworkUpgradeActive(pindex->nHeight, Consensus::UPGRADE_NU5)) { assert(orchardWallet.AppendNoteCommitments(pindex->nHeight, *pblock)); + assert(pindex->hashFinalOrchardRoot == orchardWallet.GetLatestAnchor()); } // Update witness heights @@ -2391,6 +2392,9 @@ void CWallet::DecrementNoteWitnesses(const Consensus::Params& consensus, const C uint32_t blocksRewound{0}; assert(orchardWallet.Rewind(pindex->nHeight - 1, blocksRewound)); assert(blocksRewound == 1); + if (consensus.NetworkUpgradeActive(pindex->nHeight - 1, Consensus::UPGRADE_NU5)) { + assert(pindex->pprev->hashFinalOrchardRoot == orchardWallet.GetLatestAnchor()); + } } // For performance reasons, we write out the witness cache in