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

Parse shielded data in v5 transaction #90

Closed
wants to merge 18 commits into from
Closed

Conversation

oxarbitrage
Copy link
Owner

Motivation

We need to parse V5 shielded data: ZcashFoundation#1829

Solution

Implement serialize, deserialize and tests.

The code in this pull request has:

  • Documentation Comments
  • Property Tests

Review

@teor2345

Related Issues

ZcashFoundation#1829

Follow Up Work

dependabot bot and others added 8 commits April 12, 2021 16:00
Bumps [bincode](https://github.com/servo/bincode) from 1.3.1 to 1.3.3.
- [Release notes](https://github.com/servo/bincode/releases)
- [Commits](bincode-org/bincode@v1.3.1...v1.3.3)

Signed-off-by: dependabot[bot] <support@github.com>
Comment on lines +108 to +119
// shielded data doesn't serialize by itself, so we have to stick it in
// a transaction

// stick `SharedAnchor` shielded data into a v4 transaction
let tx = Transaction::V5 {
inputs: Vec::new(),
outputs: Vec::new(),
lock_time: LockTime::min_lock_time(),
expiry_height: block::Height(0),
sapling_shielded_data: Some(shielded_v5),
rest: Vec::new(),
};

Choose a reason for hiding this comment

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

v5 shielded data should have a serialize implementation, because all its data is continuous.

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

Choose a reason for hiding this comment

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

v5 shielded data should have a serialize implementation, because all its data is continuous.

Copy link

@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.

Looks like a good start, thanks for doing the tests.

Here's what we need to change to match the spec:

  • serialize and deserialize the v5 sapling fields in the correct order

Here's what we need to change to match the design:

  • implement serialize and deserialize on Option<sapling::ShieldedData<SharedAnchor>>, and call that code from Transaction::V5 serialization

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

Choose a reason for hiding this comment

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

v5 shielded data should have a serialize implementation, because all its data is continuous.

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

Choose a reason for hiding this comment

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

v5 shielded data should have a serialize implementation, because all its data is continuous.

@@ -187,8 +188,7 @@ impl ZcashSerialize for Transaction {
None => {}
}
}
// TODO: serialize sapling shielded data according to the V5 transaction spec
#[allow(unused_variables)]

Transaction::V5 {

Choose a reason for hiding this comment

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

Suggested change
Transaction::V5 {
// Serialize Transaction v5 according to the spec.
// See the transaction v5 table in:
// https://zips.z.cash/protocol/nu5.pdf#txnencodingandconsensus
Transaction::V5 {

@@ -200,13 +200,42 @@ impl ZcashSerialize for Transaction {
// Write version 5 and set the fOverwintered bit.

Choose a reason for hiding this comment

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

Let's add some comments to help people follow the spec

Suggested change
// Write version 5 and set the fOverwintered bit.
// header: write version 5 and set the fOverwintered bit.

Comment on lines 204 to 207
lock_time.zcash_serialize(&mut writer)?;
writer.write_u32::<LittleEndian>(expiry_height.0)?;
inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?;
Copy link

@teor2345 teor2345 Apr 14, 2021

Choose a reason for hiding this comment

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

Suggested change
lock_time.zcash_serialize(&mut writer)?;
writer.write_u32::<LittleEndian>(expiry_height.0)?;
inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?;
// when can this transaction be mined?
lock_time.zcash_serialize(&mut writer)?;
writer.write_u32::<LittleEndian>(expiry_height.0)?;
// transparent
inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?;

@@ -335,7 +364,67 @@ impl ZcashDeserialize for Transaction {
let inputs = Vec::zcash_deserialize(&mut reader)?;

Choose a reason for hiding this comment

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

lock_time.zcash_serialize(&mut writer)?;
writer.write_u32::<LittleEndian>(expiry_height.0)?;
inputs.zcash_serialize(&mut writer)?;
outputs.zcash_serialize(&mut writer)?;

// TODO: serialize sapling shielded data according to the V5 transaction spec
match sapling_shielded_data {

Choose a reason for hiding this comment

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

Please follow the field order in the v5 transaction spec.

Spends have 1 count (compactsize), but they are split into 3 separate arrays in 2 different locations.
Outputs have 1 count, but they are split into 2 separate arrays in 2 different locations.

Screen Shot 2021-04-15 at 09 51 18

Choose a reason for hiding this comment

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

Also, we should move the serialize and deserialize implementations to Option<sapling::ShieldedData<SharedAnchor>>, to match the design:
https://github.com/ZcashFoundation/zebra/pull/1996/files#diff-a377e69ec608cdeb4b2c5bf8159152daf73ad6584e960882525828bf79bfdc96R277

So v5 sapling serialization will look like the Sprout JoinSplit serialization, where the code is in a separate function from v5 transaction serialization:
https://github.com/ZcashFoundation/zebra/blob/main/zebra-chain/src/transaction/serialize.rs#L34
https://github.com/ZcashFoundation/zebra/blob/main/zebra-chain/src/transaction/serialize.rs#L124
https://github.com/ZcashFoundation/zebra/blob/main/zebra-chain/src/transaction/serialize.rs#L247

Let's also serialize and deserialize Option<sapling::ShieldedData<SharedAnchor>> for consistency.

@@ -335,7 +364,67 @@ impl ZcashDeserialize for Transaction {
let inputs = Vec::zcash_deserialize(&mut reader)?;
let outputs = Vec::zcash_deserialize(&mut reader)?;

Choose a reason for hiding this comment

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

Please follow the field order in the v5 transaction spec.

Screen Shot 2021-04-15 at 09 51 18

teor2345 and others added 10 commits April 14, 2021 23:16
In general, tests shouldn't depend on debug formatting. But for now,
removing the ' fixes the issue.
In Transaction::V5, Zcash splits some types into multiple arrays, with a
single prefix count before the first array.

Add utility functions for serializing and deserializing the subsequent
arrays, with a paramater for the original array's length.
And fix the test module structure so it is consistent with the rest of
zebra-chain.
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.

2 participants