Reorganize Sapling and Orchard note commitment tree sizes in CompactBlock.

We move thes fields out into a separate BlockMetadata struct to ensure
that future additions to block metadata are structurally separated from
future additions to block data.
This commit is contained in:
Kris Nuttycombe 2023-06-29 14:28:12 -06:00
parent 547634e210
commit ba709177d3
5 changed files with 75 additions and 32 deletions

View File

@ -10,20 +10,27 @@ option swift_prefix = "";
// Remember that proto3 fields are all optional. A field that is not present will be set to its zero value.
// bytes fields of hashes are in canonical little-endian format.
// BlockMetadata represents information about a block that may not be
// represented directly in the block data, but is instead derived from chain
// data or other external sources.
message BlockMetadata {
uint32 saplingCommitmentTreeSize = 1; // the size of the Sapling note commitment tree as of the end of this block
uint32 orchardCommitmentTreeSize = 2; // the size of the Orchard note commitment tree as of the end of this block
}
// CompactBlock is a packaging of ONLY the data from a block that's needed to:
// 1. Detect a payment to your shielded Sapling address
// 2. Detect a spend of your shielded Sapling notes
// 3. Update your witnesses to generate new Sapling spend proofs.
message CompactBlock {
uint32 protoVersion = 1; // the version of this wire format, for storage
uint64 height = 2; // the height of this block
bytes hash = 3; // the ID (hash) of this block, same as in block explorers
bytes prevHash = 4; // the ID (hash) of this block's predecessor
uint32 time = 5; // Unix epoch time when the block was mined
bytes header = 6; // (hash, prevHash, and time) OR (full header)
repeated CompactTx vtx = 7; // zero or more compact transactions from this block
uint32 saplingCommitmentTreeSize = 8; // the size of the Sapling note commitment tree as of the end of this block
uint32 orchardCommitmentTreeSize = 9; // the size of the Orchard note commitment tree as of the end of this block
uint32 protoVersion = 1; // the version of this wire format, for storage
uint64 height = 2; // the height of this block
bytes hash = 3; // the ID (hash) of this block, same as in block explorers
bytes prevHash = 4; // the ID (hash) of this block's predecessor
uint32 time = 5; // Unix epoch time when the block was mined
bytes header = 6; // (hash, prevHash, and time) OR (full header)
repeated CompactTx vtx = 7; // zero or more compact transactions from this block
BlockMetadata blockMetadata = 8; // information about this block derived from the chain or other sources
}
// CompactTx contains the minimum information for a wallet to know if this transaction

View File

@ -1,3 +1,16 @@
/// BlockMetadata represents information about a block that may not be
/// represented directly in the block data, but is instead derived from chain
/// data or other external sources.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlockMetadata {
/// the size of the Sapling note commitment tree as of the end of this block
#[prost(uint32, tag = "1")]
pub sapling_commitment_tree_size: u32,
/// the size of the Orchard note commitment tree as of the end of this block
#[prost(uint32, tag = "2")]
pub orchard_commitment_tree_size: u32,
}
/// CompactBlock is a packaging of ONLY the data from a block that's needed to:
/// 1. Detect a payment to your shielded Sapling address
/// 2. Detect a spend of your shielded Sapling notes
@ -26,12 +39,9 @@ pub struct CompactBlock {
/// zero or more compact transactions from this block
#[prost(message, repeated, tag = "7")]
pub vtx: ::prost::alloc::vec::Vec<CompactTx>,
/// the size of the Sapling note commitment tree as of the end of this block
#[prost(uint32, tag = "8")]
pub sapling_commitment_tree_size: u32,
/// the size of the Orchard note commitment tree as of the end of this block
#[prost(uint32, tag = "9")]
pub orchard_commitment_tree_size: u32,
/// information about this block derived from the chain or other sources
#[prost(message, optional, tag = "8")]
pub block_metadata: ::core::option::Option<BlockMetadata>,
}
/// CompactTx contains the minimum information for a wallet to know if this transaction
/// is relevant to it (either pays to it or spends from it) via shielded elements

View File

