Add Chain ValueBalance serialization (#2554)
* add serialization for value balances * change test names * change panic messages * add a deserialization test * return the errors from `from_bytes()` methods * add prop test for serialize/deserialize Amount * Move amount proptests to amount::tests::prop Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
parent
4c4dbfe7cd
commit
910f0ff5dc
|
@ -16,6 +16,12 @@ use std::{
|
|||
use crate::serialization::{ZcashDeserialize, ZcashSerialize};
|
||||
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
#[cfg(any(test, feature = "proptest-impl"))]
|
||||
pub mod arbitrary;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
/// A runtime validated type for representing amounts of zatoshis
|
||||
|
@ -48,6 +54,15 @@ impl<C> Amount<C> {
|
|||
buf
|
||||
}
|
||||
|
||||
/// From little endian byte array
|
||||
pub fn from_bytes(bytes: [u8; 8]) -> Result<Amount<C>>
|
||||
where
|
||||
C: Constraint,
|
||||
{
|
||||
let amount = i64::from_le_bytes(bytes);
|
||||
amount.try_into()
|
||||
}
|
||||
|
||||
/// Create a zero `Amount`
|
||||
pub fn zero() -> Amount<C>
|
||||
where
|
||||
|
@ -452,22 +467,7 @@ impl ZcashDeserialize for Amount<NonNegative> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "proptest-impl"))]
|
||||
use proptest::prelude::*;
|
||||
#[cfg(any(test, feature = "proptest-impl"))]
|
||||
impl<C> Arbitrary for Amount<C>
|
||||
where
|
||||
C: Constraint + std::fmt::Debug,
|
||||
{
|
||||
type Parameters = ();
|
||||
|
||||
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
|
||||
C::valid_range().prop_map(|v| Self(v, PhantomData)).boxed()
|
||||
}
|
||||
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
}
|
||||
|
||||
// TODO: move to tests::vectors after PR #2577 merges
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::serialization::ZcashDeserializeInto;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
//! Randomised test case generation for amounts.
|
||||
|
||||
use proptest::prelude::*;
|
||||
|
||||
use crate::amount::*;
|
||||
|
||||
impl<C> Arbitrary for Amount<C>
|
||||
where
|
||||
C: Constraint + std::fmt::Debug,
|
||||
{
|
||||
type Parameters = ();
|
||||
|
||||
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
|
||||
C::valid_range().prop_map(|v| Self(v, PhantomData)).boxed()
|
||||
}
|
||||
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
//! Tests for amounts
|
||||
|
||||
mod prop;
|
|
@ -0,0 +1,27 @@
|
|||
//! Randomised property tests for amounts.
|
||||
|
||||
use proptest::prelude::*;
|
||||
|
||||
use crate::amount::*;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn amount_serialization(amount in any::<Amount<NegativeAllowed>>()) {
|
||||
zebra_test::init();
|
||||
|
||||
let bytes = amount.to_bytes();
|
||||
let serialized_amount = Amount::<NegativeAllowed>::from_bytes(bytes)?;
|
||||
|
||||
prop_assert_eq!(amount, serialized_amount);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amount_deserialization(bytes in any::<[u8; 8]>()) {
|
||||
zebra_test::init();
|
||||
|
||||
if let Ok(deserialized) = Amount::<NegativeAllowed>::from_bytes(bytes) {
|
||||
let bytes2 = deserialized.to_bytes();
|
||||
prop_assert_eq!(bytes, bytes2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use crate::amount::{Amount, Constraint, Error, NonNegative};
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[cfg(any(test, feature = "proptest-impl"))]
|
||||
mod arbitrary;
|
||||
|
||||
|
@ -128,6 +130,51 @@ where
|
|||
orchard: zero,
|
||||
}
|
||||
}
|
||||
|
||||
/// To byte array
|
||||
pub fn to_bytes(self) -> [u8; 32] {
|
||||
let transparent = self.transparent.to_bytes();
|
||||
let sprout = self.sprout.to_bytes();
|
||||
let sapling = self.sapling.to_bytes();
|
||||
let orchard = self.orchard.to_bytes();
|
||||
match [transparent, sprout, sapling, orchard].concat().try_into() {
|
||||
Ok(bytes) => bytes,
|
||||
_ => unreachable!(
|
||||
"Four [u8; 8] should always concat with no error into a single [u8; 32]"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// From byte array
|
||||
pub fn from_bytes(bytes: [u8; 32]) -> Result<ValueBalance<C>, ValueBalanceError> {
|
||||
let transparent = Amount::from_bytes(
|
||||
bytes[0..8]
|
||||
.try_into()
|
||||
.expect("Extracting the first quarter of a [u8; 32] should always succeed"),
|
||||
)?;
|
||||
let sprout = Amount::from_bytes(
|
||||
bytes[8..16]
|
||||
.try_into()
|
||||
.expect("Extracting the second quarter of a [u8; 32] should always succeed"),
|
||||
)?;
|
||||
let sapling = Amount::from_bytes(
|
||||
bytes[16..24]
|
||||
.try_into()
|
||||
.expect("Extracting the third quarter of a [u8; 32] should always succeed"),
|
||||
)?;
|
||||
let orchard = Amount::from_bytes(
|
||||
bytes[24..32]
|
||||
.try_into()
|
||||
.expect("Extracting the last quarter of a [u8; 32] should always succeed"),
|
||||
)?;
|
||||
|
||||
Ok(ValueBalance {
|
||||
transparent,
|
||||
sprout,
|
||||
sapling,
|
||||
orchard,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
//! Tests for value balances.
|
||||
|
||||
mod prop;
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use crate::{amount::*, value_balance::*};
|
||||
//! Randomised property tests for value balances.
|
||||
|
||||
use proptest::prelude::*;
|
||||
|
||||
use crate::{amount::*, value_balance::*};
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn test_add(
|
||||
fn value_blance_add(
|
||||
value_balance1 in any::<ValueBalance<NegativeAllowed>>(),
|
||||
value_balance2 in any::<ValueBalance<NegativeAllowed>>())
|
||||
{
|
||||
|
@ -32,7 +35,7 @@ proptest! {
|
|||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_sub(
|
||||
fn value_balance_sub(
|
||||
value_balance1 in any::<ValueBalance<NegativeAllowed>>(),
|
||||
value_balance2 in any::<ValueBalance<NegativeAllowed>>())
|
||||
{
|
||||
|
@ -62,7 +65,7 @@ proptest! {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_sum(
|
||||
fn value_balance_sum(
|
||||
value_balance1 in any::<ValueBalance<NegativeAllowed>>(),
|
||||
value_balance2 in any::<ValueBalance<NegativeAllowed>>(),
|
||||
) {
|
||||
|
@ -88,4 +91,24 @@ proptest! {
|
|||
_ => prop_assert!(matches!(collection.iter().sum(), Err(ValueBalanceError::AmountError(_))))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_balance_serialization(value_balance in any::<ValueBalance<NegativeAllowed>>()) {
|
||||
zebra_test::init();
|
||||
|
||||
let bytes = value_balance.to_bytes();
|
||||
let serialized_value_balance = ValueBalance::from_bytes(bytes)?;
|
||||
|
||||
prop_assert_eq!(value_balance, serialized_value_balance);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_balance_deserialization(bytes in any::<[u8; 32]>()) {
|
||||
zebra_test::init();
|
||||
|
||||
if let Ok(deserialized) = ValueBalance::<NegativeAllowed>::from_bytes(bytes) {
|
||||
let bytes2 = deserialized.to_bytes();
|
||||
prop_assert_eq!(bytes, bytes2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::{collections::BTreeMap, convert::TryInto, fmt::Debug, sync::Arc};
|
|||
|
||||
use bincode::Options;
|
||||
use zebra_chain::{
|
||||
amount::NonNegative,
|
||||
block,
|
||||
block::{Block, Height},
|
||||
history_tree::HistoryTree,
|
||||
|
@ -12,6 +13,7 @@ use zebra_chain::{
|
|||
sapling,
|
||||
serialization::{ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
|
||||
sprout, transaction, transparent,
|
||||
value_balance::ValueBalance,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
@ -253,6 +255,21 @@ impl IntoDisk for orchard::tree::Root {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoDisk for ValueBalance<NonNegative> {
|
||||
type Bytes = [u8; 32];
|
||||
|
||||
fn as_bytes(&self) -> Self::Bytes {
|
||||
self.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromDisk for ValueBalance<NonNegative> {
|
||||
fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
|
||||
let array = bytes.as_ref().try_into().unwrap();
|
||||
ValueBalance::from_bytes(array).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// The following implementations for the note commitment trees use `serde` and
|
||||
// `bincode` because currently the inner Merkle tree frontier (from
|
||||
// `incrementalmerkletree`) only supports `serde` for serialization. `bincode`
|
||||
|
@ -526,4 +543,11 @@ mod tests {
|
|||
|
||||
proptest!(|(val in any::<transparent::Utxo>())| assert_value_properties(val));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_value_balance() {
|
||||
zebra_test::init();
|
||||
|
||||
proptest!(|(val in any::<ValueBalance::<NonNegative>>())| assert_value_properties(val));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue