Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify change strategies, and deprecate or remove fee rules that no longer produce minable transactions #40

Open
wants to merge 9 commits into
base: wallet/multi_output_change_strategy
Choose a base branch
from

Conversation

daira
Copy link

@daira daira commented Oct 21, 2024

  • Remove fixed::FeeRule::standard (which was misleadingly named because fixed fees are not standard).
  • Deprecate fixed::FeeRule::non_standard.
  • Remove zcash_primitives::transaction::fees::StandardFeeRule::{PreZip313,Zip313}.
  • Rename zcash_client_backend::proto::ProposalDecodingError::FeeRuleNotSpecified to FeeRuleNotSupported and use it to report attempted use of the removed rules.
  • Refactor change strategies so that we can use a CommonChangeStrategy that is generic in the fee rule. This should simplify future generalizations of the change strategy.
  • Undo the changes to {fixed,standard,zip317}::SingleOutputChangeStrategy, and deprecate those types' new constructors. They are now implemented as trivial wrappers over CommonChangeStrategy.

nuttycom and others added 9 commits October 20, 2024 13:55
…allet metadata.

In the process this modifies input selection to take the change strategy
as an explicit argument, rather than being wrapped as part of the input
selector.
Co-authored-by: Jack Grigg <[email protected]>
Co-authored-by: Daira-Emma Hopwood <[email protected]>
fixed fees are not standard), and deprecate `fixed::FeeRule::non_standard`.

