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(anchorSapling): Change type to force consensus rule validation #3544

Merged
merged 8 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions zebra-chain/src/block/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ impl Commitment {

match NetworkUpgrade::current(network, height) {
Genesis | BeforeOverwinter | Overwinter => Ok(PreSaplingReserved(bytes)),
Sapling | Blossom => Ok(FinalSaplingRoot(sapling::tree::Root(bytes))),
Sapling | Blossom => match sapling::tree::Root::try_from(bytes) {
Ok(root) => Ok(FinalSaplingRoot(root)),
_ => Err(InvalidSapingRootBytes),
},
Heartwood if Some(height) == Heartwood.activation_height(network) => {
if bytes == CHAIN_HISTORY_ACTIVATION_RESERVED {
Ok(ChainHistoryActivationReserved)
Expand All @@ -130,7 +133,7 @@ impl Commitment {

match self {
PreSaplingReserved(bytes) => bytes,
FinalSaplingRoot(hash) => hash.0,
FinalSaplingRoot(hash) => hash.0.into(),
ChainHistoryActivationReserved => CHAIN_HISTORY_ACTIVATION_RESERVED,
ChainHistoryRoot(hash) => hash.0,
ChainHistoryBlockTxAuthCommitment(hash) => hash.0,
Expand Down Expand Up @@ -247,4 +250,7 @@ pub enum CommitmentError {

#[error("missing required block height: block commitments can't be parsed without a block height, block hash: {block_hash:?}")]
MissingBlockHeight { block_hash: block::Hash },

#[error("provided bytes are not a valid sapling root")]
InvalidSapingRootBytes,
}
2 changes: 1 addition & 1 deletion zebra-chain/src/block/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ fn block_commitment(network: Network) {
.expect("unexpected missing final sapling root test vector");
assert_eq!(
final_sapling_root,
expected_final_sapling_root.into(),
crate::sapling::tree::Root::try_from(*expected_final_sapling_root).unwrap(),
"unexpected invalid final sapling root commitment at {} {}",
network,
height
Expand Down
12 changes: 6 additions & 6 deletions zebra-chain/src/history_tree/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ fn push_and_prune_for_network_upgrade(

// Build initial history tree tree with only the first block
let first_sapling_root =
sapling::tree::Root(**sapling_roots.get(&height).expect("test vector exists"));
sapling::tree::Root::try_from(**sapling_roots.get(&height).expect("test vector exists"))?;
let mut tree = NonEmptyHistoryTree::from_block(
network,
first_block,
Expand Down Expand Up @@ -91,11 +91,11 @@ fn push_and_prune_for_network_upgrade(
assert_eq!(second_commitment, Commitment::ChainHistoryRoot(first_root));

// Append second block to history tree
let second_sapling_root = sapling::tree::Root(
let second_sapling_root = sapling::tree::Root::try_from(
**sapling_roots
.get(&(height + 1))
.expect("test vector exists"),
);
)?;
tree.push(second_block, &second_sapling_root, &Default::default())
.unwrap();

Expand Down Expand Up @@ -139,7 +139,7 @@ fn upgrade_for_network_upgrade(network: Network, network_upgrade: NetworkUpgrade
// This tree will not match the actual tree (which has all the blocks since the previous
// network upgrade), so we won't be able to check if its root is correct.
let sapling_root_prev =
sapling::tree::Root(**sapling_roots.get(&height).expect("test vector exists"));
sapling::tree::Root::try_from(**sapling_roots.get(&height).expect("test vector exists"))?;
let mut tree = NonEmptyHistoryTree::from_block(
network,
block_prev,
Expand All @@ -162,11 +162,11 @@ fn upgrade_for_network_upgrade(network: Network, network_upgrade: NetworkUpgrade

// Append block to history tree. This must trigger a upgrade of the tree,
// which should be recreated.
let activation_sapling_root = sapling::tree::Root(
let activation_sapling_root = sapling::tree::Root::try_from(
**sapling_roots
.get(&(height + 1))
.expect("test vector exists"),
);
)?;
tree.push(
activation_block,
&activation_sapling_root,
Expand Down
6 changes: 3 additions & 3 deletions zebra-chain/src/primitives/zcash_history/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn tree_for_network_upgrade(network: Network, network_upgrade: NetworkUpgrade) -

// Build initial MMR tree with only Block 0
let sapling_root0 =
sapling::tree::Root(**sapling_roots.get(&height).expect("test vector exists"));
sapling::tree::Root::try_from(**sapling_roots.get(&height).expect("test vector exists"))?;
let (mut tree, _) =
Tree::<V1>::new_from_block(network, block0, &sapling_root0, &Default::default())?;

Expand All @@ -69,11 +69,11 @@ fn tree_for_network_upgrade(network: Network, network_upgrade: NetworkUpgrade) -
assert_eq!(commitment1, Commitment::ChainHistoryRoot(hash0));

// Append Block to MMR tree
let sapling_root1 = sapling::tree::Root(
let sapling_root1 = sapling::tree::Root::try_from(
**sapling_roots
.get(&(height + 1))
.expect("test vector exists"),
);
)?;
let append = tree
.append_leaf(block1, &sapling_root1, &Default::default())
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion zebra-chain/src/sapling/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl Arbitrary for tree::Root {
(vec(any::<u8>(), 64))
.prop_map(|bytes| {
let bytes = bytes.try_into().expect("vec is the correct length");
jubjub::Fq::from_bytes_wide(&bytes).to_bytes().into()
tree::Root(jubjub::Base::from_bytes_wide(&bytes))
})
.boxed()
}
Expand Down
3 changes: 1 addition & 2 deletions zebra-chain/src/sapling/shielded_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,7 @@ where
// TODO: use TransferData::shared_anchor to improve performance for V5 transactions
self.spends_per_anchor()
.map(|spend| spend.per_spend_anchor)
.sorted()
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
.dedup()
.unique_by(|raw| raw.0.to_bytes())
}

/// Iterate over the [`Spend`]s for this transaction, returning
Expand Down
6 changes: 3 additions & 3 deletions zebra-chain/src/sapling/spend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
},
serialization::{
ReadZcashExt, SerializationError, TrustedPreallocate, WriteZcashExt, ZcashDeserialize,
ZcashSerialize,
ZcashDeserializeInto, ZcashSerialize,
},
};

Expand Down Expand Up @@ -161,7 +161,7 @@ impl Spend<SharedAnchor> {
impl ZcashSerialize for Spend<PerSpendAnchor> {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.cv.zcash_serialize(&mut writer)?;
writer.write_all(&self.per_spend_anchor.0[..])?;
self.per_spend_anchor.zcash_serialize(&mut writer)?;
writer.write_32_bytes(&self.nullifier.into())?;
writer.write_all(&<[u8; 32]>::from(self.rk.clone())[..])?;
self.zkproof.zcash_serialize(&mut writer)?;
Expand Down Expand Up @@ -199,7 +199,7 @@ impl ZcashDeserialize for Spend<PerSpendAnchor> {
// See [`commitment::NotSmallOrderValueCommitment::zcash_deserialize`].
cv: commitment::NotSmallOrderValueCommitment::zcash_deserialize(&mut reader)?,
// Type is `B^{[ℓ_{Sapling}_{Merkle}]}`, i.e. 32 bytes
per_spend_anchor: tree::Root(reader.read_32_bytes()?),
per_spend_anchor: (&mut reader).zcash_deserialize_into()?,
// Type is `B^Y^{[ℓ_{PRFnfSapling}/8]}`, i.e. 32 bytes
nullifier: note::Nullifier::from(reader.read_32_bytes()?),
// Type is `SpendAuthSig^{Sapling}.Public`, i.e. J
Expand Down
6 changes: 3 additions & 3 deletions zebra-chain/src/sapling/tests/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn incremental_roots_with_blocks_for_network(network: Network) -> Result<()> {

// Check if root of the tree of the activation block is correct
let sapling_activation_block_root =
sapling::tree::Root(**sapling_roots.get(&height).expect("test vector exists"));
sapling::tree::Root::try_from(**sapling_roots.get(&height).expect("test vector exists"))?;
assert_eq!(sapling_activation_block_root, tree.root());

// Load the block immediately after Sapling activation (activation + 1)
Expand All @@ -102,11 +102,11 @@ fn incremental_roots_with_blocks_for_network(network: Network) -> Result<()> {
.zcash_deserialize_into::<Block>()
.expect("block is structurally valid"),
);
let block_after_sapling_activation_root = sapling::tree::Root(
let block_after_sapling_activation_root = sapling::tree::Root::try_from(
**sapling_roots
.get(&(height + 1))
.expect("test vector exists"),
);
)?;

// Add note commitments from the block after Sapling activatoin to the tree
let mut appended_count = 0;
Expand Down
74 changes: 57 additions & 17 deletions zebra-chain/src/sapling/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
#![allow(clippy::unit_arg)]
#![allow(dead_code)]

use std::{cell::Cell, fmt};
use std::{
cell::Cell,
fmt,
hash::{Hash, Hasher},
io,
};

use bitvec::prelude::*;
use incrementalmerkletree::{bridgetree, Frontier};
Expand All @@ -22,6 +27,9 @@ use thiserror::Error;

use super::commitment::pedersen_hashes::pedersen_hash;

use crate::serialization::{
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
};
pub(super) const MERKLE_DEPTH: usize = 32;

/// MerkleCRH^Sapling Hash Function
Expand Down Expand Up @@ -81,36 +89,68 @@ pub struct Position(pub(crate) u64);
/// commitment tree corresponding to the final Sapling treestate of
/// this block. A root of a note commitment tree is associated with
/// each treestate.
#[derive(Clone, Copy, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Root(pub [u8; 32]);
#[derive(Clone, Copy, Default, Eq, Serialize, Deserialize)]
pub struct Root(#[serde(with = "serde_helpers::Fq")] pub(crate) jubjub::Base);

impl fmt::Debug for Root {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Root").field(&hex::encode(&self.0)).finish()
f.debug_tuple("Root")
.field(&hex::encode(&self.0.to_bytes()))
.finish()
}
}

impl From<[u8; 32]> for Root {
fn from(bytes: [u8; 32]) -> Root {
Self(bytes)
impl From<Root> for [u8; 32] {
fn from(root: Root) -> Self {
root.0.to_bytes()
}
}

impl From<Root> for [u8; 32] {
fn from(root: Root) -> Self {
root.0
impl From<&Root> for [u8; 32] {
fn from(root: &Root) -> Self {
(*root).into()
}
}

impl From<&[u8; 32]> for Root {
fn from(bytes: &[u8; 32]) -> Root {
(*bytes).into()
impl PartialEq for Root {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}

impl From<&Root> for [u8; 32] {
fn from(root: &Root) -> Self {
(*root).into()
impl Hash for Root {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_bytes().hash(state)
}
}

impl TryFrom<[u8; 32]> for Root {
type Error = SerializationError;

fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
let possible_point = jubjub::Base::from_bytes(&bytes);

if possible_point.is_some().into() {
Ok(Self(possible_point.unwrap()))
} else {
Err(SerializationError::Parse(
"Invalid jubjub::Base value for Sapling note commitment tree root",
))
}
}
}

impl ZcashSerialize for Root {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&<[u8; 32]>::from(*self)[..])?;

Ok(())
}
}

impl ZcashDeserialize for Root {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Self::try_from(reader.read_32_bytes()?)
}
}

Expand Down Expand Up @@ -241,7 +281,7 @@ impl NoteCommitmentTree {
Some(root) => root,
None => {
// Compute root and cache it
let root = Root(self.inner.root().0);
let root = Root::try_from(self.inner.root().0).unwrap();
self.cached_root.replace(Some(root));
root
}
Expand Down
2 changes: 1 addition & 1 deletion zebra-chain/src/transaction/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ impl ZcashDeserialize for Option<sapling::ShieldedData<SharedAnchor>> {
//
// Type is `B^{[ℓ_{Sapling}_{Merkle}]}`, i.e. 32 bytes
let shared_anchor = if spends_count > 0 {
Some(reader.read_32_bytes()?.into())
Some((&mut reader).zcash_deserialize_into()?)
} else {
None
};
Expand Down