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

Refactor Sapling data and use it in V4 #1946

Merged
merged 20 commits into from
Mar 31, 2021
Merged

Conversation

oxarbitrage
Copy link
Contributor

@oxarbitrage oxarbitrage commented Mar 25, 2021

TODO

  • update docs for modified code
  • write docs for new code

See the RFC for details: https://github.com/ZcashFoundation/zebra/blob/main/book/src/dev/rfcs/0010-v5-transaction.md#sapling-changes

Motivation

An important part of the transaction v5 design for Zebra is the support for sapling data in transactions V4 and V5.

I decided to split the design implementation as the Orchard parts depend on an open PR: #1885 while the refactoring needed to implement sapling data in both transaction as described by the design is independent.

Solution

Implement Sapling changes alone and use the new AnchorVariant only for V4 transactions.

The code in this pull request has:

  • Documentation Comments
  • Unit Tests and Property Tests

Review

This is a draft, still need a lot of work that i am aware of but i will like @teor2345 to take a look if possible before the end of their week.

@dconnolly can take a look as well if she haves the time.

Related Issues

Follow Up Work

- move ShieldedData to sapling
- add AnchorVariant
- rename shielded_data to sapling_shielded data in V4
- move value_balance into ShieldedData
- use temporal default PerSpendAnchor for ShieldedData
- update prop tests for new structure
- manually implement PartialEq
- use temporal default PerSpendAnchor for Spend
- make anchor types available from sapling crate
- remove default anchor for ShieldedData
- update serialize
- update v5 prop test strategy
@dconnolly dconnolly added A-rust Area: Updates to Rust code NU-5 Network Upgrade: NU5 specific tasks labels Mar 25, 2021
Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

I would like to review this change in two separate PRs:

  1. Changes to sapling_shielded_data in V4
  2. Adding sapling_shielded_data to V5

That way, we can get the V4 changes merged first.

Please also post screenshots of the spec when serialization or deserialization doesn't work. We need to base our code on the spec, not our tests.

@oxarbitrage oxarbitrage changed the title Refactor Sapling data to use in V4 and V5 Refactor Sapling data to use in V4 Mar 26, 2021
@oxarbitrage oxarbitrage changed the title Refactor Sapling data to use in V4 Refactor Sapling data and use it in V4 Mar 26, 2021
@teor2345 teor2345 requested review from dconnolly and teor2345 March 31, 2021 05:37
@teor2345 teor2345 added this to the 2021 Sprint 6 milestone Mar 31, 2021
Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

Here are the documentation and refactor changes I made:

  • Rename anchor to per_spend_anchor
  • Use nullifiers function directly in non-finalized state
  • Use self.value_balance instead of passing it as an argument
  • Derive Spend PartialEq rather than implementing it manually
  • Derive Copy for tag types
  • Add doc comments for ShieldedData refactor

Here are the bug fixes and tests I added:

  • Add missing fields to ShieldedData PartialEq
  • Add shielded data roundtrip and PartialEq proptests

Here are the features I added:

  • Implement a per-spend anchor compatibility iterator

@teor2345 teor2345 marked this pull request as ready for review March 31, 2021 05:40
@teor2345 teor2345 self-requested a review March 31, 2021 05:45
Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

I am happy with this now.

@teor2345 teor2345 self-requested a review March 31, 2021 05:45
@teor2345 teor2345 merged commit 48a8a7b into ZcashFoundation:main Mar 31, 2021
if let Some(shielded_data) = shielded_data {
for sapling::Spend { nullifier, .. } in shielded_data.spends() {
for nullifier in shielded_data.nullifiers() {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Comment on lines +497 to +499
} => match sapling_shielded_data {
Some(s) => s.value_balance,
None => Amount::try_from(0).unwrap(),
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@@ -439,10 +439,12 @@ impl<'a> SigHasher<'a> {
.personal(ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION)
.to_state();

// TODO: make a generic wrapper in `spends.rs` that does this serialization
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Comment on lines +11 to +31
proptest! {
#[test]
fn shielded_data_roundtrip(shielded in any::<ShieldedData<PerSpendAnchor>>()) {
zebra_test::init();

// shielded data doesn't serialize by itself, so we have to stick it in
// a transaction
let tx = Transaction::V4 {
inputs: Vec::new(),
outputs: Vec::new(),
lock_time: LockTime::min_lock_time(),
expiry_height: block::Height(0),
joinsplit_data: None,
sapling_shielded_data: Some(shielded),
};

let data = tx.zcash_serialize_to_vec().expect("tx should serialize");
let tx_parsed = data.zcash_deserialize_into().expect("randomized tx should deserialize");

prop_assert_eq![tx, tx_parsed];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️

Comment on lines +33 to +67
/// Check that ShieldedData serialization is equal if `shielded1 == shielded2`
#[test]
fn shielded_data_serialize_eq(shielded1 in any::<ShieldedData<PerSpendAnchor>>(), shielded2 in any::<ShieldedData<PerSpendAnchor>>()) {
zebra_test::init();

let shielded_eq = shielded1 == shielded2;

// shielded data doesn't serialize by itself, so we have to stick it in
// a transaction
let tx1 = Transaction::V4 {
inputs: Vec::new(),
outputs: Vec::new(),
lock_time: LockTime::min_lock_time(),
expiry_height: block::Height(0),
joinsplit_data: None,
sapling_shielded_data: Some(shielded1),
};
let tx2 = Transaction::V4 {
inputs: Vec::new(),
outputs: Vec::new(),
lock_time: LockTime::min_lock_time(),
expiry_height: block::Height(0),
joinsplit_data: None,
sapling_shielded_data: Some(shielded2),
};

let data1 = tx1.zcash_serialize_to_vec().expect("tx1 should serialize");
let data2 = tx2.zcash_serialize_to_vec().expect("tx2 should serialize");

if shielded_eq {
prop_assert_eq![data1, data2];
} else {
prop_assert_ne![data1, data2];
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️

self.binding_sig == other.binding_sig
// Now check that all the fields match
self.value_balance == other.value_balance
&& self.shared_anchor == other.shared_anchor
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Comment on lines +121 to +132
/// Returns `Spend<PerSpendAnchor>` regardless of the underlying transaction
/// version, to allow generic verification over V4 and V5 transactions.
///
/// # Correctness
///
/// Do not use this function for serialization.
pub fn spends_per_anchor(&self) -> impl Iterator<Item = Spend<PerSpendAnchor>> + '_ {
self.spends()
.cloned()
.map(move |spend| Spend::<PerSpendAnchor>::from((spend, self.shared_anchor.clone())))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💖

AnchorV: AnchorVariant + Clone,
{
/// The net value of Sapling spend transfers minus output transfers.
pub value_balance: Amount,
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

/// The shared anchor for all `Spend`s in this transaction.
///
/// A type of `()` means "not present in this transaction version".
pub shared_anchor: AnchorV::Shared,
Copy link
Contributor

Choose a reason for hiding this comment

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

let joinsplit_data = OptV4Jsd::zcash_deserialize(&mut reader)?;

use futures::future::Either::*;
let shielded_data = if !shielded_spends.is_empty() {
Some(ShieldedData {
// Arbitraily use a spend for `first`, if both are present
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Comment on lines +294 to +295
// the spends are actually empty here, but we use the
// vec for consistency and readability
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

@dconnolly dconnolly left a comment

Choose a reason for hiding this comment

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

💖

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-rust Area: Updates to Rust code NU-5 Network Upgrade: NU5 specific tasks
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants