Skip to content

Commit d94102c

Browse files
committed
Add Ironwood wallet migration and policy
1 parent ba30e23 commit d94102c

19 files changed

Lines changed: 2920 additions & 230 deletions

File tree

zcash_client_backend/CHANGELOG.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ workspace.
1717
- Ironwood scanning and tree APIs: `BlockMetadata::ironwood_tree_size`,
1818
`NoteCommitmentTree::Ironwood`, `ScannedBlock::ironwood`, and Ironwood
1919
tree methods on `WalletCommitmentTrees`.
20+
- The NU6.3-gated Orchard to Ironwood migration API:
21+
`data_api::wallet::create_orchard_to_ironwood_transaction` and
22+
`data_api::wallet::MigrationTransaction`.
23+
- `data_api::wallet::create_pczt_from_proposal_with_tx_version`, for creating
24+
a PCZT with an explicitly requested transaction version.
2025
- `zcash_client_backend::wallet::WalletTransparentOutput`:
2126
- `recipient_account`
2227
- `recipient_key_scope`
@@ -50,6 +55,14 @@ workspace.
5055
Tokio-based batch decryption engine for full blocks and transactions.
5156

5257
### Changed
58+
- `zcash_client_backend::fees::orchard::BundleView` no longer exposes a
59+
bundle protocol, because fee and change calculation derives Orchard and
60+
Ironwood action counts from the bundle inputs and outputs.
61+
- `zcash_client_backend::data_api::wallet::propose_send_max_transfer` now
62+
accepts an explicit transaction version under the `unstable` feature, so
63+
callers can request legacy version 5 Orchard send-max proposals after NU6.3.
64+
Its `ProposeSendMaxErrT` selection-error parameter is now
65+
`GreedyInputSelectorError` instead of `BalanceError`.
5366
- `zcash_client_backend::data_api`:
5467
- Changes to the `InputSource` trait:
5568
- The result types of `InputSource::get_unspent_transparent_output` and
@@ -83,11 +96,18 @@ workspace.
8396
- `zcash_client_backend::data_api::wallet::input_selection::ShieldingSelector`
8497
now requires implementors to provide `propose_shielding_coinbase` in
8598
addition to `propose_shielding`.
99+
- `zcash_client_backend::wallet::WalletTx::new` now takes a `transparent_outputs`
100+
argument.
86101
- `zcash_client_backend::scanning::ScanError` variants for invalid encodings
87102
and tree-size failures now report a `NoteCommitmentTree`, so Ironwood scan
88103
failures are labeled separately from Orchard failures.
89-
- `zcash_client_backend::wallet::WalletTx::new` now takes a `transparent_outputs`
90-
argument.
104+
- `data_api::wallet::propose_standard_transfer_to_address` and
105+
`create_pczt_from_proposal_with_tx_version` keep Orchard change in legacy
106+
Orchard form when transaction version 5 is explicitly requested after NU6.3.
107+
108+
### Fixed
109+
- `data_api::wallet::extract_and_store_transaction_from_pczt` now persists
110+
Ironwood sent output metadata added by `create_pczt_from_proposal`.
91111

92112
### Removed
93113
- `zcash_client_backend::data_api::WalletUtxo` (use `WalletTransparentOutput`

zcash_client_backend/src/data_api.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,20 @@ impl AccountBalance {
433433

434434
/// Returns the total value of shielded funds that may immediately be spent.
435435
pub fn spendable_value(&self) -> Zatoshis {
436-
(self.sapling_balance.spendable_value + self.orchard_balance.spendable_value)
437-
.expect("Account balance cannot overflow MAX_MONEY")
436+
let spendable = (self.sapling_balance.spendable_value
437+
+ self.orchard_balance.spendable_value)
438+
.expect("Account balance cannot overflow MAX_MONEY");
439+
440+
#[cfg(zcash_unstable = "nu6.3")]
441+
{
442+
(spendable + self.ironwood_balance.spendable_value)
443+
.expect("Account balance cannot overflow MAX_MONEY")
444+
}
445+
446+
#[cfg(not(zcash_unstable = "nu6.3"))]
447+
{
448+
spendable
449+
}
438450
}
439451

440452
/// Returns the total value of change and/or shielding transaction outputs that are awaiting
@@ -2687,6 +2699,23 @@ impl<AccountId> SentTransactionOutput<AccountId> {
26872699
}
26882700
}
26892701

2702+
/// Constructs a new [`SentTransactionOutput`] with explicit note commitment tree metadata.
2703+
pub(crate) fn from_parts_in_tree(
2704+
note_commitment_tree: Option<NoteCommitmentTree>,
2705+
output_index: usize,
2706+
recipient: Recipient<AccountId>,
2707+
value: Zatoshis,
2708+
memo: Option<MemoBytes>,
2709+
) -> Self {
2710+
Self {
2711+
output_index,
2712+
note_commitment_tree,
2713+
recipient,
2714+
value,
2715+
memo,
2716+
}
2717+
}
2718+
26902719
/// Returns the index within the transaction that contains the recipient output.
26912720
///
26922721
/// - If `recipient_address` is a Sapling address, this is an index into the Sapling

zcash_client_backend/src/data_api/testing.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use zcash_keys::{
2929
keys::{UnifiedAddressRequest, UnifiedFullViewingKey, UnifiedSpendingKey},
3030
};
3131
use zcash_note_encryption::Domain;
32+
#[cfg(feature = "unstable")]
33+
use zcash_primitives::transaction::TxVersion;
3234
use zcash_primitives::{
3335
block::BlockHash,
3436
transaction::{Transaction, TxId, components::sapling::zip212_enforcement, fees::FeeRule},
@@ -1091,6 +1093,43 @@ where
10911093
memo,
10921094
mode,
10931095
confirmations_policy,
1096+
#[cfg(feature = "unstable")]
1097+
None,
1098+
)
1099+
}
1100+
1101+
/// Invokes [`propose_send_max_transfer`] with an explicit transaction version.
1102+
#[cfg(feature = "unstable")]
1103+
#[allow(clippy::type_complexity)]
1104+
#[allow(clippy::too_many_arguments)]
1105+
pub fn propose_send_max_transfer_with_tx_version<FeeRuleT>(
1106+
&mut self,
1107+
spend_from_account: <DbT as InputSource>::AccountId,
1108+
fee_rule: &FeeRuleT,
1109+
to: ZcashAddress,
1110+
memo: Option<MemoBytes>,
1111+
mode: MaxSpendMode,
1112+
confirmations_policy: ConfirmationsPolicy,
1113+
proposed_version: TxVersion,
1114+
) -> Result<
1115+
Proposal<FeeRuleT, <DbT as InputSource>::NoteRef>,
1116+
super::wallet::ProposeSendMaxErrT<DbT, Infallible, FeeRuleT>,
1117+
>
1118+
where
1119+
FeeRuleT: FeeRule + Clone,
1120+
{
1121+
let network = self.network().clone();
1122+
propose_send_max_transfer::<_, _, _, Infallible>(
1123+
self.wallet_mut(),
1124+
&network,
1125+
spend_from_account,
1126+
&[ShieldedProtocol::Sapling, ShieldedProtocol::Orchard],
1127+
fee_rule,
1128+
to,
1129+
memo,
1130+
mode,
1131+
confirmations_policy,
1132+
Some(proposed_version),
10941133
)
10951134
}
10961135