Signed-off-by: Daira-Emma Hopwood <[email protected]>
…313,Zip313}`.

Rename `zcash_client_backend::proto::ProposalDecodingError::FeeRuleNotSpecified`
to `FeeRuleNotSupported` and use it to report attempted use of the removed
rules.

Signed-off-by: Daira-Emma Hopwood <[email protected]>
that is generic in the fee rule. This should simplify future
generalizations of the change strategy. We also undo the changes to
`{fixed,standard,zip317}::SingleOutputChangeStrategy` in this commit,
and deprecate those types' `new` constructors.

Signed-off-by: Daira-Emma Hopwood <[email protected]>
Comment on lines 123 to 214
/// A change strategy that attempts to split the change value into some number of equal-sized notes
/// as dictated by the included [`SplitPolicy`] value.
pub struct MultiOutputChangeStrategy<I> {
fee_rule: Zip317FeeRule,
change_memo: Option<MemoBytes>,
fallback_change_pool: ShieldedProtocol,
dust_output_policy: DustOutputPolicy,
split_policy: SplitPolicy,
meta_source: PhantomData<I>,
}

impl<I> MultiOutputChangeStrategy<I> {
/// Constructs a new [`MultiOutputChangeStrategy`] with the specified ZIP 317
/// fee parameters, change memo, and change splitting policy.
///
/// This change strategy will fall back to creating a single change output if insufficient
/// change value is available to create notes with at least the minimum value dictated by the
/// split policy.
///
/// - `fallback_change_pool`: the pool to which change will be sent if when more than one
/// shielded pool is enabled via feature flags, and the transaction has no shielded inputs.
/// - `split_policy`: A policy value describing how the change value should be returned as
/// multiple notes.
pub fn new(
fee_rule: Zip317FeeRule,
change_memo: Option<MemoBytes>,
fallback_change_pool: ShieldedProtocol,
dust_output_policy: DustOutputPolicy,
split_policy: SplitPolicy,
) -> Self {
Self {
fee_rule,
change_memo,
fallback_change_pool,
dust_output_policy,
split_policy,
meta_source: PhantomData,
}
}
}

impl<I: InputSource> ChangeStrategy for MultiOutputChangeStrategy<I> {
type FeeRule = Zip317FeeRule;
type Error = Zip317FeeError;
type MetaSource = I;
type WalletMeta = WalletMeta;

fn fee_rule(&self) -> &Self::FeeRule {
&self.fee_rule
}

fn fetch_wallet_meta(
&self,
meta_source: &Self::MetaSource,
account: <Self::MetaSource as InputSource>::AccountId,
exclude: &[<Self::MetaSource as InputSource>::NoteRef],
) -> Result<Self::WalletMeta, <Self::MetaSource as InputSource>::Error> {
meta_source.get_wallet_metadata(account, self.split_policy.min_split_output_size(), exclude)
}

fn compute_balance<P: consensus::Parameters, NoteRefT: Clone>(
&self,
params: &P,
target_height: BlockHeight,
transparent_inputs: &[impl transparent::InputView],
transparent_outputs: &[impl transparent::OutputView],
sapling: &impl sapling_fees::BundleView<NoteRefT>,
#[cfg(feature = "orchard")] orchard: &impl orchard_fees::BundleView<NoteRefT>,
ephemeral_balance: Option<&EphemeralBalance>,
wallet_meta: Option<&Self::WalletMeta>,
) -> Result<TransactionBalance, ChangeError<Self::Error, NoteRefT>> {
let cfg = SinglePoolBalanceConfig::new(
params,
&self.fee_rule,
&self.dust_output_policy,
self.fee_rule.marginal_fee(),
&self.split_policy,
self.fallback_change_pool,
self.fee_rule.marginal_fee(),
self.fee_rule.grace_actions(),
);

single_pool_output_balance(
cfg,
wallet_meta,
target_height,
transparent_inputs,
transparent_outputs,
sapling,
#[cfg(feature = "orchard")]
orchard,
self.change_memo.as_ref(),
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all moved to CommonChangeStrategy. I intentionally used the most non-committal name possible so that we don't have to change it when we (inevitably) generalize again.

params,
&self.fee_rule,
&self.dust_output_policy,
self.fee_rule.default_dust_threshold(),
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that we have to distinguish default_dust_threshold() from marginal_fee() in order to replicate the behaviour of the current fixed fee rule. (There was one test, fees::fixed::tests::dust_change, that failed if we didn't do that.)

@codecov-commenter
Copy link

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

Attention: Patch coverage is 61.76471% with 39 lines in your changes missing coverage. Please review.

Please upload report for BASE (wallet/multi_output_change_strategy@5afea2e). Learn more about missing BASE report.

Files with missing lines Patch % Lines
zcash_client_backend/src/fees/standard.rs 0.00% 12 Missing ⚠️
zcash_client_backend/src/fees.rs 0.00% 7 Missing ⚠️
zcash_client_backend/src/data_api/testing/pool.rs 50.00% 6 Missing ⚠️
zcash_client_backend/src/proto.rs 0.00% 5 Missing ⚠️
zcash_client_backend/src/fees/fixed.rs 0.00% 4 Missing ⚠️
zcash_client_backend/src/fees/zip317.rs 0.00% 4 Missing ⚠️
...ent_backend/src/data_api/wallet/input_selection.rs 66.66% 1 Missing ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@                          Coverage Diff                           @@
##             wallet/multi_output_change_strategy      #40   +/-   ##
======================================================================
  Coverage                                       ?   62.04%           
======================================================================
  Files                                          ?      149           
  Lines                                          ?    19254           
  Branches                                       ?        0           
======================================================================
  Hits                                           ?    11947           
  Misses                                         ?     7307           
  Partials                                       ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -60,6 +50,18 @@ impl super::FeeRule for FeeRule {
) -> Result<NonNegativeAmount, Self::Error> {
Ok(self.fixed_fee)
}

fn default_dust_threshold(&self) -> NonNegativeAmount {
self.fixed_fee
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slightly weird but it is what the previous code did.

@@ -83,7 +83,7 @@ impl FeeRule {
|| p2pkh_standard_input_size > P2PKH_STANDARD_INPUT_SIZE \
|| p2pkh_standard_output_size > P2PKH_STANDARD_OUTPUT_SIZE` \
violates ZIP 317, and might cause transactions built with it to fail. \
This API is likely to be removed. Use `[FeeRule::standard]` instead."
This API is likely to be removed. Use [`FeeRule::standard`] instead."
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm itching to remove this one as well. Pretty please?

@@ -13,8 +13,10 @@ and this library adheres to Rust's notion of
- `WalletSummary::progress`
- `WalletMeta`
- `impl Default for wallet::input_selection::GreedyInputSelector`
- `zcash_client_backend::fees::SplitPolicy`
- `zcash_client_backend::fees::zip317::MultiOutputChangeStrategy`
- `zcash_client_backend::fees::{SplitPolicy, CommonChangeStrategy}`. These
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of CommonChangeStrategy, we should probably call this SinglePoolChangeStrategy as the distinguishing feature is now that it sends change to a single pool.

Copy link
Owner

@nuttycom nuttycom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK on the consolidation, but I have a number of complaint about the type changes being introduced; specifically, DummyMetaSource.

Also the Common and simple names are not helpful.

@@ -907,7 +907,7 @@ pub trait InputSource {
account: Self::AccountId,
min_value: NonNegativeAmount,
exclude: &[Self::NoteRef],
) -> Result<WalletMeta, Self::Error>;
) -> Result<Option<WalletMeta>, Self::Error>;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this change semantically - I guess Ok(None) means that wallet metadata cannot be determined?