@ -1,3 +1,16 @@
/// BlockMetadata represents information about a block that may not be
/// represented directly in the block data, but is instead derived from chain
/// data or other external sources.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BlockMetadata {
/// the size of the Sapling note commitment tree as of the end of this block
#[prost(uint32, tag = "1")]
pub sapling_commitment_tree_size: u32,
/// the size of the Orchard note commitment tree as of the end of this block
#[prost(uint32, tag = "2")]
pub orchard_commitment_tree_size: u32,
}
/// A BlockID message contains identifiers to select a block: a height or a
/// hash. Specification by hash is not implemented, but may be in the future.
#[allow(clippy::derive_partial_eq_without_eq)]

View File

@ -218,16 +218,21 @@ pub(crate) fn scan_block_with_runner<
// to use it. `block.sapling_commitment_tree_size` is expected to be correct as of the end of
// the block, and we can't have a note of ours in a block with no outputs so treating the zero
// default value from the protobuf as `None` is always correct.
let mut sapling_tree_position = if block.sapling_commitment_tree_size == 0 {
initial_commitment_tree_meta.map(|m| (m.sapling_tree_size() + 1).into())
} else {
let end_position_exclusive = Position::from(u64::from(block.sapling_commitment_tree_size));
let mut sapling_tree_position = if let Some(sapling_tree_size) = block
.block_metadata
.as_ref()
.map(|m| m.sapling_commitment_tree_size)
.filter(|s| *s != 0)
{
let end_position_exclusive = Position::from(u64::from(sapling_tree_size));
let output_count = block
.vtx
.iter()
.map(|tx| u64::try_from(tx.outputs.len()).unwrap())
.sum();
Some(end_position_exclusive - output_count)
} else {
initial_commitment_tree_meta.map(|m| (m.sapling_tree_size() + 1).into())
};
for tx in block.vtx.into_iter() {
@ -404,11 +409,10 @@ pub(crate) fn scan_block_with_runner<
block_hash,
block_time: block.time,
transactions: wtxs,
sapling_commitment_tree_size: if block.sapling_commitment_tree_size == 0 {
None
} else {
Some(block.sapling_commitment_tree_size)
},
sapling_commitment_tree_size: block
.block_metadata
.map(|m| m.sapling_commitment_tree_size)
.filter(|s| *s != 0),
sapling_commitments: sapling_note_commitments,
})
}
@ -439,7 +443,7 @@ mod tests {
use crate::{
data_api::chain::CommitmentTreeMeta,
proto::compact_formats::{
CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx,
BlockMetadata, CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx,
},
scan::BatchRunner,
};
@ -547,8 +551,11 @@ mod tests {
cb.vtx.push(tx);
}
cb.sapling_commitment_tree_size = initial_sapling_tree_size
+ cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::<u32>();
cb.block_metadata = Some(BlockMetadata {
sapling_commitment_tree_size: initial_sapling_tree_size
+ cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::<u32>(),
..Default::default()
});
cb
}

View File

@ -964,7 +964,7 @@ mod tests {
data_api::{WalletRead, WalletWrite},
keys::{sapling, UnifiedFullViewingKey},
proto::compact_formats::{
CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx,
BlockMetadata, CompactBlock, CompactSaplingOutput, CompactSaplingSpend, CompactTx,
},
};
@ -1109,8 +1109,11 @@ mod tests {
};
cb.prev_hash.extend_from_slice(&prev_hash.0);
cb.vtx.push(ctx);
cb.sapling_commitment_tree_size = initial_sapling_tree_size
+ cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::<u32>();
cb.block_metadata = Some(BlockMetadata {
sapling_commitment_tree_size: initial_sapling_tree_size
+ cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::<u32>(),
..Default::default()
});
(cb, note.nf(&dfvk.fvk().vk.nk, 0))
}
@ -1197,8 +1200,11 @@ mod tests {
};
cb.prev_hash.extend_from_slice(&prev_hash.0);
cb.vtx.push(ctx);
cb.sapling_commitment_tree_size = initial_sapling_tree_size
+ cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::<u32>();
cb.block_metadata = Some(BlockMetadata {
sapling_commitment_tree_size: initial_sapling_tree_size
+ cb.vtx.iter().map(|tx| tx.outputs.len() as u32).sum::<u32>(),
..Default::default()
});
cb
}