From c430a0a7d6cc24ef591e5fe872ddb3f7f45c48ac Mon Sep 17 00:00:00 2001 From: howardwu Date: Fri, 15 Oct 2021 17:18:40 -0700 Subject: [PATCH 01/17] Adds serialize and deserialize to Network types --- algorithms/Cargo.toml | 5 ++ algorithms/src/signature/aleo.rs | 3 +- algorithms/src/snark/groth16/mod.rs | 3 +- dpc/src/account/address.rs | 17 +++++++ dpc/src/network/testnet1.rs | 2 + dpc/src/network/testnet2.rs | 2 + dpc/src/record/ciphertext.rs | 7 +-- dpc/src/record/payload.rs | 53 +++++++++++++++++++++- dpc/src/record/record.rs | 14 +++--- dpc/src/traits/network.rs | 42 +++++++++-------- dpc/src/transition/transition.rs | 15 +++--- dpc/src/virtual_machine/amount.rs | 3 +- dpc/src/virtual_machine/event.rs | 3 +- dpc/src/virtual_machine/function_inputs.rs | 3 +- dpc/src/virtual_machine/function_type.rs | 4 +- dpc/src/virtual_machine/operation.rs | 3 +- fields/Cargo.toml | 2 +- 17 files changed, 133 insertions(+), 48 deletions(-) diff --git a/algorithms/Cargo.toml b/algorithms/Cargo.toml index 1cabf71fcd..0000518e0e 100644 --- a/algorithms/Cargo.toml +++ b/algorithms/Cargo.toml @@ -139,6 +139,11 @@ default-features = false [dependencies.rayon] version = "1" +[dependencies.serde] +version = "1.0" +default-features = false +features = ["derive"] + [dependencies.sha2] version = "0.9" default-features = false diff --git a/algorithms/src/signature/aleo.rs b/algorithms/src/signature/aleo.rs index 35c9670a0f..bee57ea112 100644 --- a/algorithms/src/signature/aleo.rs +++ b/algorithms/src/signature/aleo.rs @@ -44,8 +44,9 @@ use snarkvm_utilities::{ use anyhow::Result; use itertools::Itertools; use rand::{CryptoRng, Rng}; +use serde::{Deserialize, Serialize}; -#[derive(Derivative)] +#[derive(Derivative, Serialize, Deserialize)] #[derivative( Copy(bound = "TE: TwistedEdwardsParameters"), Clone(bound = "TE: TwistedEdwardsParameters"), diff --git a/algorithms/src/snark/groth16/mod.rs b/algorithms/src/snark/groth16/mod.rs index 76e08fdccf..918348f082 100644 --- a/algorithms/src/snark/groth16/mod.rs +++ b/algorithms/src/snark/groth16/mod.rs @@ -23,6 +23,7 @@ use snarkvm_fields::{ConstraintFieldError, Field, ToConstraintField}; use snarkvm_r1cs::{Index, LinearCombination}; use snarkvm_utilities::{errors::SerializationError, serialize::*, FromBytes, ToBytes, ToMinimalBits}; +use serde::{Deserialize, Serialize}; use std::io::{ Read, Result as IoResult, @@ -55,7 +56,7 @@ pub use prover::*; pub use verifier::*; /// A proof in the Groth16 SNARK. -#[derive(Clone, Debug, Eq, CanonicalSerialize, CanonicalDeserialize)] +#[derive(Clone, Debug, Eq, Serialize, Deserialize, CanonicalSerialize, CanonicalDeserialize)] pub struct Proof { pub a: E::G1Affine, pub b: E::G2Affine, diff --git a/dpc/src/account/address.rs b/dpc/src/account/address.rs index 1c67fa84c7..34e02e37ba 100644 --- a/dpc/src/account/address.rs +++ b/dpc/src/account/address.rs @@ -20,6 +20,7 @@ use snarkvm_curves::AffineCurve; use snarkvm_utilities::{FromBytes, ToBytes}; use bech32::{self, FromBase32, ToBase32}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt, io::{Read, Result as IoResult, Write}, @@ -180,6 +181,22 @@ impl fmt::Debug for Address { } } +impl Serialize for Address { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_str(self) + } +} + +impl<'de, N: Network> Deserialize<'de> for Address { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(de::Error::custom) + } +} + impl Deref for Address { type Target = ::PublicKey; diff --git a/dpc/src/network/testnet1.rs b/dpc/src/network/testnet1.rs index d265cfb087..3b60af7999 100644 --- a/dpc/src/network/testnet1.rs +++ b/dpc/src/network/testnet1.rs @@ -108,6 +108,7 @@ impl Network for Testnet1 { type InnerSNARKGadget = Groth16VerifierGadget; type OuterSNARK = Groth16>; + type OuterProof = ::Proof; type ProgramSNARK = Groth16>; type ProgramSNARKGadget = Groth16VerifierGadget; @@ -144,6 +145,7 @@ impl Network for Testnet1 { type CommitmentScheme = BHPCommitment; type CommitmentGadget = BHPCommitmentGadget; + type CommitmentRandomness = ::Randomness; type Commitment = ::Output; type CommitmentsTreeCRH = BHPCRH; diff --git a/dpc/src/network/testnet2.rs b/dpc/src/network/testnet2.rs index 2778a62ae9..2bab05c9a3 100644 --- a/dpc/src/network/testnet2.rs +++ b/dpc/src/network/testnet2.rs @@ -112,6 +112,7 @@ impl Network for Testnet2 { type InnerSNARKGadget = Groth16VerifierGadget; type OuterSNARK = Groth16>; + type OuterProof = ::Proof; type ProgramSNARK = MarlinSNARK, FiatShamirAlgebraicSpongeRng>, MarlinTestnet2Mode, ProgramPublicVariables>; type ProgramSNARKGadget = MarlinVerificationGadget, SonicKZG10Gadget>; @@ -148,6 +149,7 @@ impl Network for Testnet2 { type CommitmentScheme = BHPCommitment; type CommitmentGadget = BHPCommitmentGadget; + type CommitmentRandomness = ::Randomness; type Commitment = ::Output; type CommitmentsTreeCRH = BHPCRH; diff --git a/dpc/src/record/ciphertext.rs b/dpc/src/record/ciphertext.rs index 679882ea49..895a9c209c 100644 --- a/dpc/src/record/ciphertext.rs +++ b/dpc/src/record/ciphertext.rs @@ -15,7 +15,7 @@ // along with the snarkVM library. If not, see . use crate::{Address, Network, Payload, Record, ViewKey}; -use snarkvm_algorithms::traits::{CommitmentScheme, EncryptionScheme, CRH}; +use snarkvm_algorithms::traits::{EncryptionScheme, CRH}; use snarkvm_utilities::{ io::{Cursor, Result as IoResult}, marker::PhantomData, @@ -28,8 +28,9 @@ use snarkvm_utilities::{ use anyhow::{anyhow, Result}; use rand::{thread_rng, CryptoRng, Rng}; +use serde::{Deserialize, Serialize}; -#[derive(Derivative)] +#[derive(Derivative, Serialize, Deserialize)] #[derivative( Clone(bound = "N: Network"), Debug(bound = "N: Network"), @@ -93,7 +94,7 @@ impl RecordCiphertext { let payload = Payload::read_le(&mut cursor)?; let program_id = N::ProgramID::read_le(&mut cursor)?; let serial_number_nonce = N::SerialNumber::read_le(&mut cursor)?; - let commitment_randomness = ::Randomness::read_le(&mut cursor)?; + let commitment_randomness = N::CommitmentRandomness::read_le(&mut cursor)?; // Derive the record owner. let owner = Address::from_view_key(&recipient_view_key); diff --git a/dpc/src/record/payload.rs b/dpc/src/record/payload.rs index 128398d38a..9021a0fdd8 100644 --- a/dpc/src/record/payload.rs +++ b/dpc/src/record/payload.rs @@ -16,7 +16,22 @@ use snarkvm_utilities::{FromBytes, ToBytes}; -use std::io::{Read, Result as IoResult, Write}; +use serde::{ + de::{Error as DeserializeError, SeqAccess, Visitor}, + ser::SerializeTuple, + Deserialize, + Deserializer, + Serialize, + Serializer, +}; +use std::{ + fmt::{ + Debug, + Formatter, + {self}, + }, + io::{Read, Result as IoResult, Write}, +}; pub const PAYLOAD_SIZE: usize = 128; @@ -65,6 +80,42 @@ impl FromBytes for Payload { } } +impl Serialize for Payload { + fn serialize(&self, serializer: S) -> Result { + let mut tuple = serializer.serialize_tuple(PAYLOAD_SIZE)?; + for byte in &self.to_bytes_le().expect("Failed to serialize proof") { + tuple.serialize_element(byte)?; + } + tuple.end() + } +} + +impl<'de> Deserialize<'de> for Payload { + fn deserialize>(deserializer: D) -> Result { + struct ArrayVisitor; + + impl<'de> Visitor<'de> for ArrayVisitor { + type Value = Payload; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a valid record payload") + } + + fn visit_seq>(self, mut seq: S) -> Result { + let mut bytes = vec![0; PAYLOAD_SIZE]; + for b in &mut bytes[..] { + *b = seq + .next_element()? + .ok_or_else(|| DeserializeError::custom("could not read bytes"))?; + } + Ok(Payload::read_le(&bytes[..]).expect("Failed to deserialize record payload")) + } + } + + deserializer.deserialize_tuple(PAYLOAD_SIZE, ArrayVisitor) + } +} + impl Default for Payload { fn default() -> Self { Self([0u8; PAYLOAD_SIZE]) diff --git a/dpc/src/record/record.rs b/dpc/src/record/record.rs index 9f6f28e93a..fa5c816d9c 100644 --- a/dpc/src/record/record.rs +++ b/dpc/src/record/record.rs @@ -19,13 +19,14 @@ use snarkvm_algorithms::traits::{CommitmentScheme, PRF}; use snarkvm_utilities::{to_bytes_le, FromBytes, ToBytes, UniformRand}; use rand::{CryptoRng, Rng}; +use serde::{Deserialize, Serialize}; use std::{ fmt, io::{Read, Result as IoResult, Write}, str::FromStr, }; -#[derive(Derivative)] +#[derive(Derivative, Serialize, Deserialize)] #[derivative( Default(bound = "N: Network"), Debug(bound = "N: Network"), @@ -40,7 +41,7 @@ pub struct Record { payload: Payload, program_id: N::ProgramID, serial_number_nonce: N::SerialNumber, - commitment_randomness: ::Randomness, + commitment_randomness: N::CommitmentRandomness, commitment: N::Commitment, } @@ -64,7 +65,7 @@ impl Record { payload: Payload, program_id: N::ProgramID, serial_number_nonce: N::SerialNumber, - commitment_randomness: ::Randomness, + commitment_randomness: N::CommitmentRandomness, ) -> Result { Self::from( owner, @@ -118,7 +119,7 @@ impl Record { payload: Payload, program_id: N::ProgramID, serial_number_nonce: N::SerialNumber, - commitment_randomness: ::Randomness, + commitment_randomness: N::CommitmentRandomness, ) -> Result { // Determine if the record is a dummy. let is_dummy = value == 0 && payload.is_empty() && program_id == *N::noop_program_id(); @@ -178,7 +179,7 @@ impl Record { } /// Returns the randomness used for the commitment. - pub fn commitment_randomness(&self) -> ::Randomness { + pub fn commitment_randomness(&self) -> N::CommitmentRandomness { self.commitment_randomness.clone() } @@ -223,8 +224,7 @@ impl FromBytes for Record { let payload: Payload = FromBytes::read_le(&mut reader)?; let program_id: N::ProgramID = FromBytes::read_le(&mut reader)?; let serial_number_nonce: N::SerialNumber = FromBytes::read_le(&mut reader)?; - let commitment_randomness: ::Randomness = - FromBytes::read_le(&mut reader)?; + let commitment_randomness: N::CommitmentRandomness = FromBytes::read_le(&mut reader)?; Ok(Self::from( owner, diff --git a/dpc/src/traits/network.rs b/dpc/src/traits/network.rs index c00b1c130c..903b0c7c1e 100644 --- a/dpc/src/traits/network.rs +++ b/dpc/src/traits/network.rs @@ -35,11 +35,11 @@ use snarkvm_utilities::{ use anyhow::Result; use rand::{CryptoRng, Rng}; -use serde::Serialize; +use serde::{de::DeserializeOwned, Serialize}; use std::{cell::RefCell, rc::Rc}; #[rustfmt::skip] -pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Serialize + Send + Sync { +pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Send + Sync { const NETWORK_ID: u16; const NETWORK_NAME: &'static str; @@ -76,7 +76,8 @@ pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Serialize + Send + type InnerSNARKGadget: SNARKVerifierGadget; /// SNARK for proof-verification checks. - type OuterSNARK: SNARK>; + type OuterSNARK: SNARK, Proof = Self::OuterProof>; + type OuterProof: Clone + Debug + ToBytes + FromBytes + PartialEq + Eq + Serialize + DeserializeOwned + Sync + Send; /// SNARK for Aleo programs. type ProgramSNARK: SNARK, ProvingKey = Self::ProgramProvingKey, VerifyingKey = Self::ProgramVerifyingKey, Proof = Self::ProgramProof, UniversalSetupConfig = usize>; @@ -102,55 +103,56 @@ pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Serialize + Send + + SignatureSchemeOperations; type AccountSignatureGadget: SignatureGadget; type AccountSignaturePublicKey: ToConstraintField + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; - type AccountSignature: Clone + Debug + Default + ToBytes + FromBytes + Send + Sync + PartialEq + Eq; + type AccountSignature: Clone + Debug + Default + ToBytes + FromBytes + Serialize + DeserializeOwned + Send + Sync + PartialEq + Eq; /// CRH schemes for the block hash. Invoked only over `Self::InnerScalarField`. type BlockHashCRH: CRH; type BlockHashCRHGadget: CRHGadget; - type BlockHash: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + PartialEq + Eq + Hash + Sync + Send; + type BlockHash: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// Masked Merkle tree for the block header root on Proof of Succinct Work (PoSW). Invoked only over `Self::InnerScalarField`. type BlockHeaderTreeCRH: CRH; type BlockHeaderTreeCRHGadget: MaskedCRHGadget<::H, Self::InnerScalarField, OutputGadget = >::Seed>; type BlockHeaderTreeParameters: MaskedMerkleParameters; - type BlockHeaderRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type BlockHeaderRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// CRH scheme for encrypted record ID. Invoked only over `Self::InnerScalarField`. type CiphertextIDCRH: CRH; type CiphertextIDCRHGadget: CRHGadget; - type CiphertextID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type CiphertextID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// Commitment scheme for records. Invoked only over `Self::InnerScalarField`. - type CommitmentScheme: CommitmentScheme; + type CommitmentScheme: CommitmentScheme; type CommitmentGadget: CommitmentGadget; - type Commitment: ToConstraintField + Copy + Clone + Debug + Display + Default + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type CommitmentRandomness: Copy + Clone + Debug + Display + Default + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + UniformRand + Sync + Send; + type Commitment: ToConstraintField + Copy + Clone + Debug + Display + Default + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// Merkle tree scheme for the commitments root on the ledger. Invoked only over `Self::InnerScalarField`. type CommitmentsTreeCRH: CRH; type CommitmentsTreeCRHGadget: CRHGadget; type CommitmentsTreeParameters: MerkleParameters; - type CommitmentsRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type CommitmentsRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// CRH for deriving function IDs. Invoked only over `Self::OuterScalarField`. type FunctionIDCRH: CRH; type FunctionIDCRHGadget: CRHGadget; - type FunctionID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type FunctionID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// Crypto hash for deriving the function inputs digest. Invoked only over `Self::InnerScalarField`. type FunctionInputsCRH: CRH; type FunctionInputsCRHGadget: CRHGadget; - type FunctionInputsDigest: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type FunctionInputsDigest: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// CRH for hash of the `Self::InnerSNARK` verifying keys. Invoked only over `Self::OuterScalarField`. type InnerCircuitIDCRH: CRH; type InnerCircuitIDCRHGadget: CRHGadget; - type InnerCircuitID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type InnerCircuitID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// Merkle tree scheme for the local commitments root in transitions. Invoked only over `Self::InnerScalarField`. type LocalCommitmentsTreeCRH: CRH; type LocalCommitmentsTreeCRHGadget: CRHGadget; type LocalCommitmentsTreeParameters: MerkleParameters; - type LocalCommitmentsRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type LocalCommitmentsRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// Schemes for PoSW. Invoked only over `Self::InnerScalarField`. type PoSWMaskPRF: PRF, Seed = Self::BlockHeaderRoot, Output = Self::InnerScalarField>; @@ -161,32 +163,32 @@ pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Serialize + Send + type ProgramFunctionsTreeCRH: CRH; type ProgramFunctionsTreeCRHGadget: CRHGadget; type ProgramFunctionsTreeParameters: MerkleParameters; - type ProgramID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type ProgramID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// PRF for computing serial numbers. Invoked only over `Self::InnerScalarField`. // TODO (howardwu): TEMPORARY - Revisit Vec after upgrading serial number construction. type SerialNumberPRF: PRF, Seed = Self::InnerScalarField, Output = Self::SerialNumber>; type SerialNumberPRFGadget: PRFGadget; - type SerialNumber: ToConstraintField + Copy + Clone + Debug + Display + Default + ToBytes + FromBytes + UniformRand + PartialEq + Eq + Hash + Sync + Send; + type SerialNumber: ToConstraintField + Copy + Clone + Debug + Display + Default + ToBytes + FromBytes + Serialize + DeserializeOwned + UniformRand + PartialEq + Eq + Hash + Sync + Send; /// Merkle tree scheme for the serial numbers root. Invoked only over `Self::InnerScalarField`. type SerialNumbersTreeCRH: CRH; type SerialNumbersTreeParameters: MerkleParameters; - type SerialNumbersRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type SerialNumbersRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// CRH scheme for computing the transaction ID. Invoked only over `Self::InnerScalarField`. type TransactionIDCRH: CRH; - type TransactionID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type TransactionID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// CRH scheme for computing the block transactions root. Invoked only over `Self::InnerScalarField`. type TransactionsTreeCRH: CRH; type TransactionsTreeParameters: MerkleParameters; - type TransactionsRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type TransactionsRoot: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; /// CRH scheme for computing the transition ID. Invoked only over `Self::InnerScalarField`. type TransitionIDCRH: CRH; type TransitionIDCRHGadget: CRHGadget; - type TransitionID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + PartialEq + Eq + Hash + Sync + Send; + type TransitionID: ToConstraintField + Copy + Clone + Default + Debug + Display + ToBytes + FromBytes + Serialize + DeserializeOwned + PartialEq + Eq + Hash + Sync + Send; fn account_encryption_scheme() -> &'static Self::AccountEncryptionScheme; fn account_signature_scheme() -> &'static Self::AccountSignatureScheme; diff --git a/dpc/src/transition/transition.rs b/dpc/src/transition/transition.rs index 5a13c4683d..ff02fe7942 100644 --- a/dpc/src/transition/transition.rs +++ b/dpc/src/transition/transition.rs @@ -19,12 +19,13 @@ use snarkvm_algorithms::traits::{CRH, SNARK}; use snarkvm_utilities::{to_bytes_le, FromBytes, ToBytes}; use anyhow::Result; +use serde::{Deserialize, Serialize}; use std::{ fmt, io::{Read, Result as IoResult, Write}, }; -#[derive(Derivative)] +#[derive(Derivative, Serialize, Deserialize)] #[derivative( Clone(bound = "N: Network"), Debug(bound = "N: Network"), @@ -49,17 +50,13 @@ pub struct Transition { /// The events emitted from this transition. events: Vec>, /// The zero-knowledge proof attesting to the validity of this transition. - proof: ::Proof, + proof: N::OuterProof, } impl Transition { /// Initializes a new instance of a transition. #[inline] - pub(crate) fn from( - request: &Request, - response: &Response, - proof: ::Proof, - ) -> Result { + pub(crate) fn from(request: &Request, response: &Response, proof: N::OuterProof) -> Result { // Fetch the block hash, local commitments root, and serial numbers. let block_hash = request.block_hash(); let local_commitments_root = request.local_commitments_root(); @@ -201,7 +198,7 @@ impl Transition { /// Returns a reference to the transition proof. #[inline] - pub fn proof(&self) -> &::Proof { + pub fn proof(&self) -> &N::OuterProof { &self.proof } @@ -264,7 +261,7 @@ impl FromBytes for Transition { events.push(FromBytes::read_le(&mut reader)?); } - let proof: ::Proof = FromBytes::read_le(&mut reader)?; + let proof: N::OuterProof = FromBytes::read_le(&mut reader)?; let transition_id = Self::compute_transition_id( block_hash, diff --git a/dpc/src/virtual_machine/amount.rs b/dpc/src/virtual_machine/amount.rs index 2f5bae3253..b8e5aa8339 100644 --- a/dpc/src/virtual_machine/amount.rs +++ b/dpc/src/virtual_machine/amount.rs @@ -16,13 +16,14 @@ use snarkvm_utilities::{FromBytes, ToBytes}; +use serde::{Deserialize, Serialize}; use std::{ fmt, io::{Read, Result as IoResult, Write}, }; /// Represents the amount of ALEOs. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct AleoAmount(pub i64); pub enum Denomination { diff --git a/dpc/src/virtual_machine/event.rs b/dpc/src/virtual_machine/event.rs index 38fb049372..67c2e29681 100644 --- a/dpc/src/virtual_machine/event.rs +++ b/dpc/src/virtual_machine/event.rs @@ -17,9 +17,10 @@ use crate::{Network, Operation}; use snarkvm_utilities::{FromBytes, ToBytes}; +use serde::{Deserialize, Serialize}; use std::io::{Read, Result as IoResult, Write}; -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum Event { /// Emits publicly-visible arbitrary data. Custom(Vec), diff --git a/dpc/src/virtual_machine/function_inputs.rs b/dpc/src/virtual_machine/function_inputs.rs index 371812a6ee..099fdf42a9 100644 --- a/dpc/src/virtual_machine/function_inputs.rs +++ b/dpc/src/virtual_machine/function_inputs.rs @@ -20,12 +20,13 @@ use snarkvm_fields::{ConstraintFieldError, ToConstraintField}; use snarkvm_utilities::{FromBytes, ToBytes}; use anyhow::Result; +use serde::{Deserialize, Serialize}; use std::{ io::{Read, Result as IoResult, Write}, marker::PhantomData, }; -#[derive(Derivative)] +#[derive(Derivative, Serialize, Deserialize)] #[derivative( Copy(bound = "N: Network"), Clone(bound = "N: Network"), diff --git a/dpc/src/virtual_machine/function_type.rs b/dpc/src/virtual_machine/function_type.rs index 711dc883e9..876b63307d 100644 --- a/dpc/src/virtual_machine/function_type.rs +++ b/dpc/src/virtual_machine/function_type.rs @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with the snarkVM library. If not, see . -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +use serde::{Deserialize, Serialize}; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum FunctionType { Noop, Add, diff --git a/dpc/src/virtual_machine/operation.rs b/dpc/src/virtual_machine/operation.rs index 0409bd2874..013acdba94 100644 --- a/dpc/src/virtual_machine/operation.rs +++ b/dpc/src/virtual_machine/operation.rs @@ -19,12 +19,13 @@ use snarkvm_fields::{ConstraintFieldError, ToConstraintField}; use snarkvm_utilities::{FromBytes, ToBytes}; use anyhow::Result; +use serde::{Deserialize, Serialize}; use std::io::{Read, Result as IoResult, Write}; type Caller = Address; type Recipient = Address; -#[derive(Derivative)] +#[derive(Derivative, Serialize, Deserialize)] #[derivative( Clone(bound = "N: Network"), Debug(bound = "N: Network"), diff --git a/fields/Cargo.toml b/fields/Cargo.toml index 1a784f4069..ea75ec03bb 100644 --- a/fields/Cargo.toml +++ b/fields/Cargo.toml @@ -40,7 +40,7 @@ version = "0.3" default-features = false [dependencies.serde] -version = "1.0.130" +version = "1.0" default-features = false features = [ "derive" ] From d509fe5ea943170c28b5300468678d930f1d0f55 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 00:59:23 -0700 Subject: [PATCH 02/17] Adds serializers for ToBytes/FromBytes, adds tests for address, record, and payload --- Cargo.lock | 2 + dpc/Cargo.toml | 11 +- dpc/src/account/address.rs | 71 +++++++++-- dpc/src/circuits/inner_circuit.rs | 2 +- dpc/src/errors/record.rs | 9 ++ dpc/src/network/testnet1.rs | 4 + dpc/src/network/testnet2.rs | 4 + dpc/src/record/payload.rs | 167 ++++++++++++++++---------- dpc/src/record/record.rs | 187 ++++++++++++++++++++++++++--- dpc/src/record/tests.rs | 4 +- dpc/src/traits/network.rs | 4 + dpc/src/transaction/transaction.rs | 4 +- dpc/src/transition/transition.rs | 25 ++++ dpc/src/virtual_machine/output.rs | 6 +- fields/src/macros.rs | 59 +++------ utilities/Cargo.toml | 4 + utilities/src/bytes.rs | 62 ++++++++++ 17 files changed, 473 insertions(+), 152 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2474d29a1..3134f21f20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1683,6 +1683,7 @@ dependencies = [ "rand_chacha", "rayon", "serde", + "serde_json", "snarkvm-algorithms", "snarkvm-curves", "snarkvm-fields", @@ -1861,6 +1862,7 @@ dependencies = [ "num-bigint", "rand", "rand_xorshift", + "serde", "snarkvm-derives", "thiserror", ] diff --git a/dpc/Cargo.toml b/dpc/Cargo.toml index c05e2dd79d..80969dcf2f 100644 --- a/dpc/Cargo.toml +++ b/dpc/Cargo.toml @@ -91,9 +91,6 @@ version = "0.2" [dependencies.bech32] version = "0.8" -[dependencies.bincode] -version = "1.3" - [dependencies.chrono] version = "0.4" features = [ "serde" ] @@ -118,11 +115,17 @@ version = "1" [dependencies.serde] version = "1.0" -features = [ "derive" ] +features = ["derive"] + +[dependencies.serde_json] +version = "1.0" [dependencies.thiserror] version = "1.0" +[dev-dependencies.bincode] +version = "1.3" + [dev-dependencies.criterion] version = "0.3.5" diff --git a/dpc/src/account/address.rs b/dpc/src/account/address.rs index 34e02e37ba..f225c98888 100644 --- a/dpc/src/account/address.rs +++ b/dpc/src/account/address.rs @@ -17,7 +17,7 @@ use crate::{account_format, AccountError, ComputeKey, Network, PrivateKey, ViewKey}; use snarkvm_algorithms::{EncryptionScheme, SignatureScheme}; use snarkvm_curves::AffineCurve; -use snarkvm_utilities::{FromBytes, ToBytes}; +use snarkvm_utilities::{FromBytes, FromBytesDeserializer, ToBytes, ToBytesSerializer}; use bech32::{self, FromBase32, ToBase32}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; @@ -160,10 +160,8 @@ impl FromStr for Address { impl fmt::Display for Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Write the encryption key to a buffer. - let mut encryption_key = [0u8; 32]; - self.write_le(&mut encryption_key[0..32]) - .expect("Failed to write encryption key as bytes"); + // Convert the encryption key to bytes. + let encryption_key = self.to_bytes_le().expect("Failed to write encryption key as bytes"); bech32::encode( &account_format::ADDRESS_PREFIX.to_string(), @@ -183,17 +181,19 @@ impl fmt::Debug for Address { impl Serialize for Address { fn serialize(&self, serializer: S) -> Result { - serializer.collect_str(self) + match serializer.is_human_readable() { + true => serializer.collect_str(self), + false => ToBytesSerializer::serialize(self, serializer), + } } } impl<'de, N: Network> Deserialize<'de> for Address { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - FromStr::from_str(&s).map_err(de::Error::custom) + fn deserialize>(deserializer: D) -> Result { + match deserializer.is_human_readable() { + true => FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom), + false => FromBytesDeserializer::::deserialize(deserializer, "address", N::ADDRESS_SIZE_IN_BYTES), + } } } @@ -204,3 +204,50 @@ impl Deref for Address { &self.0 } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::testnet2::Testnet2; + + use rand::thread_rng; + + #[test] + fn test_serde_json() { + let rng = &mut thread_rng(); + + let private_key = PrivateKey::new(rng); + let expected_address: Address = private_key.into(); + + // Serialize + let expected_string = &expected_address.to_string(); + let candidate_string = serde_json::to_string(&expected_address).unwrap(); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!(expected_address, Address::from_str(&candidate_string).unwrap()); + assert_eq!(expected_address, serde_json::from_str(&candidate_string).unwrap()); + } + + #[test] + fn test_bincode() { + let rng = &mut thread_rng(); + + let private_key = PrivateKey::new(rng); + let expected_address: Address = private_key.into(); + + // Serialize + let expected_bytes = expected_address.to_bytes_le().unwrap(); + assert_eq!(&expected_bytes[..], &bincode::serialize(&expected_address).unwrap()[..]); + + // Deserialize + assert_eq!(expected_address, Address::read_le(&expected_bytes[..]).unwrap()); + assert_eq!(expected_address, bincode::deserialize(&expected_bytes[..]).unwrap()); + } +} diff --git a/dpc/src/circuits/inner_circuit.rs b/dpc/src/circuits/inner_circuit.rs index 3f90f42ac9..40e637cd0d 100644 --- a/dpc/src/circuits/inner_circuit.rs +++ b/dpc/src/circuits/inner_circuit.rs @@ -140,7 +140,7 @@ impl ConstraintSynthesizer for InnerCircuit // Declares a constant for a 0 value in a record. let zero_value = UInt8::constant_vec(&(0u64).to_bytes_le()?); // Declares a constant for an empty payload in a record. - let empty_payload = UInt8::constant_vec(&Payload::default().to_bytes_le()?); + let empty_payload = UInt8::constant_vec(&Payload::::default().to_bytes_le()?); // Declare the noop program ID as bytes. let noop_program_id_bytes = UInt8::constant_vec(&N::noop_program_id().to_bytes_le()?); diff --git a/dpc/src/errors/record.rs b/dpc/src/errors/record.rs index 9abfee88a5..b0e21a19b1 100644 --- a/dpc/src/errors/record.rs +++ b/dpc/src/errors/record.rs @@ -42,6 +42,9 @@ pub enum RecordError { #[error("Given compute key does not correspond to the record owner")] IncorrectComputeKey, + #[error("Invalid commitment. Expected {}, found {}", _0, _1)] + InvalidCommitment(String, String), + #[error("{}", _0)] PRFError(#[from] PRFError), @@ -49,6 +52,12 @@ pub enum RecordError { SignatureError(#[from] SignatureError), } +impl From for RecordError { + fn from(error: serde_json::Error) -> Self { + RecordError::Crate("serde_json::Error", format!("{:?}", error)) + } +} + impl From for RecordError { fn from(error: std::io::Error) -> Self { RecordError::Crate("std::io", format!("{:?}", error)) diff --git a/dpc/src/network/testnet1.rs b/dpc/src/network/testnet1.rs index 3b60af7999..5747da6ba6 100644 --- a/dpc/src/network/testnet1.rs +++ b/dpc/src/network/testnet1.rs @@ -84,6 +84,10 @@ impl Network for Testnet1 { const NUM_INPUT_RECORDS: usize = 2; const NUM_OUTPUT_RECORDS: usize = 2; + const ADDRESS_SIZE_IN_BYTES: usize = 32; + const RECORD_SIZE_IN_BYTES: usize = 280; + const PAYLOAD_SIZE_IN_BYTES: usize = 128; + const POSW_PROOF_SIZE_IN_BYTES: usize = 771; const POSW_NUM_LEAVES: usize = 8; const POSW_TREE_DEPTH: usize = 3; diff --git a/dpc/src/network/testnet2.rs b/dpc/src/network/testnet2.rs index 2bab05c9a3..99dfd091e0 100644 --- a/dpc/src/network/testnet2.rs +++ b/dpc/src/network/testnet2.rs @@ -88,6 +88,10 @@ impl Network for Testnet2 { const NUM_INPUT_RECORDS: usize = 2; const NUM_OUTPUT_RECORDS: usize = 2; + const ADDRESS_SIZE_IN_BYTES: usize = 32; + const RECORD_SIZE_IN_BYTES: usize = 280; + const PAYLOAD_SIZE_IN_BYTES: usize = 128; + const POSW_PROOF_SIZE_IN_BYTES: usize = 771; const POSW_NUM_LEAVES: usize = 8; const POSW_TREE_DEPTH: usize = 3; diff --git a/dpc/src/record/payload.rs b/dpc/src/record/payload.rs index 9021a0fdd8..34d398f435 100644 --- a/dpc/src/record/payload.rs +++ b/dpc/src/record/payload.rs @@ -14,129 +14,164 @@ // You should have received a copy of the GNU General Public License // along with the snarkVM library. If not, see . -use snarkvm_utilities::{FromBytes, ToBytes}; - -use serde::{ - de::{Error as DeserializeError, SeqAccess, Visitor}, - ser::SerializeTuple, - Deserialize, - Deserializer, - Serialize, - Serializer, -}; +use crate::{Network, RecordError}; +use snarkvm_utilities::{FromBytes, FromBytesDeserializer, ToBytes, ToBytesSerializer}; + +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::{ - fmt::{ - Debug, - Formatter, - {self}, - }, + fmt, io::{Read, Result as IoResult, Write}, + marker::PhantomData, + str::FromStr, }; -pub const PAYLOAD_SIZE: usize = 128; - #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct Payload([u8; PAYLOAD_SIZE]); +pub struct Payload(Vec, PhantomData); -impl Payload { +impl Payload { pub fn from(bytes: &[u8]) -> Self { - assert!(bytes.len() <= PAYLOAD_SIZE); + assert!(bytes.len() <= N::PAYLOAD_SIZE_IN_BYTES); // Pad the bytes up to PAYLOAD_SIZE. let mut buffer = bytes.to_vec(); - buffer.resize(PAYLOAD_SIZE, 0u8); + buffer.resize(N::PAYLOAD_SIZE_IN_BYTES, 0u8); - // Copy exactly PAYLOAD_SIZE. - let mut payload = [0u8; PAYLOAD_SIZE]; - payload.copy_from_slice(&buffer); - - Self(payload) + Self(buffer, PhantomData) } pub fn is_empty(&self) -> bool { - self.0 == [0u8; PAYLOAD_SIZE] + let mut payload = vec![0u8; N::PAYLOAD_SIZE_IN_BYTES]; + payload.copy_from_slice(&self.0); + payload == vec![0u8; N::PAYLOAD_SIZE_IN_BYTES] + } + + pub fn size() -> usize { + N::PAYLOAD_SIZE_IN_BYTES } pub fn as_any(&self) -> &dyn std::any::Any { self } +} - pub const fn size(&self) -> usize { - PAYLOAD_SIZE +impl FromBytes for Payload { + #[inline] + fn read_le(mut reader: R) -> IoResult { + let mut buffer = vec![0u8; N::PAYLOAD_SIZE_IN_BYTES]; + reader.read_exact(&mut buffer)?; + Ok(Self::from(&buffer)) } } -impl ToBytes for Payload { +impl ToBytes for Payload { #[inline] fn write_le(&self, mut writer: W) -> IoResult<()> { self.0.write_le(&mut writer) } } -impl FromBytes for Payload { - #[inline] - fn read_le(mut reader: R) -> IoResult { - Ok(Self(FromBytes::read_le(&mut reader)?)) +impl FromStr for Payload { + type Err = RecordError; + + fn from_str(payload_hex: &str) -> Result { + Ok(Self::read_le(&hex::decode(payload_hex)?[..])?) } } -impl Serialize for Payload { +impl fmt::Display for Payload { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let bytes = self.to_bytes_le().expect("Failed to convert payload to bytes"); + write!(f, "{}", hex::encode(bytes)) + } +} + +impl Serialize for Payload { fn serialize(&self, serializer: S) -> Result { - let mut tuple = serializer.serialize_tuple(PAYLOAD_SIZE)?; - for byte in &self.to_bytes_le().expect("Failed to serialize proof") { - tuple.serialize_element(byte)?; + match serializer.is_human_readable() { + true => serializer.collect_str(self), + false => ToBytesSerializer::serialize(self, serializer), } - tuple.end() } } -impl<'de> Deserialize<'de> for Payload { +impl<'de, N: Network> Deserialize<'de> for Payload { fn deserialize>(deserializer: D) -> Result { - struct ArrayVisitor; - - impl<'de> Visitor<'de> for ArrayVisitor { - type Value = Payload; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("a valid record payload") - } - - fn visit_seq>(self, mut seq: S) -> Result { - let mut bytes = vec![0; PAYLOAD_SIZE]; - for b in &mut bytes[..] { - *b = seq - .next_element()? - .ok_or_else(|| DeserializeError::custom("could not read bytes"))?; - } - Ok(Payload::read_le(&bytes[..]).expect("Failed to deserialize record payload")) - } + match deserializer.is_human_readable() { + true => FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom), + false => FromBytesDeserializer::::deserialize(deserializer, "payload", N::PAYLOAD_SIZE_IN_BYTES), } - - deserializer.deserialize_tuple(PAYLOAD_SIZE, ArrayVisitor) } } -impl Default for Payload { +impl Default for Payload { fn default() -> Self { - Self([0u8; PAYLOAD_SIZE]) + Self::from(&[]) } } #[cfg(test)] mod tests { use super::*; + use crate::testnet2::Testnet2; use snarkvm_utilities::UniformRand; + use rand::thread_rng; + #[test] fn test_payload_from() { - let rng = &mut rand::thread_rng(); + let rng = &mut thread_rng(); // Create a random byte array, construct a payload from it, and check its byte array matches. - for i in 0..PAYLOAD_SIZE { + for i in 0..Testnet2::PAYLOAD_SIZE_IN_BYTES { let expected_payload = (0..i).map(|_| u8::rand(rng)).collect::>(); - let candidate_payload = Payload::from(&expected_payload).to_bytes_le().unwrap(); + let candidate_payload = Payload::::from(&expected_payload).to_bytes_le().unwrap(); assert_eq!(expected_payload, candidate_payload[0..i]); - assert_eq!(vec![0u8; PAYLOAD_SIZE - i], candidate_payload[i..]); + assert_eq!(vec![0u8; Testnet2::PAYLOAD_SIZE_IN_BYTES - i], candidate_payload[i..]); } } + + #[test] + fn test_serde_json() { + let rng = &mut thread_rng(); + + let expected_payload = Payload::::from( + &(0..Testnet2::PAYLOAD_SIZE_IN_BYTES) + .map(|_| u8::rand(rng)) + .collect::>(), + ); + + // Serialize + let expected_string = &expected_payload.to_string(); + let candidate_string = serde_json::to_string(&expected_payload).unwrap(); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!(expected_payload, Payload::from_str(&expected_string).unwrap()); + assert_eq!(expected_payload, serde_json::from_str(&candidate_string).unwrap()); + } + + #[test] + fn test_bincode() { + let rng = &mut thread_rng(); + + let expected_payload = Payload::::from( + &(0..Testnet2::PAYLOAD_SIZE_IN_BYTES) + .map(|_| u8::rand(rng)) + .collect::>(), + ); + + // Serialize + let expected_bytes = expected_payload.to_bytes_le().unwrap(); + assert_eq!(&expected_bytes[..], &bincode::serialize(&expected_payload).unwrap()[..]); + + // Deserialize + assert_eq!(expected_payload, Payload::read_le(&expected_bytes[..]).unwrap()); + assert_eq!(expected_payload, bincode::deserialize(&expected_bytes[..]).unwrap()); + } } diff --git a/dpc/src/record/record.rs b/dpc/src/record/record.rs index fa5c816d9c..1dd63104c7 100644 --- a/dpc/src/record/record.rs +++ b/dpc/src/record/record.rs @@ -16,17 +16,17 @@ use crate::{Address, ComputeKey, Network, Payload, RecordError}; use snarkvm_algorithms::traits::{CommitmentScheme, PRF}; -use snarkvm_utilities::{to_bytes_le, FromBytes, ToBytes, UniformRand}; +use snarkvm_utilities::{to_bytes_le, FromBytes, FromBytesDeserializer, ToBytes, ToBytesSerializer, UniformRand}; use rand::{CryptoRng, Rng}; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt, io::{Read, Result as IoResult, Write}, str::FromStr, }; -#[derive(Derivative, Serialize, Deserialize)] +#[derive(Derivative)] #[derivative( Default(bound = "N: Network"), Debug(bound = "N: Network"), @@ -38,7 +38,7 @@ pub struct Record { owner: Address, // TODO (raychu86) use AleoAmount which will guard the value range value: u64, - payload: Payload, + payload: Payload, program_id: N::ProgramID, serial_number_nonce: N::SerialNumber, commitment_randomness: N::CommitmentRandomness, @@ -51,7 +51,7 @@ impl Record { Self::new_input( owner, 0, - Payload::default(), + Payload::::default(), *N::noop_program_id(), UniformRand::rand(rng), UniformRand::rand(rng), @@ -62,7 +62,7 @@ impl Record { pub fn new_input( owner: Address, value: u64, - payload: Payload, + payload: Payload, program_id: N::ProgramID, serial_number_nonce: N::SerialNumber, commitment_randomness: N::CommitmentRandomness, @@ -86,7 +86,7 @@ impl Record { Self::new_output( owner, 0, - Payload::default(), + Payload::::default(), *N::noop_program_id(), serial_number_nonce, rng, @@ -97,7 +97,7 @@ impl Record { pub fn new_output( owner: Address, value: u64, - payload: Payload, + payload: Payload, program_id: N::ProgramID, serial_number_nonce: N::SerialNumber, rng: &mut R, @@ -112,11 +112,10 @@ impl Record { ) } - #[allow(clippy::too_many_arguments)] pub fn from( owner: Address, value: u64, - payload: Payload, + payload: Payload, program_id: N::ProgramID, serial_number_nonce: N::SerialNumber, commitment_randomness: N::CommitmentRandomness, @@ -164,7 +163,7 @@ impl Record { } /// Returns the record payload. - pub fn payload(&self) -> &Payload { + pub fn payload(&self) -> &Payload { &self.payload } @@ -221,7 +220,7 @@ impl FromBytes for Record { fn read_le(mut reader: R) -> IoResult { let owner: Address = FromBytes::read_le(&mut reader)?; let value: u64 = FromBytes::read_le(&mut reader)?; - let payload: Payload = FromBytes::read_le(&mut reader)?; + let payload: Payload = FromBytes::read_le(&mut reader)?; let program_id: N::ProgramID = FromBytes::read_le(&mut reader)?; let serial_number_nonce: N::SerialNumber = FromBytes::read_le(&mut reader)?; let commitment_randomness: N::CommitmentRandomness = FromBytes::read_le(&mut reader)?; @@ -241,16 +240,170 @@ impl FromStr for Record { type Err = RecordError; fn from_str(record: &str) -> Result { - Ok(Self::read_le(&hex::decode(record)?[..])?) + let record = serde_json::Value::from_str(record)?; + let commitment: N::Commitment = serde_json::from_value(record["commitment"].clone())?; + + // Recover the record. + let record = Self::from( + serde_json::from_value(record["owner"].clone())?, + serde_json::from_value(record["value"].clone())?, + serde_json::from_value(record["payload"].clone())?, + serde_json::from_value(record["program_id"].clone())?, + serde_json::from_value(record["serial_number_nonce"].clone())?, + serde_json::from_value(record["commitment_randomness"].clone())?, + )?; + + // Ensure the commitment matches. + match commitment == record.commitment() { + true => Ok(record), + false => Err(RecordError::InvalidCommitment( + commitment.to_string(), + record.commitment().to_string(), + )), + } } } impl fmt::Display for Record { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}", - hex::encode(to_bytes_le![self].expect("serialization to bytes failed")) + let record = serde_json::json!({ + "owner": self.owner, + "value": self.value, + "payload": self.payload, + "program_id": self.program_id, + "serial_number_nonce": self.serial_number_nonce, + "commitment_randomness": self.commitment_randomness, + "commitment": self.commitment + }); + write!(f, "{}", record) + } +} + +impl Serialize for Record { + fn serialize(&self, serializer: S) -> Result { + match serializer.is_human_readable() { + true => serializer.collect_str(self), + false => ToBytesSerializer::serialize(self, serializer), + } + } +} + +impl<'de, N: Network> Deserialize<'de> for Record { + fn deserialize>(deserializer: D) -> Result { + match deserializer.is_human_readable() { + true => FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom), + false => FromBytesDeserializer::::deserialize(deserializer, "record", N::RECORD_SIZE_IN_BYTES), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{testnet2::Testnet2, Address, PrivateKey}; + use snarkvm_utilities::UniformRand; + + use rand::thread_rng; + + #[test] + fn test_serde_json_noop() { + let rng = &mut thread_rng(); + let address: Address = PrivateKey::new(rng).into(); + + // Noop output record + let expected_record = Record::new_noop_output(address, UniformRand::rand(rng), rng).unwrap(); + + // Serialize + let expected_string = &expected_record.to_string(); + let candidate_string = serde_json::to_string(&expected_record).unwrap(); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!(expected_record, Record::from_str(&expected_string).unwrap()); + assert_eq!(expected_record, serde_json::from_str(&candidate_string).unwrap()); + } + + #[test] + fn test_serde_json() { + let rng = &mut thread_rng(); + let address: Address = PrivateKey::new(rng).into(); + + // Output record + let mut payload = [0u8; Testnet2::PAYLOAD_SIZE_IN_BYTES]; + rng.fill(&mut payload); + let expected_record = Record::new_output( + address, + 1234, + Payload::from_bytes_le(&payload).unwrap(), + *Testnet2::noop_program_id(), + UniformRand::rand(rng), + rng, ) + .unwrap(); + + // Serialize + let expected_string = &expected_record.to_string(); + let candidate_string = serde_json::to_string(&expected_record).unwrap(); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!(expected_record, Record::from_str(&expected_string).unwrap()); + assert_eq!(expected_record, serde_json::from_str(&candidate_string).unwrap()); + } + + #[test] + fn test_bincode_noop() { + let rng = &mut thread_rng(); + let address: Address = PrivateKey::new(rng).into(); + + // Noop output record + let expected_record = Record::new_noop_output(address, UniformRand::rand(rng), rng).unwrap(); + + // Serialize + let expected_bytes = expected_record.to_bytes_le().unwrap(); + assert_eq!(&expected_bytes[..], &bincode::serialize(&expected_record).unwrap()[..]); + + // Deserialize + assert_eq!(expected_record, Record::read_le(&expected_bytes[..]).unwrap()); + assert_eq!(expected_record, bincode::deserialize(&expected_bytes[..]).unwrap()); + } + + #[test] + fn test_bincode() { + let rng = &mut thread_rng(); + let address: Address = PrivateKey::new(rng).into(); + + // Output record + let mut payload = [0u8; Testnet2::PAYLOAD_SIZE_IN_BYTES]; + rng.fill(&mut payload); + let expected_record = Record::new_output( + address, + 1234, + Payload::from_bytes_le(&payload).unwrap(), + *Testnet2::noop_program_id(), + UniformRand::rand(rng), + rng, + ) + .unwrap(); + + // Serialize + let expected_bytes = expected_record.to_bytes_le().unwrap(); + assert_eq!(&expected_bytes[..], &bincode::serialize(&expected_record).unwrap()[..]); + + // Deserialize + assert_eq!(expected_record, Record::read_le(&expected_bytes[..]).unwrap()); + assert_eq!(expected_record, bincode::deserialize(&expected_bytes[..]).unwrap()); } } diff --git a/dpc/src/record/tests.rs b/dpc/src/record/tests.rs index fe4b155289..c68e1c6266 100644 --- a/dpc/src/record/tests.rs +++ b/dpc/src/record/tests.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the snarkVM library. If not, see . -use crate::{record::*, testnet2::*, Account, AccountScheme, Network, Payload, Record, ViewKey, PAYLOAD_SIZE}; +use crate::{record::*, testnet2::*, Account, AccountScheme, Network, Payload, Record, ViewKey}; use snarkvm_utilities::{FromBytes, UniformRand}; use rand::{Rng, SeedableRng}; @@ -30,7 +30,7 @@ fn test_record_ciphertext() { let account = Account::::new(rng); let value = rng.gen(); - let mut payload = [0u8; PAYLOAD_SIZE]; + let mut payload = [0u8; Testnet2::PAYLOAD_SIZE_IN_BYTES]; rng.fill(&mut payload); let expected_record = Record::new_input( diff --git a/dpc/src/traits/network.rs b/dpc/src/traits/network.rs index 903b0c7c1e..d7a389b064 100644 --- a/dpc/src/traits/network.rs +++ b/dpc/src/traits/network.rs @@ -47,6 +47,10 @@ pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Send + Sync { const NUM_INPUT_RECORDS: usize; const NUM_OUTPUT_RECORDS: usize; const NUM_TOTAL_RECORDS: usize = Self::NUM_INPUT_RECORDS + Self::NUM_OUTPUT_RECORDS; + + const ADDRESS_SIZE_IN_BYTES: usize; + const PAYLOAD_SIZE_IN_BYTES: usize; + const RECORD_SIZE_IN_BYTES: usize; const POSW_PROOF_SIZE_IN_BYTES: usize; const POSW_NUM_LEAVES: usize; diff --git a/dpc/src/transaction/transaction.rs b/dpc/src/transaction/transaction.rs index 6bf5bb86cc..de748eb594 100644 --- a/dpc/src/transaction/transaction.rs +++ b/dpc/src/transaction/transaction.rs @@ -328,7 +328,7 @@ impl Hash for Transaction { #[cfg(test)] mod tests { use super::*; - use crate::{testnet2::Testnet2, Account, AccountScheme, Payload, PAYLOAD_SIZE}; + use crate::{testnet2::Testnet2, Account, AccountScheme, Payload}; use snarkvm_utilities::UniformRand; use rand::{Rng, SeedableRng}; @@ -341,7 +341,7 @@ mod tests { let dummy_account = Account::::new(rng); // Construct output records - let mut payload = [0u8; PAYLOAD_SIZE]; + let mut payload = [0u8; Testnet2::PAYLOAD_SIZE_IN_BYTES]; rng.fill(&mut payload); let record = Record::new_output( dummy_account.address(), diff --git a/dpc/src/transition/transition.rs b/dpc/src/transition/transition.rs index ff02fe7942..0ebe4b44e6 100644 --- a/dpc/src/transition/transition.rs +++ b/dpc/src/transition/transition.rs @@ -301,3 +301,28 @@ impl ToBytes for Transition { self.proof.write_le(&mut writer) } } +// +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::testnet2::Testnet2; +// +// use rand::thread_rng; +// +// #[test] +// fn test_bincode() { +// let rng = &mut thread_rng(); +// +// let transaction = Testnet2::genesis_block().to_coinbase_transaction().unwrap(); +// let expected_transition = transaction.transitions().first().unwrap().clone(); +// +// let expected_bytes = expected_transition.to_bytes_le().unwrap(); +// assert_eq!( +// &expected_bytes[..], +// &bincode::serialize(&expected_transition).unwrap()[..] +// ); +// +// assert_eq!(expected_transition, Transition::read_le(&expected_bytes[..]).unwrap()); +// assert_eq!(expected_transition, bincode::deserialize(&expected_bytes[..]).unwrap()); +// } +// } diff --git a/dpc/src/virtual_machine/output.rs b/dpc/src/virtual_machine/output.rs index 06ab22570c..48329b6c18 100644 --- a/dpc/src/virtual_machine/output.rs +++ b/dpc/src/virtual_machine/output.rs @@ -28,7 +28,7 @@ pub struct Output { /// The balance of the recipient. value: AleoAmount, /// The program data of the recipient. - payload: Payload, + payload: Payload, /// The program that was run. program_id: N::ProgramID, } @@ -46,7 +46,7 @@ impl Output { pub fn new( address: Address, value: AleoAmount, - payload: Payload, + payload: Payload, program_id: Option, ) -> Result { // Retrieve the program ID. If `None` is provided, construct the noop program ID. @@ -95,7 +95,7 @@ impl Output { } /// Returns a reference to the payload. - pub fn payload(&self) -> &Payload { + pub fn payload(&self) -> &Payload { &self.payload } diff --git a/fields/src/macros.rs b/fields/src/macros.rs index 12df8495bf..56a5ff5936 100644 --- a/fields/src/macros.rs +++ b/fields/src/macros.rs @@ -238,58 +238,27 @@ macro_rules! impl_primefield_serializer { } impl serde::Serialize for $field

{ - fn serialize(&self, s: S) -> Result - where - S: serde::ser::Serializer, - { - use serde::ser::SerializeTuple; - - let len = self.serialized_size(); - let mut bytes = Vec::with_capacity(len); - CanonicalSerialize::serialize(self, &mut bytes).map_err(serde::ser::Error::custom)?; - - let mut tup = s.serialize_tuple(len)?; - for byte in &bytes { - tup.serialize_element(byte)?; + fn serialize(&self, serializer: S) -> Result { + match serializer.is_human_readable() { + true => serializer.collect_str(self), + false => snarkvm_utilities::ToBytesSerializer::serialize(self, serializer), } - tup.end() } } impl<'de, P: $params> serde::Deserialize<'de> for $field

{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct SerVisitor

(std::marker::PhantomData

); - - impl<'de, P: $params> serde::de::Visitor<'de> for SerVisitor

{ - type Value = $field

; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a valid field element") - } - - fn visit_seq(self, mut seq: S) -> Result - where - S: serde::de::SeqAccess<'de>, - { - let len = ::SERIALIZED_SIZE; - let bytes: Vec = (0..len) - .map(|_| { - seq.next_element()? - .ok_or_else(|| serde::de::Error::custom("could not read bytes")) - }) - .collect::, _>>()?; - - let res = - CanonicalDeserialize::deserialize(&mut &bytes[..]).map_err(serde::de::Error::custom)?; - Ok(res) + fn deserialize>(deserializer: D) -> Result { + match deserializer.is_human_readable() { + true => { + let s: String = serde::Deserialize::deserialize(deserializer)?; + core::str::FromStr::from_str(&s).map_err(serde::de::Error::custom) } + false => snarkvm_utilities::FromBytesDeserializer::::deserialize( + deserializer, + "field", + Self::SERIALIZED_SIZE, + ), } - - let visitor = SerVisitor(std::marker::PhantomData); - deserializer.deserialize_tuple(Self::SERIALIZED_SIZE, visitor) } } }; diff --git a/utilities/Cargo.toml b/utilities/Cargo.toml index a1f208e303..0384be0508 100644 --- a/utilities/Cargo.toml +++ b/utilities/Cargo.toml @@ -36,6 +36,10 @@ version = "0.8" default-features = false features = [ "std_rng" ] +[dependencies.serde] +version = "1.0" +default-features = false + [dependencies.thiserror] version = "1.0" diff --git a/utilities/src/bytes.rs b/utilities/src/bytes.rs index 1bceb7db70..997a14bbbd 100644 --- a/utilities/src/bytes.rs +++ b/utilities/src/bytes.rs @@ -16,9 +16,17 @@ use crate::{ error, + fmt, io::{Read, Result as IoResult, Write}, + marker::PhantomData, Vec, }; +use serde::{ + de::{self, SeqAccess, Visitor}, + ser::{self, SerializeTuple}, + Deserializer, + Serializer, +}; #[inline] pub fn from_bytes_le_to_bits_le(bytes: &[u8]) -> impl Iterator + DoubleEndedIterator + '_ { @@ -108,6 +116,60 @@ pub trait FromBytes { } } +pub struct ToBytesSerializer(String, Option, PhantomData); + +impl ToBytesSerializer { + /// Serializes a static-sized byte array (without length encoding). + pub fn serialize(object: &T, serializer: S) -> Result { + let bytes = object.to_bytes_le().map_err(ser::Error::custom)?; + let mut tuple = serializer.serialize_tuple(bytes.len())?; + for byte in &bytes { + tuple.serialize_element(byte)?; + } + tuple.end() + } +} + +pub struct FromBytesDeserializer(String, Option, PhantomData); + +impl<'de, T: FromBytes> FromBytesDeserializer { + /// Deserializes a static-sized byte array (without length encoding). + pub fn deserialize>(deserializer: D, name: &str, size: usize) -> Result { + deserializer.deserialize_tuple(size, FromBytesVisitor::::new_with_size(name, size)) + } +} + +struct FromBytesVisitor(String, Option, PhantomData); + +impl<'de, T: FromBytes> FromBytesVisitor { + fn new(name: &str) -> Self { + Self(name.to_string(), None, PhantomData) + } + + fn new_with_size(name: &str, size: usize) -> Self { + Self(name.to_string(), Some(size), PhantomData) + } +} + +impl<'de, T: FromBytes> Visitor<'de> for FromBytesVisitor { + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&format!("a valid {} ", self.0)) + } + + fn visit_seq>(self, mut seq: S) -> Result { + let mut buffer = match self.1 { + Some(size) => Vec::with_capacity(size), + None => Vec::with_capacity(32), + }; + while let Some(byte) = seq.next_element()? { + buffer.push(byte); + } + FromBytes::read_le(&buffer[..]).map_err(de::Error::custom) + } +} + macro_rules! to_bytes_for_int_array { ($int:ty) => { impl ToBytes for [$int; N] { From 32ac16bca66bfabe2416bc4d01c6ae2df3522847 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 03:29:43 -0700 Subject: [PATCH 03/17] Update transition and resample genesis --- dpc/src/account/address.rs | 13 +- dpc/src/network/testnet1.rs | 4 +- dpc/src/network/testnet2.rs | 6 +- dpc/src/record/ciphertext.rs | 137 +++++++++-- dpc/src/traits/network.rs | 2 + dpc/src/transaction/transaction.rs | 56 +++-- dpc/src/transition/transition.rs | 215 +++++++++++++----- dpc/src/virtual_machine/function_inputs.rs | 1 + dpc/src/virtual_machine/function_type.rs | 2 +- dpc/src/virtual_machine/virtual_machine.rs | 13 +- parameters/src/testnet1/genesis/block.rs | 2 +- .../src/testnet1/genesis/block_header.genesis | Bin 919 -> 919 bytes .../testnet1/genesis/transaction_1.genesis | Bin 1187 -> 1183 bytes .../src/testnet1/genesis/transaction_1.rs | 2 +- parameters/src/testnet2/genesis/block.rs | 2 +- .../src/testnet2/genesis/block_header.genesis | Bin 919 -> 919 bytes .../testnet2/genesis/transaction_1.genesis | Bin 1187 -> 1183 bytes .../src/testnet2/genesis/transaction_1.rs | 2 +- 18 files changed, 341 insertions(+), 116 deletions(-) diff --git a/dpc/src/account/address.rs b/dpc/src/account/address.rs index f225c98888..a3dcc2302e 100644 --- a/dpc/src/account/address.rs +++ b/dpc/src/account/address.rs @@ -17,17 +17,20 @@ use crate::{account_format, AccountError, ComputeKey, Network, PrivateKey, ViewKey}; use snarkvm_algorithms::{EncryptionScheme, SignatureScheme}; use snarkvm_curves::AffineCurve; -use snarkvm_utilities::{FromBytes, FromBytesDeserializer, ToBytes, ToBytesSerializer}; - -use bech32::{self, FromBase32, ToBase32}; -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -use std::{ +use snarkvm_utilities::{ fmt, io::{Read, Result as IoResult, Write}, ops::Deref, str::FromStr, + FromBytes, + FromBytesDeserializer, + ToBytes, + ToBytesSerializer, }; +use bech32::{self, FromBase32, ToBase32}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + #[derive(Derivative)] #[derivative( Default(bound = "N: Network"), diff --git a/dpc/src/network/testnet1.rs b/dpc/src/network/testnet1.rs index 5747da6ba6..dbb06e5200 100644 --- a/dpc/src/network/testnet1.rs +++ b/dpc/src/network/testnet1.rs @@ -80,13 +80,15 @@ impl Network for Testnet1 { const NETWORK_ID: u16 = 1u16; const NETWORK_NAME: &'static str = "testnet1"; - const NUM_EVENTS: usize = 1024; + const NUM_EVENTS: usize = 512; const NUM_INPUT_RECORDS: usize = 2; const NUM_OUTPUT_RECORDS: usize = 2; const ADDRESS_SIZE_IN_BYTES: usize = 32; + const CIPHERTEXT_SIZE_IN_BYTES: usize = 320; const RECORD_SIZE_IN_BYTES: usize = 280; const PAYLOAD_SIZE_IN_BYTES: usize = 128; + const TRANSITION_SIZE_IN_BYTES: usize = 1129; const POSW_PROOF_SIZE_IN_BYTES: usize = 771; const POSW_NUM_LEAVES: usize = 8; diff --git a/dpc/src/network/testnet2.rs b/dpc/src/network/testnet2.rs index 99dfd091e0..0247f6fcc9 100644 --- a/dpc/src/network/testnet2.rs +++ b/dpc/src/network/testnet2.rs @@ -84,14 +84,16 @@ impl Network for Testnet2 { const NETWORK_ID: u16 = 2u16; const NETWORK_NAME: &'static str = "testnet2"; - const NUM_EVENTS: usize = 1024; + const NUM_EVENTS: usize = 512; const NUM_INPUT_RECORDS: usize = 2; const NUM_OUTPUT_RECORDS: usize = 2; const ADDRESS_SIZE_IN_BYTES: usize = 32; + const CIPHERTEXT_SIZE_IN_BYTES: usize = 320; const RECORD_SIZE_IN_BYTES: usize = 280; const PAYLOAD_SIZE_IN_BYTES: usize = 128; - + const TRANSITION_SIZE_IN_BYTES: usize = 1129; + const POSW_PROOF_SIZE_IN_BYTES: usize = 771; const POSW_NUM_LEAVES: usize = 8; const POSW_TREE_DEPTH: usize = 3; diff --git a/dpc/src/record/ciphertext.rs b/dpc/src/record/ciphertext.rs index 895a9c209c..b8613eea2f 100644 --- a/dpc/src/record/ciphertext.rs +++ b/dpc/src/record/ciphertext.rs @@ -14,23 +14,27 @@ // You should have received a copy of the GNU General Public License // along with the snarkVM library. If not, see . -use crate::{Address, Network, Payload, Record, ViewKey}; +use crate::{Address, Network, Payload, Record, RecordError, ViewKey}; use snarkvm_algorithms::traits::{EncryptionScheme, CRH}; use snarkvm_utilities::{ + fmt, io::{Cursor, Result as IoResult}, marker::PhantomData, + str::FromStr, to_bytes_le, FromBytes, + FromBytesDeserializer, Read, ToBytes, + ToBytesSerializer, Write, }; use anyhow::{anyhow, Result}; use rand::{thread_rng, CryptoRng, Rng}; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -#[derive(Derivative, Serialize, Deserialize)] +#[derive(Derivative)] #[derivative( Clone(bound = "N: Network"), Debug(bound = "N: Network"), @@ -44,7 +48,8 @@ pub struct RecordCiphertext { } impl RecordCiphertext { - pub fn new(ciphertext: Vec) -> Self { + pub fn from_vec(ciphertext: Vec) -> Self { + assert_eq!(N::CIPHERTEXT_SIZE_IN_BYTES, ciphertext.len()); Self { ciphertext, phantom: PhantomData, @@ -79,7 +84,7 @@ impl RecordCiphertext { let randomizer = N::account_encryption_scheme().generate_randomness(rng); let ciphertext = N::account_encryption_scheme().encrypt(&*record.owner(), &randomizer, &buffer)?; - Ok((Self::new(ciphertext), randomizer)) + Ok((Self::from_vec(ciphertext), randomizer)) } /// Decrypt the record ciphertext using the view key of the recipient. @@ -115,30 +120,126 @@ impl RecordCiphertext { } } -impl Default for RecordCiphertext { - fn default() -> Self { - let (record, _randomness) = Self::encrypt(&Record::default(), &mut thread_rng()).unwrap(); - record +impl FromBytes for RecordCiphertext { + #[inline] + fn read_le(mut reader: R) -> IoResult { + let mut ciphertext = Vec::with_capacity(N::CIPHERTEXT_SIZE_IN_BYTES); + for _ in 0..N::CIPHERTEXT_SIZE_IN_BYTES { + ciphertext.push(u8::read_le(&mut reader)?); + } + + Ok(Self::from_vec(ciphertext)) } } impl ToBytes for RecordCiphertext { #[inline] fn write_le(&self, mut writer: W) -> IoResult<()> { - (self.ciphertext.len() as u16).write_le(&mut writer)?; self.ciphertext.write_le(&mut writer) } } -impl FromBytes for RecordCiphertext { - #[inline] - fn read_le(mut reader: R) -> IoResult { - let ciphertext_len = u16::read_le(&mut reader)?; - let mut ciphertext = Vec::with_capacity(ciphertext_len as usize); - for _ in 0..ciphertext_len { - ciphertext.push(u8::read_le(&mut reader)?); +impl FromStr for RecordCiphertext { + type Err = RecordError; + + fn from_str(payload_hex: &str) -> Result { + Ok(Self::read_le(&hex::decode(payload_hex)?[..])?) + } +} + +impl fmt::Display for RecordCiphertext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let bytes = self.to_bytes_le().expect("Failed to convert ciphertext to bytes"); + write!(f, "{}", hex::encode(bytes)) + } +} + +impl Serialize for RecordCiphertext { + fn serialize(&self, serializer: S) -> Result { + match serializer.is_human_readable() { + true => serializer.collect_str(self), + false => ToBytesSerializer::serialize(self, serializer), } + } +} + +impl<'de, N: Network> Deserialize<'de> for RecordCiphertext { + fn deserialize>(deserializer: D) -> Result { + match deserializer.is_human_readable() { + true => FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom), + false => { + FromBytesDeserializer::::deserialize(deserializer, "ciphertext", N::CIPHERTEXT_SIZE_IN_BYTES) + } + } + } +} + +impl Default for RecordCiphertext { + fn default() -> Self { + let (record, _randomness) = Self::encrypt(&Record::default(), &mut thread_rng()).unwrap(); + record + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::testnet2::Testnet2; + use snarkvm_utilities::UniformRand; + + use rand::thread_rng; + + #[test] + fn test_serde_json() { + let rng = &mut thread_rng(); + + let expected_ciphertext = RecordCiphertext::::from_vec( + (0..Testnet2::CIPHERTEXT_SIZE_IN_BYTES) + .map(|_| u8::rand(rng)) + .collect::>(), + ); + + // Serialize + let expected_string = &expected_ciphertext.to_string(); + let candidate_string = serde_json::to_string(&expected_ciphertext).unwrap(); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!( + expected_ciphertext, + RecordCiphertext::from_str(&expected_string).unwrap() + ); + assert_eq!(expected_ciphertext, serde_json::from_str(&candidate_string).unwrap()); + } - Ok(Self::new(ciphertext)) + #[test] + fn test_bincode() { + let rng = &mut thread_rng(); + + let expected_ciphertext = RecordCiphertext::::from_vec( + (0..Testnet2::CIPHERTEXT_SIZE_IN_BYTES) + .map(|_| u8::rand(rng)) + .collect::>(), + ); + + // Serialize + let expected_bytes = expected_ciphertext.to_bytes_le().unwrap(); + assert_eq!( + &expected_bytes[..], + &bincode::serialize(&expected_ciphertext).unwrap()[..] + ); + + // Deserialize + assert_eq!( + expected_ciphertext, + RecordCiphertext::read_le(&expected_bytes[..]).unwrap() + ); + assert_eq!(expected_ciphertext, bincode::deserialize(&expected_bytes[..]).unwrap()); } } diff --git a/dpc/src/traits/network.rs b/dpc/src/traits/network.rs index d7a389b064..f82dbb1632 100644 --- a/dpc/src/traits/network.rs +++ b/dpc/src/traits/network.rs @@ -49,8 +49,10 @@ pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Send + Sync { const NUM_TOTAL_RECORDS: usize = Self::NUM_INPUT_RECORDS + Self::NUM_OUTPUT_RECORDS; const ADDRESS_SIZE_IN_BYTES: usize; + const CIPHERTEXT_SIZE_IN_BYTES: usize; const PAYLOAD_SIZE_IN_BYTES: usize; const RECORD_SIZE_IN_BYTES: usize; + const TRANSITION_SIZE_IN_BYTES: usize; const POSW_PROOF_SIZE_IN_BYTES: usize; const POSW_NUM_LEAVES: usize; diff --git a/dpc/src/transaction/transaction.rs b/dpc/src/transaction/transaction.rs index de748eb594..e6ad8ee1c8 100644 --- a/dpc/src/transaction/transaction.rs +++ b/dpc/src/transaction/transaction.rs @@ -51,6 +51,8 @@ pub struct Transaction { inner_circuit_id: N::InnerCircuitID, /// The state transition. transitions: Vec>, + /// The events emitted from this transaction. + events: Vec>, } impl Transaction { @@ -69,11 +71,17 @@ impl Transaction { /// Initializes an instance of `Transaction` from the given inputs. #[inline] - pub fn from(network_id: u16, inner_circuit_id: N::InnerCircuitID, transitions: Vec>) -> Result { + pub fn from( + network_id: u16, + inner_circuit_id: N::InnerCircuitID, + transitions: Vec>, + events: Vec>, + ) -> Result { let transaction = Self { network_id, - transitions, inner_circuit_id, + transitions, + events, }; match transaction.is_valid() { @@ -94,7 +102,7 @@ impl Transaction { } // Ensure the number of events is less than `N::NUM_EVENTS`. - if self.events().len() > N::NUM_EVENTS { + if self.events.len() > N::NUM_EVENTS { eprintln!("Transaction contains an invalid number of events"); return false; } @@ -242,18 +250,18 @@ impl Transaction { .fold(AleoAmount::ZERO, |a, b| a.add(*b)) } - /// Returns the events. - #[inline] - pub fn events(&self) -> Vec> { - self.transitions.iter().flat_map(Transition::events).cloned().collect() - } - /// Returns a reference to the state transitions. #[inline] pub fn transitions(&self) -> &Vec> { &self.transitions } + /// Returns a reference to the events. + #[inline] + pub fn events(&self) -> &Vec> { + &self.events + } + /// Returns the ciphertext IDs. #[inline] pub fn to_ciphertext_ids(&self) -> Result> { @@ -290,16 +298,6 @@ impl Transaction { } } -impl ToBytes for Transaction { - #[inline] - fn write_le(&self, mut writer: W) -> IoResult<()> { - self.network_id.write_le(&mut writer)?; - self.inner_circuit_id.write_le(&mut writer)?; - (self.transitions.len() as u16).write_le(&mut writer)?; - self.transitions.write_le(&mut writer) - } -} - impl FromBytes for Transaction { #[inline] fn read_le(mut reader: R) -> IoResult { @@ -312,7 +310,25 @@ impl FromBytes for Transaction { transitions.push(FromBytes::read_le(&mut reader)?); } - Ok(Self::from(network_id, inner_circuit_id, transitions).expect("Failed to deserialize a transaction")) + let num_events: u16 = FromBytes::read_le(&mut reader)?; + let mut events = Vec::with_capacity(num_events as usize); + for _ in 0..num_events { + events.push(FromBytes::read_le(&mut reader)?); + } + + Ok(Self::from(network_id, inner_circuit_id, transitions, events).expect("Failed to deserialize a transaction")) + } +} + +impl ToBytes for Transaction { + #[inline] + fn write_le(&self, mut writer: W) -> IoResult<()> { + self.network_id.write_le(&mut writer)?; + self.inner_circuit_id.write_le(&mut writer)?; + (self.transitions.len() as u16).write_le(&mut writer)?; + self.transitions.write_le(&mut writer)?; + (self.events.len() as u16).write_le(&mut writer)?; + self.events.write_le(&mut writer) } } diff --git a/dpc/src/transition/transition.rs b/dpc/src/transition/transition.rs index 0ebe4b44e6..8ca81dfc31 100644 --- a/dpc/src/transition/transition.rs +++ b/dpc/src/transition/transition.rs @@ -16,16 +16,17 @@ use crate::{circuits::*, prelude::*}; use snarkvm_algorithms::traits::{CRH, SNARK}; -use snarkvm_utilities::{to_bytes_le, FromBytes, ToBytes}; +use snarkvm_utilities::{to_bytes_le, FromBytes, FromBytesDeserializer, ToBytes, ToBytesSerializer}; -use anyhow::Result; -use serde::{Deserialize, Serialize}; +use anyhow::{anyhow, Result}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt, io::{Read, Result as IoResult, Write}, + str::FromStr, }; -#[derive(Derivative, Serialize, Deserialize)] +#[derive(Derivative)] #[derivative( Clone(bound = "N: Network"), Debug(bound = "N: Network"), @@ -47,8 +48,6 @@ pub struct Transition { ciphertexts: Vec>, /// A value balance is the difference between the input and output record values. value_balance: AleoAmount, - /// The events emitted from this transition. - events: Vec>, /// The zero-knowledge proof attesting to the validity of this transition. proof: N::OuterProof, } @@ -56,7 +55,7 @@ pub struct Transition { impl Transition { /// Initializes a new instance of a transition. #[inline] - pub(crate) fn from(request: &Request, response: &Response, proof: N::OuterProof) -> Result { + pub(crate) fn new(request: &Request, response: &Response, proof: N::OuterProof) -> Result { // Fetch the block hash, local commitments root, and serial numbers. let block_hash = request.block_hash(); let local_commitments_root = request.local_commitments_root(); @@ -66,8 +65,29 @@ impl Transition { let commitments = response.commitments(); let ciphertexts = response.ciphertexts().clone(); let value_balance = response.value_balance(); - let events = response.events().clone(); + // Construct the transition. + Self::from( + block_hash, + local_commitments_root, + serial_numbers, + commitments, + ciphertexts, + value_balance, + proof, + ) + } + + /// Constructs an instance of a transition from the given inputs. + pub(crate) fn from( + block_hash: N::BlockHash, + local_commitments_root: N::LocalCommitmentsRoot, + serial_numbers: Vec, + commitments: Vec, + ciphertexts: Vec>, + value_balance: AleoAmount, + proof: N::OuterProof, + ) -> Result { // Compute the transition ID. let transition_id = Self::compute_transition_id( block_hash, @@ -87,7 +107,6 @@ impl Transition { commitments, ciphertexts, value_balance, - events, proof, }) } @@ -190,12 +209,6 @@ impl Transition { &self.value_balance } - /// Returns a reference to the events. - #[inline] - pub fn events(&self) -> &Vec> { - &self.events - } - /// Returns a reference to the transition proof. #[inline] pub fn proof(&self) -> &N::OuterProof { @@ -254,36 +267,18 @@ impl FromBytes for Transition { } let value_balance: AleoAmount = FromBytes::read_le(&mut reader)?; - - let num_events: u16 = FromBytes::read_le(&mut reader)?; - let mut events = Vec::with_capacity(num_events as usize); - for _ in 0..num_events { - events.push(FromBytes::read_le(&mut reader)?); - } - let proof: N::OuterProof = FromBytes::read_le(&mut reader)?; - let transition_id = Self::compute_transition_id( - block_hash, - local_commitments_root, - &serial_numbers, - &commitments, - &ciphertexts, - value_balance, - ) - .expect("Failed to compute the transition ID during deserialization"); - - Ok(Self { - transition_id, + Ok(Self::from( block_hash, local_commitments_root, serial_numbers, commitments, ciphertexts, value_balance, - events, proof, - }) + ) + .expect("Failed to deserialize a transition from bytes")) } } @@ -296,33 +291,127 @@ impl ToBytes for Transition { self.commitments.write_le(&mut writer)?; self.ciphertexts.write_le(&mut writer)?; self.value_balance.write_le(&mut writer)?; - (self.events.len() as u16).write_le(&mut writer)?; - self.events.write_le(&mut writer)?; self.proof.write_le(&mut writer) } } -// -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::testnet2::Testnet2; -// -// use rand::thread_rng; -// -// #[test] -// fn test_bincode() { -// let rng = &mut thread_rng(); -// -// let transaction = Testnet2::genesis_block().to_coinbase_transaction().unwrap(); -// let expected_transition = transaction.transitions().first().unwrap().clone(); -// -// let expected_bytes = expected_transition.to_bytes_le().unwrap(); -// assert_eq!( -// &expected_bytes[..], -// &bincode::serialize(&expected_transition).unwrap()[..] -// ); -// -// assert_eq!(expected_transition, Transition::read_le(&expected_bytes[..]).unwrap()); -// assert_eq!(expected_transition, bincode::deserialize(&expected_bytes[..]).unwrap()); -// } -// } + +impl FromStr for Transition { + type Err = anyhow::Error; + + fn from_str(transition: &str) -> Result { + let transition = serde_json::Value::from_str(transition)?; + let transition_id: N::TransitionID = serde_json::from_value(transition["transition_id"].clone())?; + + // Recover the transition. + let transition = Self::from( + serde_json::from_value(transition["block_hash"].clone())?, + serde_json::from_value(transition["local_commitments_root"].clone())?, + serde_json::from_value(transition["serial_numbers"].clone())?, + serde_json::from_value(transition["commitments"].clone())?, + serde_json::from_value(transition["ciphertexts"].clone())?, + serde_json::from_value(transition["value_balance"].clone())?, + serde_json::from_value(transition["proof"].clone())?, + )?; + + // Ensure the transition ID matches. + match transition_id == transition.transition_id() { + true => Ok(transition), + false => Err(anyhow!( + "Incorrect transition ID during deserialization. Expected {}, found {}", + transition_id, + transition.transition_id() + )), + } + } +} + +impl fmt::Display for Transition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let transition = serde_json::json!({ + "transition_id": self.transition_id, + "block_hash": self.block_hash, + "local_commitments_root": self.local_commitments_root, + "serial_numbers": self.serial_numbers, + "commitments": self.commitments, + "ciphertext_ids": self.to_ciphertext_ids().collect::>>().expect("Failed to format ciphertext IDs"), + "ciphertexts": self.ciphertexts, + "value_balance": self.value_balance, + "proof": self.proof, + }); + write!(f, "{}", transition) + } +} + +impl Serialize for Transition { + fn serialize(&self, serializer: S) -> Result { + match serializer.is_human_readable() { + true => serializer.collect_str(self), + false => ToBytesSerializer::serialize(self, serializer), + } + } +} + +impl<'de, N: Network> Deserialize<'de> for Transition { + fn deserialize>(deserializer: D) -> Result { + match deserializer.is_human_readable() { + true => FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom), + false => { + FromBytesDeserializer::::deserialize(deserializer, "transition", N::TRANSITION_SIZE_IN_BYTES) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::testnet2::Testnet2; + + #[test] + fn test_size() { + let transaction = Testnet2::genesis_block().to_coinbase_transaction().unwrap(); + let transition = transaction.transitions().first().unwrap().clone(); + assert_eq!( + transition.to_bytes_le().unwrap().len(), + Testnet2::TRANSITION_SIZE_IN_BYTES + ); + } + + #[test] + fn test_serde_json() { + let transaction = Testnet2::genesis_block().to_coinbase_transaction().unwrap(); + let expected_transition = transaction.transitions().first().unwrap().clone(); + + // Serialize + let expected_string = &expected_transition.to_string(); + let candidate_string = serde_json::to_string(&expected_transition).unwrap(); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!(expected_transition, Transition::from_str(&expected_string).unwrap()); + assert_eq!(expected_transition, serde_json::from_str(&candidate_string).unwrap()); + } + + #[test] + fn test_bincode() { + let transaction = Testnet2::genesis_block().to_coinbase_transaction().unwrap(); + let expected_transition = transaction.transitions().first().unwrap().clone(); + + println!("{}", serde_json::to_string(&expected_transition).unwrap()); + + let expected_bytes = expected_transition.to_bytes_le().unwrap(); + assert_eq!( + &expected_bytes[..], + &bincode::serialize(&expected_transition).unwrap()[..] + ); + + assert_eq!(expected_transition, Transition::read_le(&expected_bytes[..]).unwrap()); + assert_eq!(expected_transition, bincode::deserialize(&expected_bytes[..]).unwrap()); + } +} diff --git a/dpc/src/virtual_machine/function_inputs.rs b/dpc/src/virtual_machine/function_inputs.rs index 099fdf42a9..7e9a0881c3 100644 --- a/dpc/src/virtual_machine/function_inputs.rs +++ b/dpc/src/virtual_machine/function_inputs.rs @@ -35,6 +35,7 @@ use std::{ PartialEq(bound = "N: Network") )] pub struct FunctionInputs { + #[serde(skip)] _unused: PhantomData, } diff --git a/dpc/src/virtual_machine/function_type.rs b/dpc/src/virtual_machine/function_type.rs index 876b63307d..8e312228f5 100644 --- a/dpc/src/virtual_machine/function_type.rs +++ b/dpc/src/virtual_machine/function_type.rs @@ -51,7 +51,7 @@ impl FunctionType { Self::Update => 1, Self::Remove => 0, Self::DoubleAdd => 2, - Self::DoubleRemove => 2, + Self::DoubleRemove => 0, Self::Join => 1, Self::Split => 2, Self::Full => 2, diff --git a/dpc/src/virtual_machine/virtual_machine.rs b/dpc/src/virtual_machine/virtual_machine.rs index a50f97dd1b..19d089b339 100644 --- a/dpc/src/virtual_machine/virtual_machine.rs +++ b/dpc/src/virtual_machine/virtual_machine.rs @@ -25,6 +25,8 @@ pub struct VirtualMachine { local_commitments: LocalCommitments, /// The current list of transitions. transitions: Vec>, + /// The current list of events. + events: Vec>, } impl VirtualMachine { @@ -33,6 +35,7 @@ impl VirtualMachine { Ok(Self { local_commitments: LocalCommitments::new()?, transitions: Default::default(), + events: Default::default(), }) } @@ -88,18 +91,24 @@ impl VirtualMachine { )?); // Construct the transition. - let transition = Transition::::from(request, &response, outer_proof)?; + let transition = Transition::::new(request, &response, outer_proof)?; // Update the state of the virtual machine. self.local_commitments.add(transition.commitments())?; self.transitions.push(transition); + self.events.extend_from_slice(response.events()); Ok(self) } /// Finalizes the virtual machine state and returns a transaction. pub fn finalize(&self) -> Result> { - Transaction::from(N::NETWORK_ID, *N::inner_circuit_id(), self.transitions.clone()) + Transaction::from( + N::NETWORK_ID, + *N::inner_circuit_id(), + self.transitions.clone(), + self.events.clone(), + ) } /// Performs a noop transition. diff --git a/parameters/src/testnet1/genesis/block.rs b/parameters/src/testnet1/genesis/block.rs index e573f67204..b6c42b16b9 100644 --- a/parameters/src/testnet1/genesis/block.rs +++ b/parameters/src/testnet1/genesis/block.rs @@ -23,7 +23,7 @@ pub struct GenesisBlock; impl Genesis for GenesisBlock { const CHECKSUM: &'static str = ""; - const SIZE: u64 = 2140; + const SIZE: u64 = 2136; fn load_bytes() -> Vec { let block_header_bytes = GenesisBlockHeader::load_bytes(); diff --git a/parameters/src/testnet1/genesis/block_header.genesis b/parameters/src/testnet1/genesis/block_header.genesis index 4c68ef548bdd90b64eddddf328aa3911cbd2141d..634551c4924d457f2e4d6edbbb4c71b628afaf6b 100644 GIT binary patch literal 919 zcmcEe;~Hw0ue~)X;)+>&^#kslOF=<%*DU;^v`_V^t79)qR2*B0)BNunPL_0hQC+QZ z=~L387sb)%xAaOLI_^5Xi&4UK#f=AVubkSj!eUy;;~Ni-{8YYVrq^!E|5qr2seBI` z0}}WT1LH8aE%j^{nQ`+$KNs)q$iTRd7>5g$+y z?Ehxfa{&vRgHeA|^p3c)AGo?%q{4N>xhoP@6RQ6#T(1%nn#^f#R?+w^d*#fKDGO$; z$=%i^qdqOSKHtkmA@6OLd#u~FuWl-bGTA53t@(L;w&U>|N%cH08TS10h?YNHTwkj% zBpAmt?8%SB=W{jLJEzQ_eM?mHSbu-&kN(AG zv!A+NdNEadp~9`2fNvk$Y9u(AR=RF+srcSBf6~lygS9cL@8JIV>vcS-z2RxB>$ZRc zB}-hC6T4RH zpC9BHgtzkc=1rP)q*BaN@%EqKtX-a!ES@JHR$n`0me9{wmE!uzeMLdm`XxVm?`+JA zWMkO#Xp}j88Xm&CvU1>boZIQIzHn;q)z!97G>@-SNu&+hKjp zQB%Ni^2V1%(<-EnJ~G(7XlF^o=1(ENHg$^f-~Y${IR8+ikg4?D|Kay~OFe%W74-Yu+r{wo>MQwA1579P1hxB{s3VOeqU;JNrd)O|`t?FS&gNiF0hz zzJK`ta@CQZ>x{chM4glx72kZ=oL+R}hm3>rE34cp+ll4AJd@5_v6%#XIqY-oe|${i OR1S*<$WIzr4)JuFcAY1Bq`y%*8^ z%TpE~x9sQ>w`+HI~TcgqQjly3}`v31c<1X6ZeP!p1Bf&SKUp3UNc%Ss;s&HoJ z*?x|X{Qu1^ScQI)mG~)jxOCbM#~+qAGkp#`*VZc#e5f^HW}rnB*G4~{Eu_sTV{u;RToBe-92;^WN3 zzz3z~T8?k#^!M$Oa#Ou~^xM8Utm$VDPK^GwJD_^DRzlME<_4I5u1(O==b2JweLKV_ z$I~d>@Zee1c{LHIW*+Vgf34tfWqE>%OnQ?@)HN^d~b*bSCn8i6+hO>WCc)_yO|8ov+a^GIUkZ!nqeG#M1hI6eCwP&=f z6i>EoG9y=K%IbLM0 z;rjBb;oJ|^SIR#fb8mFN`zWQEzF+YNPit~||a;85#r@ni=ZNmHI zXW4IQY)e|`+E5YyHTKyreRfzpASFo@CM-O{RqO8Vyrq=!K2wuX>`w6;hP7EI`hR4Y zv(3^HNY454^2pt`MJ?~1-?x0ra&=9qI|Jk5kZBzi3nb0*@87>;BO7pS7cQ>-`+e|~!Q@Dcrr NDT(r*KB)j51ppEOm%9J} diff --git a/parameters/src/testnet1/genesis/transaction_1.genesis b/parameters/src/testnet1/genesis/transaction_1.genesis index 4a85422c5a34d593cec725a9efc716cef8e38385..d4e763626571ffb2cac8f4b20325e402cbd0be07 100644 GIT binary patch delta 1082 zcmV-A1jYNK37-j&bbmer1-+2(`4PRo-7DL#AqNPz0uU?RO^XO(!n`nPr3yD%b=UR5 zrQ@#17zt*I*k|SI#&x&cUsY}&*k6wkH4P%fcequwJ0X$C`Nt={QPEtiDih zDww$hu?u$#t9#i9M6YbSE1-)9$J*52d1rD=9jV_ZELX;`TYnMEOD5SwuAx-~SDFfW zm#4t#Hi~4{OQUroJSn=}MpX`WW&)pMRhhjA{ExT%vCbulug%vv!x>ePel*F_Wov} zwh@KRjH^$B$k>JY7Kg8_Ooh6k_wf-1%L26Js0n+Iv7V>iSOS&huc-Lt2^6!V4ZC)9 zd6#|#iIZv|$5ib=2|CE0gk8|@zo1vm8veNO;@mA&Nq=|61qmwSVcn}ZEeLFnmUbF| zywv!Pad^1fF}8UW3ra><5_#C=*z}!Z5>%@D^=RJ@*=i>Ao zIW2RWK;KR2lr1O2{?Q}~<+K9(G6YMTRF|5I4BbwsPD*Dne75PP)cfT_XZhhtnG|aR zbqCy{0hbU^YwaLNRIRJ7-k<~R`*o6QGJ<4g?W|)1)h_sPQM<9ZRYq{;Tkkw1a*ulV740z2EpL>23yJ-;V&} zFnNn_A=e!bFfrLMGbfH)#iM`l#zcrBAmk3q(`Mfg(ZFFS7-Q@Xl|zObUgDO;zgMco zZ+{I+1kL~g0C=sHi~s-s?_fZ@8Qf}&$r7?g9Xv}87!J$b^9as{TBh3= zwR|Ma0~NC)`R04KfzJqh2`9Sy(d_R~8VRz{d;8X!KhNUgP{G>Hl3YfcIurb|BPvZf zEGJG6jax6z5{xStef+C>XKsx2mOd&LAx+id9YdLBp%iLq<$@nW@{rd7D+DRk477Z z4b+`2R|5d)C4F|3Z8}o68InAC)F@$3=)tuY6z9KZ01ewMe(5}DquKIpd^}Yh=KblA z1yXL^&ew#b#D50$#0mla^M@rBxZ9HYF=aCc!Ekk*x<8) zlP~rtdbs=nda46Xnd&<0^otH_@75S3r;;L1t)s?=I>pDena$y|xf^#M41s}l5>XC7 z0XT}P*d%n`KswUxyh!MmdPS%LcTUn3RiW9om&Rv45K0EM5B7d~NW$SW)>t9G1U9tK zIBE)n%YOw9k)ml%-~e*Z#sc08^H>z-{cE?cELj2WEX2gNJ8|o483Ac3#}1n%Mj;Py zFEdv#q$D5dk4Z{Hr7j?s>p=H05L>+lxDYY;dS1oSV6zA@2Lqh1(G)^-TOkp2m7lRt z!P2dWWCjg~wrp{2<~|heI}0YJrXumLtQ4#haeottu0(xHaMJ_g(nioQWb|oI`?&0J z%ms+Jpkzr=;|KEJyOhU5cs2}mLZ3zL^~OufB7KU0Y^kfttRI@NV8ofVYkNI&?^F`_ z5j?Q~KQ4y!HcVhvVtYhW8@id(bVfP?eFk^ywQdp^d$?sf?1)NK<3*M|OZZdXKL)SW z^?&TEyYll>B}!ir0C=sHi~s-s002UhQ?V!lTw=v)p``9TIR(7s( z`H8lON16QPlh`~G^3Fem)Y@sc;ULc}vl^9^LTauY_vk%o;C@ zHp_IhaGKMx{OplYj{rLZrvP=en)nQF;(ub|Q4%5{?BAa{qM~U(Oc1WQ(gQ?0EO5ntcZtI0<%6(~k@K zM45~wfsFt*ef{!hs^y#sr^*O`^*^+$LOwu?3DElOJ9H E0T*iwF8}}l diff --git a/parameters/src/testnet1/genesis/transaction_1.rs b/parameters/src/testnet1/genesis/transaction_1.rs index b2da1490c3..95b9871324 100644 --- a/parameters/src/testnet1/genesis/transaction_1.rs +++ b/parameters/src/testnet1/genesis/transaction_1.rs @@ -20,7 +20,7 @@ pub struct Transaction1; impl Genesis for Transaction1 { const CHECKSUM: &'static str = ""; - const SIZE: u64 = 1187; + const SIZE: u64 = 1183; fn load_bytes() -> Vec { let buffer = include_bytes!("transaction_1.genesis"); diff --git a/parameters/src/testnet2/genesis/block.rs b/parameters/src/testnet2/genesis/block.rs index 0100abd5e1..4ee8307282 100644 --- a/parameters/src/testnet2/genesis/block.rs +++ b/parameters/src/testnet2/genesis/block.rs @@ -23,7 +23,7 @@ pub struct GenesisBlock; impl Genesis for GenesisBlock { const CHECKSUM: &'static str = ""; - const SIZE: u64 = 2140; + const SIZE: u64 = 2136; fn load_bytes() -> Vec { let block_header_bytes = GenesisBlockHeader::load_bytes(); diff --git a/parameters/src/testnet2/genesis/block_header.genesis b/parameters/src/testnet2/genesis/block_header.genesis index 8656d4a51f29f54376ac4d09d60d21c52d3d77b1..ce0333608c2be48973182369fe0f1d80aed23612 100644 GIT binary patch delta 888 zcmV-;1Bd*V2bTw6p7SkPT62QMc)jpI_1W&3M}8RZg>#gWvX?_z zX2W+1kzf>mMI>T)Cn9zQB(st2Ut>B!nXvyUxq9ZhUVJ(aSO3Zi0{{R3000001ONa4 z00000rX^?&zl-s3_YIR5%_+A9uf0a}ZBHIhJJ*vp*%sBExL;-wfb5o;n7c;ivk0+( zxyOG;*e8J|JLuvhym%zdSor2_tHM%$)PZ~r4j5KPB8sl0000008W-E ztp~t=b$y9$+a9RohlI^MSFHXxq=4{Ep9ZQx1b`4{)xL`2wdsL4$E?djtbxzi-OZ+F=uq-`px`2*#Jo~;y`FKvw$MaCu8rT=Zf&> z7*rFs-cj)*M1vHZmhFb!X;`#A4%tBn4gKwZD(_UJg$Po$byt7_000000001V^b+9Z zaInPd{MdTPuMC000000001ku_2p( zeFZ|q>qiT|gdm|g!sf@03`1#J!0oyG43TjMJ@6fZH}YoDK%rr}X>=YVq1?Mq*}lau zX2%NwBAmdP6@y^{gukgQ`lOSoUG^Bz= z8s^iz2tG5-ud!jLc+CU#Em=i*rL0nd5$Zq!?9%*3sgY@zPJ<#@AL>fh OC4$r<8~6lX0RRA%P@uU0 literal 919 zcmWIib;)^uz?QjPmu{5*IdtR7<7xE_Wly5>c0EZKKEi7*n5X$;+r9hz0Zt!oo=?8G z+A2L|)3>lUOrbX03p3Ae>JzxO)5!LAdH2)0)gs4a)mSsCqE(nG^xB32 z8T^NWIUWLBDf=}0eJ}pV*%f?J=ypoFNzf6I!c_s!`e(m9#KH{K!~&&7pCq=(`Ek4U znZ8_a|4DZDPTQ?+hVlCzuJXM0|5FtE=_$LjljKy3-RtjP3lXZ{){yPXuCidVneX(q zeE(-8C2l>)eCJT?H-qOXC&c$#KKkd|=4(4|%d`o;_ottZJ@joQW6JIS#}@MJ3^-U( zq!hx$z4yYlD{iseXS+hyDZan_@~x~0+uo%IG=41WOZ})(aYnh}zVe)?{m-sR`R(AV zD&KNEe%?*N+q=c*{7ss1?O^!Ry$16NPW`ukeP$ZN%DSHP3@-+lefk7hg zxo5hX%VwCZPODkpr?mBnUxWy9nUU-23R~E*lRJOe zFT9}SHto#&=hJvr9d*v@y>soEBVS<{lSQlk^eK!lmDGZ7eU^MFs(Rtd;l2JE=PxKM z2$dA*TBX81p}dAgdycEU^}qDje^xXXteACiy2-l!tBslcOlM-7H)Os0nYQ=t{D{`h zJy{(+QESw~utgrUH%nvbno017<<+! N&MBSS$x diff --git a/parameters/src/testnet2/genesis/transaction_1.genesis b/parameters/src/testnet2/genesis/transaction_1.genesis index 2a16104518ea6f308781d3c81b3498c668c37bc3..aca8db5b0c9f4c893a4264659d9cdb9add0e6a25 100644 GIT binary patch delta 1082 zcmV-A1jYNK37-j&bbpfPqkg;=i`wlrDTl^-T=bH8dMye?0TMjiovVsz9t`XPray9y zUAjwE2yy1GFs#~c2nHqhOLNX!472`9)(Q0{HaY}+oH}nl&9Y!AlP1x@_O!*mo4x#U zmF4H}c@G(3<9Yn*n=bA;i1(y0>rc z%*NSGoI0YLbCsC{%_I`3^u`T0!gCUIRjZL=F7_AP+@x7^C!X=fL0T6lgqx^ZuW<$T zQ+F9Hy>PPSlREbe28ct!BNFV##qAN3(qI|OA_)sdlLYp)iqBfNNl*n6r)~U@Akm{z zTk^Yr_Iha@>VNnRaHz1V0Dg{H)$M`NV-Lw0l3?we4yXsF!&z=ZPe?5WA;K@=!xYjr ziODi^Uz$b6$0TLBo9=*;0}D)+Rf<2TQ@WlUa+CrDf>YxVHX>D9 z3CKo%pnv#Cl?dT@vQU?CV)Z&Oi7&8*8xN58*sU$bXvaO!)y5#TFC_yeKA@?EPy8xJFG%jtv-Bi3dBqyf%!{F=ho9a}wqo zEnJ^Kdezm{&Jt=Pr`P9T`b1RU>~_QFz(x>IbtZ{K@jO1U`@Q)(eA^t2<{Hnbv+>MD zB!90YB{bg$0C=sHi~s-sMKERnqCgkE5ZChyQ20Nod>K=!g3-#Y(t52AW$#+tqOM;VvBItWcu{+^qQ~CTtbO z0I6yz)7dszc8iuL&-hA^WTmXO3kmQkJGM9~2WRaqoZVQb2^1QJZ@dz^z7aK>Rx?fU z)O8vsSFFZ)T#>OtV?}`!QpDWXD_dUlDMJQdAT<5ne$!~$U5(jvT{tE#Vt@ev0CzDK AtN;K2 delta 1086 zcmV-E1i|~C38M*+bbrBAuMfjaB6kToWdmu{A}JFhWW%=_ubFr}3IK11tP#mKl*60D z_71k;rl4mfOu3>7&w@ZuX}Wo6MulHFRRcR%Ow1Tf_eQ20OQ{+U(-e>9pmeYbBR&^sZ3e#;66=OiQ3dqG6UIy~eoF=c-eu9c#C^3r1vh z1j=l#0UG{ALnahajmcK?m8#nB_aY=V_#f8{Ytt7z2-YUfwgd7k|1qI`9;Tc)^6{JN zOjPZ(**eXq27g{=Q4Ee?q9qqW*D#E=icV^@TFkDW?n@HlovJ0gFMIwHH3iiwEX&Ck zs2O6HtUog}_-q7<21oDbpFab)5uWP0q!8droU{Jnp2g42W#Pi6*s{#sR!)#t6K26M zQ5r6*{t1GqCswBp2AD^50Ma<>o$nNula4~-iF&}!HGjVheS8i0E5<)m=DZgPr*j(# zEWsqCPL5jfo@@MM2xEq$>i_~aV^Slt2Z)ETu#qo?$Wr#az&H@lo!cxrK$yDckVX+e z0ly`FU%Al2XFp;oAGwGy3gt9x8VE=}a_z?U(B*<*4SbU)t4;w)QYpQfZMrE$xv4CA zJ1O~fRDbFyt|4rP_7ELQ8pakjW#@j#@434|?qmrtk-|9nq$LRjDm885KMsqwpNfsR za}x^E{`}NGFC#dny#>bKDEqf6Ob~Mk9Sl`?Dpx9k1@JI#^MwJBrkt~QIo-xG6l;>8 zuIkaT^bbeNw12vmFP@`gwf{?j{Ri+1mELgP-a+{86^x$VZZhK&>@H)td7HdYOH!hwAeSH7qGI|j-gt}jU52F zF+hRu5axh!2<40E!-T`Kr^50@a%X$ZTLAo8IEdAO&qB6+X^{cP|Q>Z&`rJO-dz(P_6?Orkkx*nNO2#3$xHl2mSP!&Fq} zR6Z)N?PC6m8pB)ywE+KUqt2VXC;IU(X@B9_u;m2MgXzJV7Hd2nga6~P*F(Xpi;nEs zReXD*ZLpJewj~@;p>`f%2o!ORm7)np45%RP!x|Y3N^!N1^kT=LFFpzk1oG@x85uR= z(HrE=qKyDz!fAO?1r&ML0k=00OfHL&&zz0UgE;O|8$OZ3wN^~_&L(R-N**AvS z|8mg9Trf24aHB<8BA7s${6@ws&d8rkO7iv$;R^b0A&*i-0IhO77V|n8%l*F5LgU7O E0djr|X8-^I diff --git a/parameters/src/testnet2/genesis/transaction_1.rs b/parameters/src/testnet2/genesis/transaction_1.rs index b2da1490c3..95b9871324 100644 --- a/parameters/src/testnet2/genesis/transaction_1.rs +++ b/parameters/src/testnet2/genesis/transaction_1.rs @@ -20,7 +20,7 @@ pub struct Transaction1; impl Genesis for Transaction1 { const CHECKSUM: &'static str = ""; - const SIZE: u64 = 1187; + const SIZE: u64 = 1183; fn load_bytes() -> Vec { let buffer = include_bytes!("transaction_1.genesis"); From a7a1dc72d7c9d2050e1a3d8ba220b1b39e39e496 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 04:29:05 -0700 Subject: [PATCH 04/17] Update field macro --- dpc/src/account/address.rs | 2 +- fields/src/macros.rs | 40 ++++++++++++++++++++++++++++++++------ fields/src/tests_field.rs | 3 +-- utilities/src/bytes.rs | 6 +++--- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/dpc/src/account/address.rs b/dpc/src/account/address.rs index a3dcc2302e..0f921b25d2 100644 --- a/dpc/src/account/address.rs +++ b/dpc/src/account/address.rs @@ -234,7 +234,7 @@ mod tests { ); // Deserialize - assert_eq!(expected_address, Address::from_str(&candidate_string).unwrap()); + assert_eq!(expected_address, Address::from_str(&expected_string).unwrap()); assert_eq!(expected_address, serde_json::from_str(&candidate_string).unwrap()); } diff --git a/fields/src/macros.rs b/fields/src/macros.rs index 56a5ff5936..a30bb10d9a 100644 --- a/fields/src/macros.rs +++ b/fields/src/macros.rs @@ -239,9 +239,13 @@ macro_rules! impl_primefield_serializer { impl serde::Serialize for $field

{ fn serialize(&self, serializer: S) -> Result { + let mut bytes = Vec::with_capacity(Self::SERIALIZED_SIZE); + CanonicalSerialize::serialize(self, &mut bytes).map_err(serde::ser::Error::custom)?; + println!("{:?}, {}", bytes, bytes.len()); + match serializer.is_human_readable() { true => serializer.collect_str(self), - false => snarkvm_utilities::ToBytesSerializer::serialize(self, serializer), + false => snarkvm_utilities::ToBytesSerializer::serialize(&bytes, serializer), } } } @@ -253,11 +257,35 @@ macro_rules! impl_primefield_serializer { let s: String = serde::Deserialize::deserialize(deserializer)?; core::str::FromStr::from_str(&s).map_err(serde::de::Error::custom) } - false => snarkvm_utilities::FromBytesDeserializer::::deserialize( - deserializer, - "field", - Self::SERIALIZED_SIZE, - ), + false => { + struct SerVisitor

(std::marker::PhantomData

); + + impl<'de, P: $params> serde::de::Visitor<'de> for SerVisitor

{ + type Value = $field

; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a valid field element") + } + + fn visit_seq(self, mut seq: S) -> Result + where + S: serde::de::SeqAccess<'de>, + { + let len = ::SERIALIZED_SIZE; + let bytes: Vec = (0..len) + .map(|_| { + seq.next_element()? + .ok_or_else(|| serde::de::Error::custom("could not read bytes")) + }) + .collect::, _>>()?; + + CanonicalDeserialize::deserialize(&mut &bytes[..]).map_err(serde::de::Error::custom) + } + } + + let visitor = SerVisitor(std::marker::PhantomData); + deserializer.deserialize_tuple(Self::SERIALIZED_SIZE, visitor) + } } } } diff --git a/fields/src/tests_field.rs b/fields/src/tests_field.rs index 8317deac5d..97345ef052 100644 --- a/fields/src/tests_field.rs +++ b/fields/src/tests_field.rs @@ -408,11 +408,10 @@ pub fn frobenius_test>(characteristic: C, maxpower: us } } -// Taken from https://github.com/scipr-lab/zexe/blob/master/algebra/src/tests/fields.rs#L381 pub fn field_serialization_test() { let buf_size = F::SERIALIZED_SIZE; - let mut rng = XorShiftRng::seed_from_u64(1231275789u64); + let mut rng = &mut rand::thread_rng(); for _ in 0..ITERATIONS { let a = F::rand(&mut rng); diff --git a/utilities/src/bytes.rs b/utilities/src/bytes.rs index 997a14bbbd..6add234c4b 100644 --- a/utilities/src/bytes.rs +++ b/utilities/src/bytes.rs @@ -139,14 +139,14 @@ impl<'de, T: FromBytes> FromBytesDeserializer { } } -struct FromBytesVisitor(String, Option, PhantomData); +pub struct FromBytesVisitor(String, Option, PhantomData); impl<'de, T: FromBytes> FromBytesVisitor { - fn new(name: &str) -> Self { + pub fn new(name: &str) -> Self { Self(name.to_string(), None, PhantomData) } - fn new_with_size(name: &str, size: usize) -> Self { + pub fn new_with_size(name: &str, size: usize) -> Self { Self(name.to_string(), Some(size), PhantomData) } } From 1c1b5043c9d23a5dea1086edd12d011e2f1b502c Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 04:30:44 -0700 Subject: [PATCH 05/17] Clean up --- fields/src/macros.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/fields/src/macros.rs b/fields/src/macros.rs index a30bb10d9a..3bf199d7da 100644 --- a/fields/src/macros.rs +++ b/fields/src/macros.rs @@ -241,7 +241,6 @@ macro_rules! impl_primefield_serializer { fn serialize(&self, serializer: S) -> Result { let mut bytes = Vec::with_capacity(Self::SERIALIZED_SIZE); CanonicalSerialize::serialize(self, &mut bytes).map_err(serde::ser::Error::custom)?; - println!("{:?}, {}", bytes, bytes.len()); match serializer.is_human_readable() { true => serializer.collect_str(self), From 423b7da76c50089dffecf9a148b21ffa01c92254 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 04:44:05 -0700 Subject: [PATCH 06/17] Moves tests_Field to curves module, simplifying dependencies --- Cargo.lock | 3 +-- curves/Cargo.toml | 12 ++++++------ curves/src/bls12_377/tests.rs | 2 +- curves/src/bw6_761/tests.rs | 15 ++++++++------- curves/src/edwards_bls12/tests.rs | 10 ++-------- curves/src/edwards_bw6/tests.rs | 10 ++++++++-- curves/src/traits/mod.rs | 5 +++++ {fields/src => curves/src/traits}/tests_field.rs | 2 +- fields/Cargo.toml | 7 ------- fields/src/lib.rs | 2 -- 10 files changed, 32 insertions(+), 36 deletions(-) rename {fields/src => curves/src/traits}/tests_field.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 3134f21f20..c9b09cdaff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1643,6 +1643,7 @@ dependencies = [ name = "snarkvm-curves" version = "0.7.5" dependencies = [ + "bincode", "criterion", "derivative", "rand", @@ -1701,10 +1702,8 @@ name = "snarkvm-fields" version = "0.7.5" dependencies = [ "anyhow", - "bincode", "derivative", "rand", - "rand_xorshift", "serde", "snarkvm-utilities", "thiserror", diff --git a/curves/Cargo.toml b/curves/Cargo.toml index 012d9d5d25..e7bf1dce53 100644 --- a/curves/Cargo.toml +++ b/curves/Cargo.toml @@ -39,10 +39,6 @@ version = "2" version = "0.8" default-features = false -[dependencies.rand_xorshift] -version = "0.3" -default-features = false - [dependencies.serde] version = "1.0.130" default-features = false @@ -51,11 +47,15 @@ features = [ "derive" ] [dependencies.thiserror] version = "1.0" +[dev-dependencies.bincode] +version = "1.3.3" + [dev-dependencies.criterion] version = "0.3" -[dev-dependencies.rand] -version = "0.8" +[dev-dependencies.rand_xorshift] +version = "0.3" +default-features = false [build-dependencies] rustc_version = "0.4" diff --git a/curves/src/bls12_377/tests.rs b/curves/src/bls12_377/tests.rs index 393f7079fc..79bf3550a0 100644 --- a/curves/src/bls12_377/tests.rs +++ b/curves/src/bls12_377/tests.rs @@ -36,6 +36,7 @@ use crate::{ templates::{short_weierstrass_jacobian::tests::sw_tests, twisted_edwards_extended::tests::edwards_test}, traits::{ tests_curve::curve_tests, + tests_field::{field_serialization_test, field_test, frobenius_test, primefield_test, sqrt_field_test}, tests_group::group_test, AffineCurve, PairingEngine, @@ -45,7 +46,6 @@ use crate::{ }; use snarkvm_fields::{ fp6_3over2::Fp6Parameters, - tests_field::{field_serialization_test, field_test, frobenius_test, primefield_test, sqrt_field_test}, FftField, FftParameters, Field, diff --git a/curves/src/bw6_761/tests.rs b/curves/src/bw6_761/tests.rs index 8168468108..53eb89271d 100644 --- a/curves/src/bw6_761/tests.rs +++ b/curves/src/bw6_761/tests.rs @@ -29,14 +29,15 @@ use crate::{ BW6_761, }, templates::short_weierstrass_jacobian::tests::sw_tests, - traits::{tests_curve::curve_tests, tests_group::group_test, AffineCurve, PairingEngine}, -}; -use snarkvm_fields::{ - tests_field::{field_serialization_test, field_test, frobenius_test, primefield_test, sqrt_field_test}, - Field, - One, - PrimeField, + traits::{ + tests_curve::curve_tests, + tests_field::{field_serialization_test, field_test, frobenius_test, primefield_test, sqrt_field_test}, + tests_group::group_test, + AffineCurve, + PairingEngine, + }, }; +use snarkvm_fields::{Field, One, PrimeField}; #[test] fn test_bw6_761_fr() { diff --git a/curves/src/edwards_bls12/tests.rs b/curves/src/edwards_bls12/tests.rs index 5c77f5ab94..8641e34464 100644 --- a/curves/src/edwards_bls12/tests.rs +++ b/curves/src/edwards_bls12/tests.rs @@ -19,6 +19,7 @@ use crate::{ templates::twisted_edwards_extended::tests::{edwards_test, montgomery_conversion_test}, traits::{ tests_curve::curve_tests, + tests_field::{field_serialization_test, field_test, primefield_test}, tests_group::group_test, AffineCurve, Group, @@ -27,14 +28,7 @@ use crate::{ TwistedEdwardsParameters, }, }; -use snarkvm_fields::{ - tests_field::{field_serialization_test, field_test, primefield_test}, - Field, - LegendreSymbol, - One, - SquareRootField, - Zero, -}; +use snarkvm_fields::{Field, LegendreSymbol, One, SquareRootField, Zero}; use snarkvm_utilities::{rand::UniformRand, to_bytes_le, ToBytes}; use rand::thread_rng; diff --git a/curves/src/edwards_bw6/tests.rs b/curves/src/edwards_bw6/tests.rs index 8b8e91bd95..a48ac57745 100644 --- a/curves/src/edwards_bw6/tests.rs +++ b/curves/src/edwards_bw6/tests.rs @@ -17,9 +17,15 @@ use crate::{ edwards_bw6::*, templates::twisted_edwards_extended::tests::{edwards_test, montgomery_conversion_test}, - traits::{tests_curve::curve_tests, tests_group::group_test, AffineCurve, Group, ProjectiveCurve}, + traits::{ + tests_curve::curve_tests, + tests_field::{field_serialization_test, field_test, primefield_test}, + tests_group::group_test, + AffineCurve, + Group, + ProjectiveCurve, + }, }; -use snarkvm_fields::tests_field::{field_serialization_test, field_test, primefield_test}; #[test] fn test_edwards_bw6_fr() { diff --git a/curves/src/traits/mod.rs b/curves/src/traits/mod.rs index 1c6ee71f0b..792cd5ffc0 100644 --- a/curves/src/traits/mod.rs +++ b/curves/src/traits/mod.rs @@ -20,6 +20,11 @@ pub use group::*; pub mod pairing_engine; pub use pairing_engine::*; +#[cfg(test)] +pub mod tests_field; + +#[cfg(test)] pub mod tests_group; +#[cfg(test)] pub mod tests_curve; diff --git a/fields/src/tests_field.rs b/curves/src/traits/tests_field.rs similarity index 99% rename from fields/src/tests_field.rs rename to curves/src/traits/tests_field.rs index 97345ef052..ecf3199aec 100644 --- a/fields/src/tests_field.rs +++ b/curves/src/traits/tests_field.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the snarkVM library. If not, see . -use crate::{traits::FftParameters, FftField, Field, LegendreSymbol, PrimeField, SquareRootField}; +use snarkvm_fields::{traits::FftParameters, FftField, Field, LegendreSymbol, PrimeField, SquareRootField}; use snarkvm_utilities::{ io::Cursor, serialize::{CanonicalDeserialize, CanonicalSerialize, Flags, SWFlags}, diff --git a/fields/Cargo.toml b/fields/Cargo.toml index ea75ec03bb..567f7c9fc8 100644 --- a/fields/Cargo.toml +++ b/fields/Cargo.toml @@ -25,9 +25,6 @@ default-features = false [dependencies.anyhow] version = "1.0" -[dependencies.bincode] -version = "1.3.3" - [dependencies.derivative] version = "2" @@ -35,10 +32,6 @@ version = "2" version = "0.8" default-features = false -[dependencies.rand_xorshift] -version = "0.3" -default-features = false - [dependencies.serde] version = "1.0" default-features = false diff --git a/fields/src/lib.rs b/fields/src/lib.rs index c6d9b7f344..9113ec568d 100644 --- a/fields/src/lib.rs +++ b/fields/src/lib.rs @@ -51,8 +51,6 @@ pub use fp12_2over3over2::*; mod legendre; pub use legendre::*; -pub mod tests_field; - mod to_field_vec; pub use to_field_vec::*; From 85be2ed171a0c47d4d53f573fbe92573c9253a66 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 04:51:08 -0700 Subject: [PATCH 07/17] Fix import --- curves/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/curves/Cargo.toml b/curves/Cargo.toml index e7bf1dce53..13124e2683 100644 --- a/curves/Cargo.toml +++ b/curves/Cargo.toml @@ -53,6 +53,9 @@ version = "1.3.3" [dev-dependencies.criterion] version = "0.3" +[dev-dependencies.rand] +version = "0.8" + [dev-dependencies.rand_xorshift] version = "0.3" default-features = false From c4d291f8a3797e9fae1fda5e5e94f13e602c9215 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 17:28:01 -0700 Subject: [PATCH 08/17] Updates byte serializer, updates groth16 proof --- Cargo.lock | 1 + algorithms/Cargo.toml | 3 + algorithms/src/snark/groth16/mod.rs | 100 +++++---- algorithms/src/snark/groth16/tests.rs | 142 +++++++++--- utilities/src/bytes.rs | 297 ++++++++++++++------------ utilities/src/errors/serialization.rs | 18 +- 6 files changed, 355 insertions(+), 206 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9b09cdaff..c26573622e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1611,6 +1611,7 @@ name = "snarkvm-algorithms" version = "0.7.5" dependencies = [ "anyhow", + "bincode", "blake2", "blake2s_simd", "criterion", diff --git a/algorithms/Cargo.toml b/algorithms/Cargo.toml index 0000518e0e..565f535533 100644 --- a/algorithms/Cargo.toml +++ b/algorithms/Cargo.toml @@ -151,6 +151,9 @@ default-features = false [dependencies.thiserror] version = "1.0" +[dev-dependencies.bincode] +version = "1" + [dev-dependencies.criterion] version = "0.3.5" diff --git a/algorithms/src/snark/groth16/mod.rs b/algorithms/src/snark/groth16/mod.rs index 918348f082..9fb4055a81 100644 --- a/algorithms/src/snark/groth16/mod.rs +++ b/algorithms/src/snark/groth16/mod.rs @@ -21,9 +21,17 @@ use snarkvm_curves::traits::{AffineCurve, PairingCurve, PairingEngine}; use snarkvm_fields::{ConstraintFieldError, Field, ToConstraintField}; use snarkvm_r1cs::{Index, LinearCombination}; -use snarkvm_utilities::{errors::SerializationError, serialize::*, FromBytes, ToBytes, ToMinimalBits}; +use snarkvm_utilities::{ + errors::SerializationError, + serialize::*, + FromBytes, + FromBytesDeserializer, + ToBytes, + ToBytesSerializer, + ToMinimalBits, +}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::io::{ Read, Result as IoResult, @@ -56,7 +64,7 @@ pub use prover::*; pub use verifier::*; /// A proof in the Groth16 SNARK. -#[derive(Clone, Debug, Eq, Serialize, Deserialize, CanonicalSerialize, CanonicalDeserialize)] +#[derive(Clone, Debug, Eq, CanonicalSerialize, CanonicalDeserialize)] pub struct Proof { pub a: E::G1Affine, pub b: E::G2Affine, @@ -64,46 +72,16 @@ pub struct Proof { pub(crate) compressed: bool, } -impl ToBytes for Proof { - #[inline] - fn write_le(&self, mut writer: W) -> IoResult<()> { - match self.compressed { - true => self.write_compressed(&mut writer), - false => self.write_uncompressed(&mut writer), - } - } -} - -impl FromBytes for Proof { - #[inline] - fn read_le(mut reader: R) -> IoResult { - Self::read(&mut reader) - } -} - -impl PartialEq for Proof { - fn eq(&self, other: &Self) -> bool { - self.a == other.a && self.b == other.b && self.c == other.c - } -} - -impl Default for Proof { - fn default() -> Self { - Self { - a: E::G1Affine::default(), - b: E::G2Affine::default(), - c: E::G1Affine::default(), - compressed: true, - } +impl Proof { + /// Returns `true` if the proof is in compressed form. + pub fn is_compressed(&self) -> bool { + self.compressed } -} -impl Proof { /// Serialize the proof into bytes in compressed form, for storage /// on disk or transmission over the network. pub fn write_compressed(&self, mut writer: W) -> IoResult<()> { CanonicalSerialize::serialize(self, &mut writer)?; - Ok(()) } @@ -170,6 +148,54 @@ impl Proof { } } +impl ToBytes for Proof { + #[inline] + fn write_le(&self, mut writer: W) -> IoResult<()> { + match self.compressed { + true => self.write_compressed(&mut writer), + false => self.write_uncompressed(&mut writer), + } + } +} + +impl FromBytes for Proof { + #[inline] + fn read_le(mut reader: R) -> IoResult { + Self::read(&mut reader) + } +} + +impl Serialize for Proof { + fn serialize(&self, serializer: S) -> Result { + ToBytesSerializer::serialize(self, serializer) + } +} + +impl<'de, E: PairingEngine> Deserialize<'de> for Proof { + fn deserialize>(deserializer: D) -> Result { + let compressed_size = Self::compressed_proof_size().map_err(serde::de::Error::custom)?; + let uncompressed_size = Self::uncompressed_proof_size().map_err(serde::de::Error::custom)?; + FromBytesDeserializer::::try_deserialize(deserializer, "proof", compressed_size, uncompressed_size) + } +} + +impl PartialEq for Proof { + fn eq(&self, other: &Self) -> bool { + self.a == other.a && self.b == other.b && self.c == other.c + } +} + +impl Default for Proof { + fn default() -> Self { + Self { + a: E::G1Affine::default(), + b: E::G2Affine::default(), + c: E::G1Affine::default(), + compressed: true, + } + } +} + /// A verification key in the Groth16 SNARK. #[derive(Clone, Debug, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct VerifyingKey { diff --git a/algorithms/src/snark/groth16/tests.rs b/algorithms/src/snark/groth16/tests.rs index 7fa3500507..5da0d95a7b 100644 --- a/algorithms/src/snark/groth16/tests.rs +++ b/algorithms/src/snark/groth16/tests.rs @@ -50,23 +50,28 @@ impl ConstraintSynthesizer for MySillyCircuit(&MySillyCircuit { a: None, b: None }, rng).unwrap(); for _ in 0..100 { - let a = Fr::rand(rng); - let b = Fr::rand(rng); - let mut c = a; - c.mul_assign(&b); + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); + let c = a * b; let proof = create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap(); let pvk = prepare_verifying_key::(parameters.vk.clone()); @@ -75,24 +80,69 @@ mod bls12_377 { assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); } } + + #[test] + fn test_bincode_compressed() { + let expected_proof = { + let rng = &mut thread_rng(); + let parameters = + generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); + + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); + create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap() + }; + + // Serialize + let expected_bytes = expected_proof.to_bytes_le().unwrap(); + assert_eq!(&expected_bytes[..], &bincode::serialize(&expected_proof).unwrap()[..]); + + // Deserialize + assert_eq!(expected_proof, bincode::deserialize(&expected_bytes[..]).unwrap()); + assert_eq!(expected_proof, Proof::read_le(&expected_bytes[..]).unwrap()); + } + + #[test] + fn test_bincode_uncompressed() { + let expected_proof = { + let rng = &mut thread_rng(); + let parameters = + generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); + + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); + create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap() + }; + + // Uncompressed bytes. + let mut expected_bytes = Vec::new(); + expected_proof.write_uncompressed(&mut expected_bytes).unwrap(); + + // Deserialize + assert_eq!(expected_proof, bincode::deserialize(&expected_bytes[..]).unwrap()); + assert_eq!(expected_proof, Proof::read_le(&expected_bytes[..]).unwrap()); + } } mod bw6_761 { use super::*; - use crate::snark::groth16::{create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof}; - + use crate::snark::groth16::{ + create_random_proof, + generate_random_parameters, + prepare_verifying_key, + verify_proof, + Proof, + }; use snarkvm_curves::bw6_761::{Fr, BW6_761}; - use snarkvm_utilities::rand::{test_rng, UniformRand}; + use snarkvm_utilities::{rand::UniformRand, FromBytes, ToBytes}; + + use rand::thread_rng; #[test] fn prove_and_verify() { - let rng = &mut test_rng(); - + let rng = &mut thread_rng(); let parameters = generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); - let a = Fr::rand(rng); - let b = Fr::rand(rng); + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); let c = a * b; let proof = create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap(); @@ -101,6 +151,46 @@ mod bw6_761 { assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); assert!(!verify_proof(&pvk, &proof, &[Fr::zero()]).unwrap()); } + + #[test] + fn test_bincode_compressed() { + let expected_proof = { + let rng = &mut thread_rng(); + let parameters = + generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); + + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); + create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap() + }; + + // Serialize + let expected_bytes = expected_proof.to_bytes_le().unwrap(); + assert_eq!(&expected_bytes[..], &bincode::serialize(&expected_proof).unwrap()[..]); + + // Deserialize + assert_eq!(expected_proof, bincode::deserialize(&expected_bytes[..]).unwrap()); + assert_eq!(expected_proof, Proof::read_le(&expected_bytes[..]).unwrap()); + } + + #[test] + fn test_bincode_uncompressed() { + let expected_proof = { + let rng = &mut thread_rng(); + let parameters = + generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); + + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); + create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap() + }; + + // Uncompressed bytes. + let mut expected_bytes = Vec::new(); + expected_proof.write_uncompressed(&mut expected_bytes).unwrap(); + + // Deserialize + assert_eq!(expected_proof, bincode::deserialize(&expected_bytes[..]).unwrap()); + assert_eq!(expected_proof, Proof::read_le(&expected_bytes[..]).unwrap()); + } } mod serialization { @@ -114,50 +204,46 @@ mod serialization { }; #[test] - fn test_compressed_proof_serialization() { + fn test_canonical_compressed() { let rng = &mut test_rng(); - let parameters = generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); - let a = Fr::rand(rng); - let b = Fr::rand(rng); - + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); let proof = create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap(); + // Serialize let compressed_serialization = proof.to_bytes_le().unwrap(); - assert_eq!( Proof::::compressed_proof_size().unwrap(), compressed_serialization.len() ); assert!(Proof::::read_uncompressed(&compressed_serialization[..]).is_err()); + // Deserialize let recovered_proof: Proof = FromBytes::read_le(&compressed_serialization[..]).unwrap(); assert_eq!(recovered_proof.compressed, true); } #[test] - fn test_uncompressed_proof_serialization() { + fn test_canonical_uncompressed() { let rng = &mut test_rng(); - let parameters = generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); - let a = Fr::rand(rng); - let b = Fr::rand(rng); - + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); let proof = create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap(); + // Serialize let mut uncompressed_serialization = Vec::new(); proof.write_uncompressed(&mut uncompressed_serialization).unwrap(); - assert_eq!( Proof::::uncompressed_proof_size().unwrap(), uncompressed_serialization.len() ); assert!(Proof::::read_compressed(&uncompressed_serialization[..]).is_err()); + // Deserialize let recovered_proof: Proof = FromBytes::read_le(&uncompressed_serialization[..]).unwrap(); assert_eq!(recovered_proof.compressed, false); } diff --git a/utilities/src/bytes.rs b/utilities/src/bytes.rs index 6add234c4b..b02dc879f5 100644 --- a/utilities/src/bytes.rs +++ b/utilities/src/bytes.rs @@ -28,43 +28,6 @@ use serde::{ Serializer, }; -#[inline] -pub fn from_bytes_le_to_bits_le(bytes: &[u8]) -> impl Iterator + DoubleEndedIterator + '_ { - bytes - .iter() - .map(|byte| (0..8).map(move |i| (*byte >> i) & 1 == 1)) - .flatten() -} - -#[inline] -pub fn from_bits_le_to_bytes_le(bits: &[bool]) -> Vec { - let desired_size = if bits.len() % 8 == 0 { - bits.len() / 8 - } else { - bits.len() / 8 + 1 - }; - - let mut bytes = Vec::with_capacity(desired_size); - for bits in bits.chunks(8) { - let mut result = 0u8; - for (i, bit) in bits.iter().enumerate() { - let bit_value = *bit as u8; - result += bit_value << i as u8; - } - - // Pad the bits if their number doesn't correspond to full bytes - if bits.len() < 8 { - for i in bits.len()..8 { - let bit_value = false as u8; - result += bit_value << i as u8; - } - } - bytes.push(result); - } - - bytes -} - /// Takes as input a sequence of structs, and converts them to a series of little-endian bytes. /// All traits that implement `ToBytes` can be automatically converted to bytes in this manner. #[macro_export] @@ -133,74 +96,138 @@ impl ToBytesSerializer { pub struct FromBytesDeserializer(String, Option, PhantomData); impl<'de, T: FromBytes> FromBytesDeserializer { + /// /// Deserializes a static-sized byte array (without length encoding). + /// + /// This method fails if `deserializer` is given an insufficient `size`. + /// pub fn deserialize>(deserializer: D, name: &str, size: usize) -> Result { - deserializer.deserialize_tuple(size, FromBytesVisitor::::new_with_size(name, size)) + let mut buffer = Vec::with_capacity(size); + deserializer.deserialize_tuple(size, FromBytesVisitor::new(&mut buffer, name))?; + FromBytes::read_le(&buffer[..]).map_err(de::Error::custom) } -} -pub struct FromBytesVisitor(String, Option, PhantomData); + /// + /// Attempts to deserialize a byte array (without length encoding). + /// + /// This method does *not* fail if `deserializer` is given an insufficient `size`, + /// however this method fails if `FromBytes` fails to read the value of `T`. + /// + pub fn try_deserialize>( + deserializer: D, + name: &str, + size_a: usize, + size_b: usize, + ) -> Result { + // Order the given sizes from smallest to largest. + let (size_a, size_b) = match size_a < size_b { + true => (size_a, size_b), + false => (size_b, size_a), + }; -impl<'de, T: FromBytes> FromBytesVisitor { - pub fn new(name: &str) -> Self { - Self(name.to_string(), None, PhantomData) + // Reserve a new `Vec` with the larger size capacity. + let mut buffer = Vec::with_capacity(size_b); + + // Attempt to deserialize on the larger size, to load up to the maximum buffer size. + match deserializer.deserialize_tuple(size_b, FromBytesVisitor::new(&mut buffer, name)) { + // Deserialized a full buffer, attempt to read up to `size_b`. + Ok(()) => FromBytes::read_le(&buffer[..size_b]).map_err(de::Error::custom), + // Deserialized a partial buffer, attempt to read up to `size_a`, if exactly `size_a` was read. + Err(error) => match buffer.len() == size_a { + true => FromBytes::read_le(&buffer[..size_a]).map_err(de::Error::custom), + false => Err(error), + }, + } } +} + +pub struct FromBytesVisitor<'a>(&'a mut Vec, String, Option); - pub fn new_with_size(name: &str, size: usize) -> Self { - Self(name.to_string(), Some(size), PhantomData) +impl<'a, 'de> FromBytesVisitor<'a> { + pub fn new(buffer: &'a mut Vec, name: &str) -> Self { + Self(buffer, name.to_string(), None) } } -impl<'de, T: FromBytes> Visitor<'de> for FromBytesVisitor { - type Value = T; +impl<'a, 'de> Visitor<'de> for FromBytesVisitor<'a> { + type Value = (); fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(&format!("a valid {} ", self.0)) + formatter.write_str(&format!("a valid {} ", self.1)) } fn visit_seq>(self, mut seq: S) -> Result { - let mut buffer = match self.1 { - Some(size) => Vec::with_capacity(size), - None => Vec::with_capacity(32), - }; while let Some(byte) = seq.next_element()? { - buffer.push(byte); + self.0.push(byte); + } + Ok(()) + } +} + +impl ToBytes for () { + #[inline] + fn write_le(&self, _writer: W) -> IoResult<()> { + Ok(()) + } +} + +impl FromBytes for () { + #[inline] + fn read_le(_bytes: R) -> IoResult { + Ok(()) + } +} + +impl ToBytes for bool { + #[inline] + fn write_le(&self, writer: W) -> IoResult<()> { + u8::write_le(&(*self as u8), writer) + } +} + +impl FromBytes for bool { + #[inline] + fn read_le(reader: R) -> IoResult { + match u8::read_le(reader) { + Ok(0) => Ok(false), + Ok(1) => Ok(true), + Ok(_) => Err(error("FromBytes::read failed")), + Err(err) => Err(err), } - FromBytes::read_le(&buffer[..]).map_err(de::Error::custom) } } -macro_rules! to_bytes_for_int_array { +macro_rules! impl_bytes_for_integer { ($int:ty) => { - impl ToBytes for [$int; N] { + impl ToBytes for $int { #[inline] fn write_le(&self, mut writer: W) -> IoResult<()> { - for num in self { - writer.write_all(&num.to_le_bytes())?; - } - Ok(()) + writer.write_all(&self.to_le_bytes()) } } - impl FromBytes for [$int; N] { + impl FromBytes for $int { #[inline] fn read_le(mut reader: R) -> IoResult { - let mut res: [$int; N] = [0; N]; - for num in res.iter_mut() { - let mut bytes = [0u8; core::mem::size_of::<$int>()]; - reader.read_exact(&mut bytes)?; - *num = <$int>::from_le_bytes(bytes); - } - Ok(res) + let mut bytes = [0u8; core::mem::size_of::<$int>()]; + reader.read_exact(&mut bytes)?; + Ok(<$int>::from_le_bytes(bytes)) } } }; } -// u8 has a dedicated, faster implementation -to_bytes_for_int_array!(u16); -to_bytes_for_int_array!(u32); -to_bytes_for_int_array!(u64); +impl_bytes_for_integer!(u8); +impl_bytes_for_integer!(u16); +impl_bytes_for_integer!(u32); +impl_bytes_for_integer!(u64); +impl_bytes_for_integer!(u128); + +impl_bytes_for_integer!(i8); +impl_bytes_for_integer!(i16); +impl_bytes_for_integer!(i32); +impl_bytes_for_integer!(i64); +impl_bytes_for_integer!(i128); impl ToBytes for [u8; N] { #[inline] @@ -218,85 +245,52 @@ impl FromBytes for [u8; N] { } } -impl ToBytes for (L, R) { - fn write_le(&self, mut writer: W) -> IoResult<()> { - self.0.write_le(&mut writer)?; - self.1.write_le(&mut writer)?; - Ok(()) - } -} - -impl FromBytes for (L, R) { - #[inline] - fn read_le(mut reader: Reader) -> IoResult { - let left: L = FromBytes::read_le(&mut reader)?; - let right: R = FromBytes::read_le(&mut reader)?; - Ok((left, right)) - } -} - -macro_rules! to_bytes_for_integer { +macro_rules! impl_bytes_for_integer_array { ($int:ty) => { - impl ToBytes for $int { + impl ToBytes for [$int; N] { #[inline] fn write_le(&self, mut writer: W) -> IoResult<()> { - writer.write_all(&self.to_le_bytes()) + for num in self { + writer.write_all(&num.to_le_bytes())?; + } + Ok(()) } } - impl FromBytes for $int { + impl FromBytes for [$int; N] { #[inline] fn read_le(mut reader: R) -> IoResult { - let mut bytes = [0u8; core::mem::size_of::<$int>()]; - reader.read_exact(&mut bytes)?; - Ok(<$int>::from_le_bytes(bytes)) + let mut res: [$int; N] = [0; N]; + for num in res.iter_mut() { + let mut bytes = [0u8; core::mem::size_of::<$int>()]; + reader.read_exact(&mut bytes)?; + *num = <$int>::from_le_bytes(bytes); + } + Ok(res) } } }; } -to_bytes_for_integer!(u8); -to_bytes_for_integer!(u16); -to_bytes_for_integer!(u32); -to_bytes_for_integer!(u64); -to_bytes_for_integer!(u128); - -to_bytes_for_integer!(i8); -to_bytes_for_integer!(i16); -to_bytes_for_integer!(i32); -to_bytes_for_integer!(i64); -to_bytes_for_integer!(i128); - -impl ToBytes for () { - #[inline] - fn write_le(&self, _writer: W) -> IoResult<()> { - Ok(()) - } -} +// u8 has a dedicated, faster implementation above +impl_bytes_for_integer_array!(u16); +impl_bytes_for_integer_array!(u32); +impl_bytes_for_integer_array!(u64); -impl FromBytes for () { - #[inline] - fn read_le(_bytes: R) -> IoResult { +impl ToBytes for (L, R) { + fn write_le(&self, mut writer: W) -> IoResult<()> { + self.0.write_le(&mut writer)?; + self.1.write_le(&mut writer)?; Ok(()) } } -impl ToBytes for bool { - #[inline] - fn write_le(&self, writer: W) -> IoResult<()> { - u8::write_le(&(*self as u8), writer) - } -} - -impl FromBytes for bool { +impl FromBytes for (L, R) { #[inline] - fn read_le(reader: R) -> IoResult { - match u8::read_le(reader) { - Ok(0) => Ok(false), - Ok(1) => Ok(true), - Ok(_) => Err(error("FromBytes::read failed")), - Err(err) => Err(err), - } + fn read_le(mut reader: Reader) -> IoResult { + let left: L = FromBytes::read_le(&mut reader)?; + let right: R = FromBytes::read_le(&mut reader)?; + Ok((left, right)) } } @@ -327,6 +321,45 @@ impl<'a, T: 'a + ToBytes> ToBytes for &'a T { } } +#[deprecated] +#[inline] +pub fn from_bytes_le_to_bits_le(bytes: &[u8]) -> impl Iterator + DoubleEndedIterator + '_ { + bytes + .iter() + .map(|byte| (0..8).map(move |i| (*byte >> i) & 1 == 1)) + .flatten() +} + +#[deprecated] +#[inline] +pub fn from_bits_le_to_bytes_le(bits: &[bool]) -> Vec { + let desired_size = if bits.len() % 8 == 0 { + bits.len() / 8 + } else { + bits.len() / 8 + 1 + }; + + let mut bytes = Vec::with_capacity(desired_size); + for bits in bits.chunks(8) { + let mut result = 0u8; + for (i, bit) in bits.iter().enumerate() { + let bit_value = *bit as u8; + result += bit_value << i as u8; + } + + // Pad the bits if their number doesn't correspond to full bytes + if bits.len() < 8 { + for i in bits.len()..8 { + let bit_value = false as u8; + result += bit_value << i as u8; + } + } + bytes.push(result); + } + + bytes +} + #[cfg(test)] mod test { use super::{from_bits_le_to_bytes_le, from_bytes_le_to_bits_le, ToBytes}; diff --git a/utilities/src/errors/serialization.rs b/utilities/src/errors/serialization.rs index f0dd3234a3..f60ff08988 100644 --- a/utilities/src/errors/serialization.rs +++ b/utilities/src/errors/serialization.rs @@ -16,22 +16,22 @@ #[derive(Error, Debug)] pub enum SerializationError { - /// During serialization, we didn't have enough space to write extra info. - #[error("the last byte does not have enough space to encode the extra info bits")] - NotEnoughSpace, + /// During serialization with bincode, we encountered a serialization issue + #[error(transparent)] + BincodeError(#[from] bincode::Error), /// During serialization, the data was invalid. #[error("the input buffer contained invalid data")] InvalidData, + /// During serialization, we countered an I/O error. + #[error("IoError: {0}")] + IoError(#[from] crate::io::Error), + /// During serialization, we didn't have enough space to write extra info. + #[error("the last byte does not have enough space to encode the extra info bits")] + NotEnoughSpace, /// During serialization, non-empty flags were given where none were /// expected. #[error("the call expects empty flags")] UnexpectedFlags, - /// During serialization, we countered an I/O error. - #[error("IoError: {0}")] - IoError(#[from] crate::io::Error), - /// During serialization with bincode, we encountered a serialization issue - #[error(transparent)] - BincodeError(#[from] bincode::Error), } impl From for crate::io::Error { From 6cda3084db1c51cca4d0167c3a92079c3f8eaa38 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 18:11:43 -0700 Subject: [PATCH 09/17] Adds hex string for groth proof --- algorithms/Cargo.toml | 3 ++ algorithms/src/snark/groth16/mod.rs | 47 +++++++++++++++++---- algorithms/src/snark/groth16/tests.rs | 59 +++++++++++++++++++++++++-- curves/src/traits/pairing_engine.rs | 3 ++ dpc/src/record/ciphertext.rs | 2 +- 5 files changed, 101 insertions(+), 13 deletions(-) diff --git a/algorithms/Cargo.toml b/algorithms/Cargo.toml index 565f535533..3c00d57a3c 100644 --- a/algorithms/Cargo.toml +++ b/algorithms/Cargo.toml @@ -114,6 +114,9 @@ version = "2" [dependencies.digest] version = "0.9" +[dependencies.hex] +version = "0.4.3" + [dependencies.itertools] version = "0.10.1" diff --git a/algorithms/src/snark/groth16/mod.rs b/algorithms/src/snark/groth16/mod.rs index 9fb4055a81..4d7b036a69 100644 --- a/algorithms/src/snark/groth16/mod.rs +++ b/algorithms/src/snark/groth16/mod.rs @@ -23,7 +23,9 @@ use snarkvm_fields::{ConstraintFieldError, Field, ToConstraintField}; use snarkvm_r1cs::{Index, LinearCombination}; use snarkvm_utilities::{ errors::SerializationError, + fmt, serialize::*, + str::FromStr, FromBytes, FromBytesDeserializer, ToBytes, @@ -31,7 +33,7 @@ use snarkvm_utilities::{ ToMinimalBits, }; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::io::{ Read, Result as IoResult, @@ -148,6 +150,13 @@ impl Proof { } } +impl FromBytes for Proof { + #[inline] + fn read_le(mut reader: R) -> IoResult { + Self::read(&mut reader) + } +} + impl ToBytes for Proof { #[inline] fn write_le(&self, mut writer: W) -> IoResult<()> { @@ -158,24 +167,44 @@ impl ToBytes for Proof { } } -impl FromBytes for Proof { - #[inline] - fn read_le(mut reader: R) -> IoResult { - Self::read(&mut reader) +impl FromStr for Proof { + type Err = anyhow::Error; + + fn from_str(proof_hex: &str) -> Result { + Self::from_bytes_le(&hex::decode(proof_hex)?) + } +} + +impl fmt::Display for Proof { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let proof_hex = hex::encode(self.to_bytes_le().expect("Failed to convert proof to bytes")); + write!(f, "{}", proof_hex) } } impl Serialize for Proof { fn serialize(&self, serializer: S) -> Result { - ToBytesSerializer::serialize(self, serializer) + match serializer.is_human_readable() { + true => serializer.collect_str(self), + false => ToBytesSerializer::serialize(self, serializer), + } } } impl<'de, E: PairingEngine> Deserialize<'de> for Proof { fn deserialize>(deserializer: D) -> Result { - let compressed_size = Self::compressed_proof_size().map_err(serde::de::Error::custom)?; - let uncompressed_size = Self::uncompressed_proof_size().map_err(serde::de::Error::custom)?; - FromBytesDeserializer::::try_deserialize(deserializer, "proof", compressed_size, uncompressed_size) + match deserializer.is_human_readable() { + true => { + let s: String = Deserialize::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(de::Error::custom) + } + false => FromBytesDeserializer::::try_deserialize( + deserializer, + "proof", + Self::compressed_proof_size().map_err(de::Error::custom)?, + Self::uncompressed_proof_size().map_err(de::Error::custom)?, + ), + } } } diff --git a/algorithms/src/snark/groth16/tests.rs b/algorithms/src/snark/groth16/tests.rs index 5da0d95a7b..d4c610fe32 100644 --- a/algorithms/src/snark/groth16/tests.rs +++ b/algorithms/src/snark/groth16/tests.rs @@ -58,9 +58,8 @@ mod bls12_377 { Proof, }; use snarkvm_curves::bls12_377::{Bls12_377, Fr}; - use snarkvm_utilities::{FromBytes, ToBytes, UniformRand}; + use snarkvm_utilities::{str::FromStr, FromBytes, ToBytes, UniformRand}; - use core::ops::MulAssign; use rand::thread_rng; #[test] @@ -81,6 +80,33 @@ mod bls12_377 { } } + #[test] + fn test_serde_json() { + let expected_proof = { + let rng = &mut thread_rng(); + let parameters = + generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); + + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); + create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap() + }; + + // Serialize + let expected_string = &expected_proof.to_string(); + let candidate_string = serde_json::to_string(&expected_proof).unwrap(); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!(expected_proof, serde_json::from_str(&candidate_string).unwrap()); + assert_eq!(expected_proof, Proof::from_str(&expected_string).unwrap()); + } + #[test] fn test_bincode_compressed() { let expected_proof = { @@ -132,7 +158,7 @@ mod bw6_761 { Proof, }; use snarkvm_curves::bw6_761::{Fr, BW6_761}; - use snarkvm_utilities::{rand::UniformRand, FromBytes, ToBytes}; + use snarkvm_utilities::{rand::UniformRand, str::FromStr, FromBytes, ToBytes}; use rand::thread_rng; @@ -152,6 +178,33 @@ mod bw6_761 { assert!(!verify_proof(&pvk, &proof, &[Fr::zero()]).unwrap()); } + #[test] + fn test_serde_json() { + let expected_proof = { + let rng = &mut thread_rng(); + let parameters = + generate_random_parameters::(&MySillyCircuit { a: None, b: None }, rng).unwrap(); + + let (a, b) = (Fr::rand(rng), Fr::rand(rng)); + create_random_proof(&MySillyCircuit { a: Some(a), b: Some(b) }, ¶meters, rng).unwrap() + }; + + // Serialize + let expected_string = &expected_proof.to_string(); + let candidate_string = serde_json::to_string(&expected_proof).unwrap(); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!(expected_proof, serde_json::from_str(&candidate_string).unwrap()); + assert_eq!(expected_proof, Proof::from_str(&expected_string).unwrap()); + } + #[test] fn test_bincode_compressed() { let expected_proof = { diff --git a/curves/src/traits/pairing_engine.rs b/curves/src/traits/pairing_engine.rs index a001484c04..b09af952fa 100644 --- a/curves/src/traits/pairing_engine.rs +++ b/curves/src/traits/pairing_engine.rs @@ -18,6 +18,7 @@ use crate::traits::Group; use snarkvm_fields::{Field, PrimeField, SquareRootField, ToConstraintField}; use snarkvm_utilities::{biginteger::BigInteger, serialize::*, BitIteratorBE, ToBytes, ToMinimalBits}; +use serde::{de::DeserializeOwned, Serialize}; use std::{fmt::Debug, iter}; pub trait PairingEngine: Sized + 'static + Copy + Debug + PartialEq + Eq + Sync + Send { @@ -156,6 +157,8 @@ pub trait ProjectiveCurve: pub trait AffineCurve: Group + Sized + + Serialize + + DeserializeOwned + CanonicalSerialize + ConstantSerializedSize + CanonicalDeserialize diff --git a/dpc/src/record/ciphertext.rs b/dpc/src/record/ciphertext.rs index b8613eea2f..32ee63b880 100644 --- a/dpc/src/record/ciphertext.rs +++ b/dpc/src/record/ciphertext.rs @@ -143,7 +143,7 @@ impl FromStr for RecordCiphertext { type Err = RecordError; fn from_str(payload_hex: &str) -> Result { - Ok(Self::read_le(&hex::decode(payload_hex)?[..])?) + Ok(Self::from_bytes_le(&hex::decode(payload_hex)?)?) } } From 227df5a3884492d7d7e538372d3cce146d0f9330 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 18:21:30 -0700 Subject: [PATCH 10/17] Add sanity check --- algorithms/src/snark/groth16/tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/algorithms/src/snark/groth16/tests.rs b/algorithms/src/snark/groth16/tests.rs index d4c610fe32..cb5be49ce8 100644 --- a/algorithms/src/snark/groth16/tests.rs +++ b/algorithms/src/snark/groth16/tests.rs @@ -94,6 +94,7 @@ mod bls12_377 { // Serialize let expected_string = &expected_proof.to_string(); let candidate_string = serde_json::to_string(&expected_proof).unwrap(); + assert_eq!(388, candidate_string.len(), "Update me if serialization has changed"); assert_eq!( expected_string, serde_json::Value::from_str(&candidate_string) @@ -192,6 +193,7 @@ mod bw6_761 { // Serialize let expected_string = &expected_proof.to_string(); let candidate_string = serde_json::to_string(&expected_proof).unwrap(); + assert_eq!(580, candidate_string.len(), "Update me if serialization has changed"); assert_eq!( expected_string, serde_json::Value::from_str(&candidate_string) From 42b6089b3c8fc6c6be44bd49da9849bbc28e252f Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 18:43:25 -0700 Subject: [PATCH 11/17] Update signature serializers --- algorithms/src/signature/aleo.rs | 75 ++++++++++++++++++++++++----- algorithms/src/signature/tests.rs | 63 ++++++++++++++++++++++-- algorithms/src/snark/groth16/mod.rs | 4 ++ dpc/src/transition/transition.rs | 1 + 4 files changed, 125 insertions(+), 18 deletions(-) diff --git a/algorithms/src/signature/aleo.rs b/algorithms/src/signature/aleo.rs index bee57ea112..efeb4e2fd4 100644 --- a/algorithms/src/signature/aleo.rs +++ b/algorithms/src/signature/aleo.rs @@ -31,22 +31,26 @@ use snarkvm_curves::{ }; use snarkvm_fields::{ConstraintFieldError, Field, FieldParameters, PrimeField, ToConstraintField}; use snarkvm_utilities::{ + fmt, io::{Read, Result as IoResult, Write}, ops::Mul, rand::UniformRand, serialize::*, + str::FromStr, FromBits, FromBytes, + FromBytesDeserializer, ToBits, ToBytes, + ToBytesSerializer, }; use anyhow::Result; use itertools::Itertools; use rand::{CryptoRng, Rng}; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; -#[derive(Derivative, Serialize, Deserialize)] +#[derive(Derivative)] #[derivative( Copy(bound = "TE: TwistedEdwardsParameters"), Clone(bound = "TE: TwistedEdwardsParameters"), @@ -63,6 +67,11 @@ pub struct AleoSignature { } impl AleoSignature { + #[inline] + pub fn size() -> usize { + 2 * TE::ScalarField::SERIALIZED_SIZE + 2 * TE::BaseField::SERIALIZED_SIZE + } + #[inline] pub fn root_public_key(&self) -> Result> { if let Some(element) = TEAffine::::from_x_coordinate(self.root_public_key, true) { @@ -98,16 +107,6 @@ impl AleoSignature { } } -impl ToBytes for AleoSignature { - #[inline] - fn write_le(&self, mut writer: W) -> IoResult<()> { - self.prover_response.write_le(&mut writer)?; - self.verifier_challenge.write_le(&mut writer)?; - self.root_public_key.write_le(&mut writer)?; - self.root_randomizer.write_le(&mut writer) - } -} - impl FromBytes for AleoSignature { #[inline] fn read_le(mut reader: R) -> IoResult { @@ -125,6 +124,56 @@ impl FromBytes for AleoSignature { } } +impl ToBytes for AleoSignature { + #[inline] + fn write_le(&self, mut writer: W) -> IoResult<()> { + self.prover_response.write_le(&mut writer)?; + self.verifier_challenge.write_le(&mut writer)?; + self.root_public_key.write_le(&mut writer)?; + self.root_randomizer.write_le(&mut writer) + } +} + +impl FromStr for AleoSignature { + type Err = anyhow::Error; + + #[inline] + fn from_str(signature_hex: &str) -> Result { + Self::from_bytes_le(&hex::decode(signature_hex)?) + } +} + +impl fmt::Display for AleoSignature { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let signature_hex = hex::encode(self.to_bytes_le().expect("Failed to convert signature to bytes")); + write!(f, "{}", signature_hex) + } +} + +impl Serialize for AleoSignature { + #[inline] + fn serialize(&self, serializer: S) -> Result { + match serializer.is_human_readable() { + true => serializer.collect_str(self), + false => ToBytesSerializer::serialize(self, serializer), + } + } +} + +impl<'de, TE: TwistedEdwardsParameters> Deserialize<'de> for AleoSignature { + #[inline] + fn deserialize>(deserializer: D) -> Result { + match deserializer.is_human_readable() { + true => { + let s: String = Deserialize::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(de::Error::custom) + } + false => FromBytesDeserializer::::deserialize(deserializer, "signature", Self::size()), + } + } +} + #[derive(Derivative)] #[derivative( Clone(bound = "TE: TwistedEdwardsParameters"), @@ -136,7 +185,7 @@ pub struct AleoSignatureScheme where TE::BaseField: PoseidonDefaultParametersField, { - pub g_bases: Vec>, + g_bases: Vec>, crypto_hash: PoseidonCryptoHash, } diff --git a/algorithms/src/signature/tests.rs b/algorithms/src/signature/tests.rs index acb15adaf9..7ebc187cdc 100644 --- a/algorithms/src/signature/tests.rs +++ b/algorithms/src/signature/tests.rs @@ -17,11 +17,10 @@ use crate::SignatureScheme; use snarkvm_utilities::FromBytes; -use rand::SeedableRng; -use rand_chacha::ChaChaRng; +use rand::thread_rng; fn sign_and_verify(message: &[u8]) { - let rng = &mut ChaChaRng::seed_from_u64(1231275789u64); + let rng = &mut thread_rng(); let signature_scheme = S::setup("sign_and_verify"); let private_key = signature_scheme.generate_private_key(rng); @@ -31,7 +30,7 @@ fn sign_and_verify(message: &[u8]) { } fn failed_verification(message: &[u8], bad_message: &[u8]) { - let rng = &mut ChaChaRng::seed_from_u64(1231275789u64); + let rng = &mut thread_rng(); let signature_scheme = S::setup("failed_verification"); let private_key = signature_scheme.generate_private_key(rng); @@ -48,11 +47,12 @@ fn signature_scheme_serialization() { mod aleo { use super::*; - use crate::signature::AleoSignatureScheme; + use crate::signature::{AleoSignature, AleoSignatureScheme}; use snarkvm_curves::{ edwards_bls12::EdwardsParameters as EdwardsBls12, edwards_bw6::EdwardsParameters as EdwardsBW6, }; + use snarkvm_utilities::{str::FromStr, FromBytes, ToBytes}; #[test] fn test_aleo_signature_on_edwards_bls12_377() { @@ -77,4 +77,57 @@ mod aleo { signature_scheme_serialization::>(); signature_scheme_serialization::>(); } + + #[test] + fn test_serde_json() { + type TestSignature = AleoSignatureScheme; + + let expected_signature = { + let rng = &mut thread_rng(); + let signature_scheme = TestSignature::setup("test_serde_json"); + let private_key = signature_scheme.generate_private_key(rng); + let message = b"Hi, I am an Aleo signature!"; + signature_scheme.sign(&private_key, message, rng).unwrap() + }; + + // Serialize + let expected_string = &expected_signature.to_string(); + let candidate_string = serde_json::to_string(&expected_signature).unwrap(); + assert_eq!(258, candidate_string.len(), "Update me if serialization has changed"); + assert_eq!( + expected_string, + serde_json::Value::from_str(&candidate_string) + .unwrap() + .as_str() + .unwrap() + ); + + // Deserialize + assert_eq!(expected_signature, serde_json::from_str(&candidate_string).unwrap()); + assert_eq!(expected_signature, AleoSignature::from_str(&expected_string).unwrap()); + } + + #[test] + fn test_bincode() { + type TestSignature = AleoSignatureScheme; + + let expected_signature = { + let rng = &mut thread_rng(); + let signature_scheme = TestSignature::setup("test_bincode"); + let private_key = signature_scheme.generate_private_key(rng); + let message = b"Hi, I am an Aleo signature!"; + signature_scheme.sign(&private_key, message, rng).unwrap() + }; + + // Serialize + let expected_bytes = expected_signature.to_bytes_le().unwrap(); + assert_eq!( + &expected_bytes[..], + &bincode::serialize(&expected_signature).unwrap()[..] + ); + + // Deserialize + assert_eq!(expected_signature, bincode::deserialize(&expected_bytes[..]).unwrap()); + assert_eq!(expected_signature, AleoSignature::read_le(&expected_bytes[..]).unwrap()); + } } diff --git a/algorithms/src/snark/groth16/mod.rs b/algorithms/src/snark/groth16/mod.rs index 4d7b036a69..ab7761444f 100644 --- a/algorithms/src/snark/groth16/mod.rs +++ b/algorithms/src/snark/groth16/mod.rs @@ -170,12 +170,14 @@ impl ToBytes for Proof { impl FromStr for Proof { type Err = anyhow::Error; + #[inline] fn from_str(proof_hex: &str) -> Result { Self::from_bytes_le(&hex::decode(proof_hex)?) } } impl fmt::Display for Proof { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let proof_hex = hex::encode(self.to_bytes_le().expect("Failed to convert proof to bytes")); write!(f, "{}", proof_hex) @@ -183,6 +185,7 @@ impl fmt::Display for Proof { } impl Serialize for Proof { + #[inline] fn serialize(&self, serializer: S) -> Result { match serializer.is_human_readable() { true => serializer.collect_str(self), @@ -192,6 +195,7 @@ impl Serialize for Proof { } impl<'de, E: PairingEngine> Deserialize<'de> for Proof { + #[inline] fn deserialize>(deserializer: D) -> Result { match deserializer.is_human_readable() { true => { diff --git a/dpc/src/transition/transition.rs b/dpc/src/transition/transition.rs index 8ca81dfc31..5e1c4a7ec2 100644 --- a/dpc/src/transition/transition.rs +++ b/dpc/src/transition/transition.rs @@ -385,6 +385,7 @@ mod tests { // Serialize let expected_string = &expected_transition.to_string(); let candidate_string = serde_json::to_string(&expected_transition).unwrap(); + assert_eq!(2710, candidate_string.len(), "Update me if serialization has changed"); assert_eq!( expected_string, serde_json::Value::from_str(&candidate_string) From 9c71fe4c22d302169e3a82f315a7f1b63b0f6214 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 18:46:51 -0700 Subject: [PATCH 12/17] Update parameters in signature --- algorithms/src/signature/aleo.rs | 4 ++-- algorithms/src/traits/signature.rs | 2 +- gadgets/src/algorithms/signature/aleo.rs | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/algorithms/src/signature/aleo.rs b/algorithms/src/signature/aleo.rs index efeb4e2fd4..f2ea41f5f5 100644 --- a/algorithms/src/signature/aleo.rs +++ b/algorithms/src/signature/aleo.rs @@ -221,8 +221,8 @@ where Self { g_bases, crypto_hash } } - fn parameters(&self) -> Self::Parameters { - self.g_bases.clone() + fn parameters(&self) -> &Self::Parameters { + &self.g_bases } /// diff --git a/algorithms/src/traits/signature.rs b/algorithms/src/traits/signature.rs index 6910b90d48..5579b5620d 100644 --- a/algorithms/src/traits/signature.rs +++ b/algorithms/src/traits/signature.rs @@ -30,7 +30,7 @@ pub trait SignatureScheme: fn setup(message: &str) -> Self; - fn parameters(&self) -> Self::Parameters; + fn parameters(&self) -> &Self::Parameters; fn generate_private_key(&self, rng: &mut R) -> Self::PrivateKey; diff --git a/gadgets/src/algorithms/signature/aleo.rs b/gadgets/src/algorithms/signature/aleo.rs index d015f248d5..26c179f9f6 100644 --- a/gadgets/src/algorithms/signature/aleo.rs +++ b/gadgets/src/algorithms/signature/aleo.rs @@ -33,6 +33,7 @@ use crate::{ use snarkvm_algorithms::{ crypto_hash::PoseidonDefaultParametersField, signature::{AleoSignature, AleoSignatureScheme}, + SignatureScheme, }; use snarkvm_curves::{templates::twisted_edwards_extended::Affine as TEAffine, TwistedEdwardsParameters}; use snarkvm_fields::{FieldParameters, PrimeField}; @@ -411,7 +412,7 @@ impl, F: PrimeField + PoseidonDefaul // Compute G^s. let g_s = { let mut g_s = zero_affine.clone(); - for (i, (base, bit)) in self.signature.g_bases.iter().zip_eq(s).enumerate() { + for (i, (base, bit)) in self.signature.parameters().iter().zip_eq(s).enumerate() { let added = g_s.add_constant(cs.ns(|| format!("add_g_s_{}", i)), base)?; g_s = TEAffineGadget::::conditionally_select( From 10a7cea342295fef29eabe4f9d437bbbd29231a2 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 19:21:22 -0700 Subject: [PATCH 13/17] Fix transaction decrypted record test --- dpc/src/transaction/transaction.rs | 57 +++++++++++------------- gadgets/src/algorithms/signature/aleo.rs | 2 +- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/dpc/src/transaction/transaction.rs b/dpc/src/transaction/transaction.rs index e6ad8ee1c8..3ae9813132 100644 --- a/dpc/src/transaction/transaction.rs +++ b/dpc/src/transaction/transaction.rs @@ -31,13 +31,14 @@ use snarkvm_utilities::{has_duplicates, FromBytes, ToBytes}; use anyhow::{anyhow, Result}; use rand::{CryptoRng, Rng}; +use serde::{Deserialize, Serialize}; use std::{ collections::HashSet, hash::{Hash, Hasher}, io::{Read, Result as IoResult, Write}, }; -#[derive(Derivative)] +#[derive(Derivative, Serialize, Deserialize)] #[derivative( Clone(bound = "N: Network"), Debug(bound = "N: Network"), @@ -271,17 +272,15 @@ impl Transaction { .collect::>>() } - /// Returns the records that can be decrypted with the given account view key. - pub fn to_decrypted_records(&self, account_view_key: &ViewKey) -> Result>> { + /// Returns records from the transaction belonging to the given account view key. + #[inline] + pub fn to_decrypted_records(&self, account_view_key: &ViewKey) -> Vec> { self.transitions .iter() .flat_map(Transition::ciphertexts) - .map(|c| c.decrypt(account_view_key)) - .filter(|decryption_result| match decryption_result { - Ok(r) => !r.is_dummy(), - Err(_) => true, - }) - .collect::>>>() + .filter_map(|c| c.decrypt(account_view_key).ok()) + .filter(|record| !record.is_dummy()) + .collect() } /// Returns the transaction ID. @@ -347,39 +346,33 @@ mod tests { use crate::{testnet2::Testnet2, Account, AccountScheme, Payload}; use snarkvm_utilities::UniformRand; - use rand::{Rng, SeedableRng}; - use rand_chacha::ChaChaRng; + use rand::thread_rng; - #[ignore] #[test] fn test_decrypt_records() { - let rng = &mut ChaChaRng::seed_from_u64(1231275789u64); - let dummy_account = Account::::new(rng); - - // Construct output records - let mut payload = [0u8; Testnet2::PAYLOAD_SIZE_IN_BYTES]; - rng.fill(&mut payload); - let record = Record::new_output( - dummy_account.address(), + let rng = &mut thread_rng(); + let account = Account::::new(rng); + + // Craft the expected coinbase record. + let expected_record = Record::new_output( + account.address(), 1234, - Payload::from_bytes_le(&payload).unwrap(), + Payload::default(), *Testnet2::noop_program_id(), UniformRand::rand(rng), rng, ) .unwrap(); - let dummy_record = Record::new_noop_output(dummy_account.address(), UniformRand::rand(rng), rng).unwrap(); - - // Encrypt output records - let (_encrypted_record, _) = RecordCiphertext::encrypt(&record, rng).unwrap(); - let (_encrypted_dummy_record, _) = RecordCiphertext::encrypt(&dummy_record, rng).unwrap(); - let account_view_key = ViewKey::from_private_key(&dummy_account.private_key()); - // Construct transaction with 1 output record and 1 dummy output record - let transaction = Transaction::new_coinbase(dummy_account.address(), AleoAmount(1234), rng).unwrap(); + // Craft a transaction with 1 coinbase record. + let transaction = Transaction::new_coinbase(account.address(), AleoAmount(1234), rng).unwrap(); + let decrypted_records = transaction.to_decrypted_records(&account.view_key()); + assert_eq!(decrypted_records.len(), 1); // Excludes dummy records upon decryption. - let decrypted_records = transaction.to_decrypted_records(&account_view_key).unwrap(); - assert_eq!(decrypted_records.len(), 1); // Excludes dummy record upon decryption - assert_eq!(decrypted_records.first().unwrap(), &record); + let candidate_record = decrypted_records.first().unwrap(); + assert_eq!(expected_record.owner(), candidate_record.owner()); + assert_eq!(expected_record.value(), candidate_record.value()); + assert_eq!(expected_record.payload(), candidate_record.payload()); + assert_eq!(expected_record.program_id(), candidate_record.program_id()); } } diff --git a/gadgets/src/algorithms/signature/aleo.rs b/gadgets/src/algorithms/signature/aleo.rs index 26c179f9f6..5d8b8490fd 100644 --- a/gadgets/src/algorithms/signature/aleo.rs +++ b/gadgets/src/algorithms/signature/aleo.rs @@ -514,7 +514,7 @@ impl, F: PrimeField + PoseidonDefaul // Compute G^sk_prf. let g_sk_prf = { let mut g_sk_prf = zero_affine.clone(); - for (i, (base, bit)) in self.signature.g_bases.iter().zip_eq(sk_prf).enumerate() { + for (i, (base, bit)) in self.signature.parameters().iter().zip_eq(sk_prf).enumerate() { let added = g_sk_prf.add_constant(cs.ns(|| format!("add_g_sk_prf_{}", i)), base)?; g_sk_prf = TEAffineGadget::::conditionally_select( From 3664d4cc547bba12b565ff7c097399ee316c5ced Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 20:14:04 -0700 Subject: [PATCH 14/17] Update hasher for Transaction, adds transaction_id into struct --- dpc/src/block/transactions.rs | 5 +--- dpc/src/ledger/memory_pool.rs | 10 ++------ dpc/src/transaction/transaction.rs | 38 +++++++++++++++++++----------- dpc/src/transition/transition.rs | 8 +++++++ 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/dpc/src/block/transactions.rs b/dpc/src/block/transactions.rs index 059983480f..90d16ef656 100644 --- a/dpc/src/block/transactions.rs +++ b/dpc/src/block/transactions.rs @@ -107,10 +107,7 @@ impl Transactions { pub fn to_transactions_root(&self) -> Result { match self.is_valid() { true => { - let transaction_ids = (*self) - .iter() - .map(Transaction::to_transaction_id) - .collect::>>()?; + let transaction_ids = (*self).iter().map(Transaction::transaction_id).collect::>(); Ok(*MerkleTree::::new( Arc::new(N::transactions_tree_parameters().clone()), diff --git a/dpc/src/ledger/memory_pool.rs b/dpc/src/ledger/memory_pool.rs index 0c7c77386d..45db2fef16 100644 --- a/dpc/src/ledger/memory_pool.rs +++ b/dpc/src/ledger/memory_pool.rs @@ -40,13 +40,7 @@ impl MemoryPool { /// Returns `true` if the given transaction exists in the memory pool. pub fn contains_transaction(&self, transaction: &Transaction) -> bool { - match transaction.to_transaction_id() { - Ok(id) => self.transactions.contains_key(&id), - Err(error) => { - eprintln!("Failed to lookup transaction ID: {}", error); - return false; - } - } + self.transactions.contains_key(&transaction.transaction_id()) } /// Returns the transactions in the memory pool. @@ -67,7 +61,7 @@ impl MemoryPool { } // Ensure the transaction does not already exist in the memory pool. - let transaction_id = transaction.to_transaction_id()?; + let transaction_id = transaction.transaction_id(); if self.transactions.contains_key(&transaction_id) { return Err(anyhow!("Transaction already exists in memory pool")); } diff --git a/dpc/src/transaction/transaction.rs b/dpc/src/transaction/transaction.rs index 3ae9813132..b61637950d 100644 --- a/dpc/src/transaction/transaction.rs +++ b/dpc/src/transaction/transaction.rs @@ -27,7 +27,7 @@ use crate::{ VirtualMachine, }; use snarkvm_algorithms::CRH; -use snarkvm_utilities::{has_duplicates, FromBytes, ToBytes}; +use snarkvm_utilities::{has_duplicates, to_bytes_le, FromBytes, ToBytes}; use anyhow::{anyhow, Result}; use rand::{CryptoRng, Rng}; @@ -46,6 +46,8 @@ use std::{ Eq(bound = "N: Network") )] pub struct Transaction { + /// The ID of this transaction. + transaction_id: N::TransactionID, /// The network ID. network_id: u16, /// The ID of the inner circuit used to execute this transaction. @@ -78,7 +80,10 @@ impl Transaction { transitions: Vec>, events: Vec>, ) -> Result { + let transaction_id = Self::compute_transaction_id(&transitions)?; + let transaction = Self { + transaction_id, network_id, inner_circuit_id, transitions, @@ -194,6 +199,12 @@ impl Transaction { true } + /// Returns the transaction ID. + #[inline] + pub fn transaction_id(&self) -> N::TransactionID { + self.transaction_id + } + /// Returns the network ID. #[inline] pub fn network_id(&self) -> u16 { @@ -257,6 +268,12 @@ impl Transaction { &self.transitions } + /// Returns the transition IDs. + #[inline] + pub fn transition_ids(&self) -> Vec { + self.transitions.iter().map(Transition::transition_id).collect() + } + /// Returns a reference to the events. #[inline] pub fn events(&self) -> &Vec> { @@ -283,17 +300,12 @@ impl Transaction { .collect() } - /// Returns the transaction ID. + /// Transaction ID := Hash(transition IDs) #[inline] - pub fn to_transaction_id(&self) -> Result { - let transition_ids = self - .transitions - .iter() - .map(|transition| Ok(transition.transition_id().to_bytes_le()?)) - .collect::>>()? - .concat(); - - Ok(N::transaction_id_crh().hash(&transition_ids)?) + pub(crate) fn compute_transaction_id(transitions: &Vec>) -> Result { + Ok(N::transaction_id_crh().hash(&to_bytes_le![ + transitions.iter().map(Transition::transition_id).collect::>() + ]?)?) } } @@ -334,9 +346,7 @@ impl ToBytes for Transaction { impl Hash for Transaction { #[inline] fn hash(&self, state: &mut H) { - self.to_transaction_id() - .expect("Failed to compute the transaction ID") - .hash(state); + self.transaction_id().hash(state); } } diff --git a/dpc/src/transition/transition.rs b/dpc/src/transition/transition.rs index 5e1c4a7ec2..612f001b12 100644 --- a/dpc/src/transition/transition.rs +++ b/dpc/src/transition/transition.rs @@ -22,6 +22,7 @@ use anyhow::{anyhow, Result}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt, + hash::{Hash, Hasher}, io::{Read, Result as IoResult, Write}, str::FromStr, }; @@ -362,6 +363,13 @@ impl<'de, N: Network> Deserialize<'de> for Transition { } } +impl Hash for Transition { + #[inline] + fn hash(&self, state: &mut H) { + self.transition_id().hash(state); + } +} + #[cfg(test)] mod tests { use super::*; From 495a1985e39d5ca116854a5dd79dbad51b6543c0 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sat, 16 Oct 2021 20:21:06 -0700 Subject: [PATCH 15/17] Minor cleanup --- dpc/src/transaction/transaction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpc/src/transaction/transaction.rs b/dpc/src/transaction/transaction.rs index b61637950d..5fe2581cfd 100644 --- a/dpc/src/transaction/transaction.rs +++ b/dpc/src/transaction/transaction.rs @@ -353,7 +353,7 @@ impl Hash for Transaction { #[cfg(test)] mod tests { use super::*; - use crate::{testnet2::Testnet2, Account, AccountScheme, Payload}; + use crate::{testnet2::Testnet2, Account, AccountScheme}; use snarkvm_utilities::UniformRand; use rand::thread_rng; @@ -367,7 +367,7 @@ mod tests { let expected_record = Record::new_output( account.address(), 1234, - Payload::default(), + Default::default(), *Testnet2::noop_program_id(), UniformRand::rand(rng), rng, From 0eceaa605a919464607666a1719f26567a5abeb1 Mon Sep 17 00:00:00 2001 From: howardwu Date: Sun, 17 Oct 2021 18:17:10 -0700 Subject: [PATCH 16/17] Adds back the serialize trait on N --- dpc/src/traits/network.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dpc/src/traits/network.rs b/dpc/src/traits/network.rs index f82dbb1632..bf251bd943 100644 --- a/dpc/src/traits/network.rs +++ b/dpc/src/traits/network.rs @@ -35,11 +35,11 @@ use snarkvm_utilities::{ use anyhow::Result; use rand::{CryptoRng, Rng}; -use serde::{de::DeserializeOwned, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{cell::RefCell, rc::Rc}; #[rustfmt::skip] -pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Send + Sync { +pub trait Network: 'static + Clone + Debug + PartialEq + Eq + Serialize + Send + Sync { const NETWORK_ID: u16; const NETWORK_NAME: &'static str; From bb5438409aeacc754cfb16857ebf04704c65f1b9 Mon Sep 17 00:00:00 2001 From: howardwu Date: Mon, 18 Oct 2021 04:37:40 -0700 Subject: [PATCH 17/17] Fix compilation --- dpc/src/traits/network.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpc/src/traits/network.rs b/dpc/src/traits/network.rs index bf251bd943..e705428ea1 100644 --- a/dpc/src/traits/network.rs +++ b/dpc/src/traits/network.rs @@ -35,7 +35,7 @@ use snarkvm_utilities::{ use anyhow::Result; use rand::{CryptoRng, Rng}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Serialize}; use std::{cell::RefCell, rc::Rc}; #[rustfmt::skip]