let change_memo = "Test change memo".parse::<Memo>().unwrap();
let change_strategy = standard::SingleOutputChangeStrategy::new(
fee_rule,
let change_strategy = CommonChangeStrategy::simple(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I don't like simple as a name for a constructor; it doesn't give me any information about what it actually does.

@@ -506,6 +488,43 @@ pub trait ChangeStrategy {
) -> Result<TransactionBalance, ChangeError<Self::Error, NoteRefT>>;
}

pub struct DummyMetaSource;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I very much dislike this being part of the public API.

};

#[cfg(feature = "orchard")]
use super::orchard as orchard_fees;

/// A change strategy that attempts to split the change value into some number of equal-sized notes
/// as dictated by the included [`SplitPolicy`] value.
pub struct CommonChangeStrategy<I, FR> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct CommonChangeStrategy<I, FR> {
pub struct SinglePoolChangeStrategy<I, FR> {

And related changes elsewhere

/// and a single change output. This is equivalent to [`CommonChangeStrategy::new`] with
/// `dust_output_policy == DustOutputPolicy::default()` and
/// `split_policy == SplitPolicy::single_output()`.
pub fn simple(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn simple(
pub fn single_output(

and affected call sites


impl<I: InputSource, FR: FeeRule + Clone> ChangeStrategy for CommonChangeStrategy<I, FR> {
type FeeRule = FR;
type Error = FR::FeeRuleOrBalanceError;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look like something that should be an associated type; it should not be necessary to modify zcash_primitives to introduce this change. Instead, have an enum that wraps the fee rule error.

Comment on lines +493 to +526
impl InputSource for DummyMetaSource {
type Error = ();
type AccountId = ();
type NoteRef = ();

fn get_spendable_note(
&self,
_txid: &TxId,
_protocol: ShieldedProtocol,
_index: u32,
) -> Result<Option<ReceivedNote<Self::NoteRef, Note>>, Self::Error> {
Err(())
}

fn select_spendable_notes(
&self,
_account: Self::AccountId,
_target_value: NonNegativeAmount,
_sources: &[ShieldedProtocol],
_anchor_height: BlockHeight,
_exclude: &[Self::NoteRef],
) -> Result<SpendableNotes<Self::NoteRef>, Self::Error> {
Err(())
}

fn get_wallet_metadata(
&self,
_account: Self::AccountId,
_min_value: NonNegativeAmount,
_exclude: &[Self::NoteRef],
) -> Result<Option<WalletMeta>, Self::Error> {
Err(())
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NACK on this introduction; a InputSource type that errors on every call is the wrong way here. The PhantomData solution was fine.

Comment on lines +39 to +48

/// Returns the default dust threshold.
fn default_dust_threshold(&self) -> NonNegativeAmount;

/// Returns the marginal fee, i.e. the amount by which the fee increases for each
/// logical action as defined in ZIP 317.
fn marginal_fee(&self) -> NonNegativeAmount;

/// Returns the number of grace actions, or 0 if this rule does not use grace actions.
fn grace_actions(&self) -> usize;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NACK, these are all ZIP 317 specific.

Zip317,
}

impl FeeRule for StandardFeeRule {
type Error = zip317::FeeError;
type FeeRuleOrBalanceError = zip317::FeeError;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NACK on this change; this error type is unused in the zcash_primitives crate and doesn't justify its existence.

Comment on lines -106 to -113
/// Returns the ZIP 317 marginal fee.
pub fn marginal_fee(&self) -> NonNegativeAmount {
self.marginal_fee
}
/// Returns the ZIP 317 number of grace actions
pub fn grace_actions(&self) -> usize {
self.grace_actions
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are intentionally here, instead of in the general trait, because they're concepts specific to ZIP 317.

@nuttycom
Copy link
Owner

nuttycom commented Oct 21, 2024

In general, the way that I would approach this change instead is to deprecate/delete zcash_client_backend::fees::{standard, fixed} but leave the fee infrastructure in zcash_primitives unchanged.

zcash_client_backend is intended to be an opinionated client layer, and I'm fine with the opinion "we only support ZIP 317". zcash_primitives is intended to represent the Zcash protocol; as such, it should not be opinionated in the same fashion.

@nuttycom nuttycom force-pushed the wallet/multi_output_change_strategy branch 2 times, most recently from 4059a29 to 47b1065 Compare October 23, 2024 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants