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

pczt: Define the structure and semantics of the PCZT format #1577

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

str4d
Copy link
Contributor

@str4d str4d commented Oct 16, 2024

Todo:

  • Decide on an encoding (might not happen in this PR)

Copy link

codecov bot commented Oct 16, 2024

Codecov Report

Attention: Patch coverage is 0% with 519 lines in your changes missing coverage. Please review.

Project coverage is 54.80%. Comparing base (dd51c2a) to head (37fc6ce).
Report is 71 commits behind head on main.

Files with missing lines Patch % Lines
pczt/src/sapling.rs 0.00% 144 Missing ⚠️
pczt/src/orchard.rs 0.00% 142 Missing ⚠️
pczt/src/transparent.rs 0.00% 79 Missing ⚠️
pczt/src/roles/tx_extractor/mod.rs 0.00% 48 Missing ⚠️
pczt/src/roles/prover/sapling.rs 0.00% 39 Missing ⚠️
pczt/src/roles/combiner/mod.rs 0.00% 31 Missing ⚠️
pczt/src/common.rs 0.00% 18 Missing ⚠️
pczt/src/roles/creator/mod.rs 0.00% 6 Missing ⚠️
pczt/src/roles/tx_extractor/sapling.rs 0.00% 5 Missing ⚠️
...imitives/src/transaction/components/transparent.rs 0.00% 4 Missing ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1577      +/-   ##
==========================================
- Coverage   61.63%   54.80%   -6.83%     
==========================================
  Files         148      158      +10     
  Lines       18836    19581     +745     
==========================================
- Hits        11610    10732     -878     
- Misses       7226     8849    +1623     

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


/// Global fields that are relevant to the transaction as a whole.
#[derive(Clone)]
pub(crate) struct Global {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Keystone's proposal included a network field (indicating mainnet vs testnet vs ?) at the global level, possibly because in my 2019 PCZT draft I suggested it. However, I note that PSBTs (BIP 174) do not include such a field (AFAICT). Is such a field necessary, and/or useful?

Copy link

Choose a reason for hiding this comment

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

I don't see why it would be necessary or useful here, but it looks like BIP 174 does include an implicit indication of the network in the serialized extended public keys.

Copy link

@soralit soralit Oct 28, 2024

Choose a reason for hiding this comment

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

We meet a similar problem on Unisat's Fractal Bitcoin recently. PSBT doesn't have a network field, so we have to set it on the global map field which means it becomes a custom protocol between Unisat and us.
This can be useful if the chain have multiple sub/child chains like Bitcoin vs Litecoin or Dogecoin. In theory Litecoin can also use PSBT, but without network field, we cannot know some accurate info like coin precision.
Anyway, that's depends on Zcash's needs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, this is a good point. And as @arya2 notes above and @pacu notes below, we will already be semi-explicitly encoding the network into the PCZT at some later stage. So I think it makes sense for the Creator to encode the network up-front, and then every later instance of data that includes network information can be checked against it for correctness.

Next question then is what format this network field should have. A SLIP 44 coin_type (as used in BIP 32 and ZIP 32 derivation paths) would make sense for mainnet applications, but SLIP 44 only defines a single testnet code for all coins. Would that be sufficient for Zcash (and potentially other) testnet uses? (I think so, because if you're running against testnet then you already likely know what network you're dealing with, and the important part is that we have domain separation between mainnet and non-mainnet, as well as between Zcash and non-Zcash mainnets.)

Comment on lines +10 to +15
pub(crate) tx_version: u32,
pub(crate) version_group_id: u32,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this pair of fields be optional independently of the others?

Copy link

@arya2 arya2 Oct 16, 2024

Choose a reason for hiding this comment

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

Would it be better to infer the version group id from the tx version instead of having it as an explicit field?

Should this pair of fields be optional independently of the others?

Would it make sense to adding a builder struct where the tx_version field is optional instead of making it optional here?

Update: I see there's already a builder struct, so it seems unnecessary to make the field optional here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it be better to infer the version group id from the tx version instead of having it as an explicit field?

That's more fragile across consensus forks, but I can see an argument that the tuple (tx_version, consensus_branch_id) might be sufficient to fully constrain the value of version_group_id such that every role can reliably deduce it. The main argument against this would be that it would force PCZT roles to be aware of more details about transaction formats, where they might otherwise just be able to focus on the PCZT.

Comment on lines +19 to +24
pub(crate) lock_time: u32,
pub(crate) expiry_height: u32,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should these be independently optional?

Copy link

Choose a reason for hiding this comment

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

It could be nice to type these as LockTime and BlockHeight.

Should these be independently optional?

If this needs to be able to produce v1 or v2 transactions, then at least expiry_height should probably be optional.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@arya2 I'm intentionally not adding types to the core structure (at least at this stage), to keep its dependencies as minimal / non-existent as possible (because there are many roles that do not need to care about every type in the PCZT, e.g. an Orchard signer doesn't need to be able to parse Sapling-specific types). The Transaction Extractor role necessarily performs all conversions and checks, so that acts as a backstop.

Once we've figured out and agreed on the structure, then it may make sense to add certain newtypes that pczt can rely on (depending on who the downstream consumers of the crate will be).

///
/// This is initialized by the Creator, and updated by the Constructor as spends or
/// outputs are added to the PCZT. It enables per-spend and per-output values to be
/// redacted from the PCZT after they are no longer necessary.
Copy link
Contributor

@daira daira Oct 16, 2024

Choose a reason for hiding this comment

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

Maybe calculate this at the same time as bsk (in which case it would be an option of (value_balance, bsk)).

/// This is initialized by the Creator, and updated by the Constructor as spends or
/// outputs are added to the PCZT. It enables per-spend and per-output values to be
/// redacted from the PCZT after they are no longer necessary.
pub(crate) value_balance: u64,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment as Orchard.

pczt/src/lib.rs Show resolved Hide resolved
/// - After`zkproof` / `spend_auth_sig` has been set, this can be redacted.
pub(crate) alpha: Option<[u8; 32]>,

// TODO derivation path
Copy link
Contributor

Choose a reason for hiding this comment

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

wouldn't this account for network information (see comments on top of the PR)

Copy link

@conradoplg conradoplg left a comment

Choose a reason for hiding this comment

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

Looks good, added some comments about FROST usage

pczt/src/orchard.rs Outdated Show resolved Hide resolved
@conradoplg
Copy link

For ZSA issuance I think we need the fields required for computing the issuance_digest?

@str4d
Copy link
Contributor Author

str4d commented Nov 8, 2024

Force-pushed to fill in some more parts:

  • Transparent bundle fields for signing (adapting from BIPs 174 and 370)
  • Optional ZIP 32 paths (that Updaters can set to help Signers determine which keys to use)
  • Decided on semantics for merging data in proprietary maps (equal keys are required to have equal values, same as every other field and map).

@str4d
Copy link
Contributor Author

str4d commented Nov 8, 2024

I'm currently inclined to leave out both FROST support and ZSA issuance support from the first version of PCZTs, as we don't have experience with either of them yet, and trying to get them right at the same time as the base PCZT format is likely to end in sorrow.

As @conradoplg notes, we can in some cases have FROST internally pass around a PCZT, but that won't necessarily help for all possible PCZT use cases, so we'll likely want a v2 that adds those fields. That would also be the right time to adjust the core fields if we find weaknesses in the v1 PCZT format.

@conradoplg
Copy link

I'm happy with leaving FROST out of v1, we can make it work for now by sending additional data along with the PCZT.

@str4d
Copy link
Contributor Author

str4d commented Nov 12, 2024

Force-pushed with more changes:

  • Anchors are now required and set by the Creator.
  • Ciphertexts are now Vec<u8> to accommodate upcoming plaintext changes.
  • Pczt struct no longer contains a Version; instead that will be explicitly part of the encoding.
  • Extracted some conversion logic from the Prover role to use it for consistency checks in the Signer role.

@str4d
Copy link
Contributor Author

str4d commented Nov 26, 2024

Force-pushed:

  • Added an IO Finalizer role, and started on a Constructor role.
  • Noted some bugs in the format that need to be fixed.
  • Started writing an end-to-end test.

Use of the Constructor role interacts heavily with the per-protocol crates, so I'm now working on the production impl of those, both to fix the format bugs and get us to a working client-side implementation.

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.

6 participants