@@ -1139,6 +1178,53 @@ where
11391178
result
11401179
}
11411180

1181+
/// Invokes [`propose_standard_transfer_to_address`] with an explicitly requested
1182+
/// transaction version.
1183+
#[cfg(feature = "unstable")]
1184+
#[allow(clippy::type_complexity)]
1185+
#[allow(clippy::too_many_arguments)]
1186+
pub fn propose_standard_transfer_with_tx_version<CommitmentTreeErrT>(
1187+
&mut self,
1188+
spend_from_account: <DbT as InputSource>::AccountId,
1189+
fee_rule: StandardFeeRule,
1190+
confirmations_policy: ConfirmationsPolicy,
1191+
to: &Address,
1192+
amount: Zatoshis,
1193+
memo: Option<MemoBytes>,
1194+
change_memo: Option<MemoBytes>,
1195+
fallback_change_pool: ShieldedProtocol,
1196+
proposed_version: TxVersion,
1197+
) -> Result<
1198+
Proposal<StandardFeeRule, <DbT as InputSource>::NoteRef>,
1199+
super::wallet::ProposeTransferErrT<
1200+
DbT,
1201+
CommitmentTreeErrT,
1202+
GreedyInputSelector<DbT>,
1203+
SingleOutputChangeStrategy<DbT>,
1204+
>,
1205+
> {
1206+
let network = self.network().clone();
1207+
let result = propose_standard_transfer_to_address::<_, _, CommitmentTreeErrT>(
1208+
self.wallet_mut(),
1209+
&network,
1210+
fee_rule,
1211+
spend_from_account,
1212+
confirmations_policy,
1213+
to,
1214+
amount,
1215+
memo,
1216+
change_memo,
1217+
fallback_change_pool,
1218+
Some(proposed_version),
1219+
);
1220+
1221+
if let Ok(proposal) = &result {
1222+
check_proposal_serialization_roundtrip(self.wallet(), proposal);
1223+
}
1224+
1225+
result
1226+
}
1227+
11421228
/// Invokes [`propose_shielding`] with the given arguments.
11431229
///
11441230
/// [`propose_shielding`]: crate::data_api::wallet::propose_shielding
@@ -1276,6 +1362,39 @@ where
12761362
)
12771363
}
12781364

1365+
/// Invokes [`create_pczt_from_proposal_with_tx_version`] with the given arguments.
1366+
///
1367+
/// [`create_pczt_from_proposal_with_tx_version`]: super::wallet::create_pczt_from_proposal_with_tx_version
1368+
#[cfg(all(feature = "pczt", feature = "unstable"))]
1369+
#[allow(clippy::type_complexity)]
1370+
pub fn create_pczt_from_proposal_with_tx_version<InputsErrT, FeeRuleT, ChangeErrT>(
1371+
&mut self,
1372+
spend_from_account: <DbT as InputSource>::AccountId,
1373+
ovk_policy: OvkPolicy,
1374+
proposal: &Proposal<FeeRuleT, <DbT as InputSource>::NoteRef>,
1375+
proposed_version: TxVersion,
1376+
) -> Result<
1377+
pczt::Pczt,
1378+
super::wallet::CreateErrT<DbT, InputsErrT, FeeRuleT, ChangeErrT, DbT::NoteRef>,
1379+
>
1380+
where
1381+
<DbT as WalletRead>::AccountId: serde::Serialize,
1382+
FeeRuleT: FeeRule,
1383+
{
1384+
use super::wallet::create_pczt_from_proposal_with_tx_version;
1385+
1386+
let network = self.network().clone();
1387+
1388+
create_pczt_from_proposal_with_tx_version(
1389+
self.wallet_mut(),
1390+
&network,
1391+
spend_from_account,
1392+
ovk_policy,
1393+
proposal,
1394+
proposed_version,
1395+
)
1396+
}
1397+
12791398
/// Invokes [`extract_and_store_transaction_from_pczt`] with the given arguments.
12801399
///
12811400
/// [`extract_and_store_transaction_from_pczt`]: super::wallet::extract_and_store_transaction_from_pczt

0 commit comments

Comments
 (0)