From d91bdc48b6d7fbaf9192b6e6a44b52c8e4c3afa9 Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 13:51:28 +0200 Subject: [PATCH 01/15] execution-core: Change `Prove` trait to take the circuit in bytes --- execution-core/src/transfer/phoenix.rs | 44 +++++++++++++++----------- execution-core/tests/serialization.rs | 19 +++++------ 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/execution-core/src/transfer/phoenix.rs b/execution-core/src/transfer/phoenix.rs index 6934b6832d..119ad9faaf 100644 --- a/execution-core/src/transfer/phoenix.rs +++ b/execution-core/src/transfer/phoenix.rs @@ -209,13 +209,13 @@ impl Transaction { value_commitment(transfer_value, transfer_value_blinder); let transfer_note_sender_enc = match transfer_note.sender() { Sender::Encryption(enc) => enc, - Sender::ContractInfo(_) => panic!("The sender is encrypted"), + Sender::ContractInfo(_) => unreachable!("The sender is encrypted"), }; let change_value_commitment = value_commitment(change_value, change_value_blinder); let change_note_sender_enc = match change_note.sender() { Sender::Encryption(enc) => enc, - Sender::ContractInfo(_) => panic!("The sender is encrypted"), + Sender::ContractInfo(_) => unreachable!("The sender is encrypted"), }; let output_notes_info = [ OutputNoteInfo { @@ -246,22 +246,22 @@ impl Transaction { let schnorr_sk_b = SchnorrSecretKey::from(sender_sk.b()); let sig_b = schnorr_sk_b.sign(rng, payload_hash); - let circuit = TxCircuitVec { - input_notes_info, - output_notes_info, - payload_hash, - root, - deposit, - max_fee, - sender_pk, - signatures: (sig_a, sig_b), - }; - Self { payload, - proof: P::prove(circuit).expect( - "The proof generation shouldn't fail with a valid circuit", - ), + proof: P::prove( + &TxCircuitVec { + input_notes_info, + output_notes_info, + payload_hash, + root, + deposit, + max_fee, + sender_pk, + signatures: (sig_a, sig_b), + } + .to_var_bytes(), + ) + .expect("The proof generation shouldn't fail with a valid circuit"), } } @@ -273,6 +273,13 @@ impl Transaction { Self { payload, proof } } + /// Replaces the inner `proof` bytes for a given `proof`. + /// Note: This method is likely to invalidate the transaction and should + /// only be used with care. + pub fn replace_proof(&mut self, proof: Vec) { + self.proof = proof; + } + /// The proof of the transaction. #[must_use] pub fn proof(&self) -> &[u8] { @@ -878,6 +885,7 @@ impl TxCircuitVec { }); } + let bytes = &bytes[u64::SIZE..]; let circuit: TxCircuitVec = match input_len { 1 => TxCircuit::::from_slice(bytes)?.into(), 2 => TxCircuit::::from_slice(bytes)?.into(), @@ -916,7 +924,7 @@ impl From> for TxCircuitVec { } /// This trait can be used to implement different methods to generate a proof -/// from the circuit-input data. +/// from the circuit-bytes. pub trait Prove { /// The type returned in the event of an error during proof generation. type Error; @@ -927,5 +935,5 @@ pub trait Prove { /// # Errors /// This function errors in case of an incorrect circuit or of an /// unobtainable prover-key. - fn prove(circuit: TxCircuitVec) -> Result, Self::Error>; + fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Self::Error>; } diff --git a/execution-core/tests/serialization.rs b/execution-core/tests/serialization.rs index 0afb8c4d7f..0896d01330 100644 --- a/execution-core/tests/serialization.rs +++ b/execution-core/tests/serialization.rs @@ -27,17 +27,18 @@ use poseidon_merkle::{Item, Tree}; use rand::rngs::StdRng; use rand::{CryptoRng, Rng, RngCore, SeedableRng}; -struct RandomTestProver(); +struct TxCircuitVecProver(); -impl Prove for RandomTestProver { +// use the serialized TxCircuitVec as proof. This way that serialization is also +// tested. +impl Prove for TxCircuitVecProver { type Error = (); - fn prove(_circuit: TxCircuitVec) -> Result, Self::Error> { - let mut proof = vec![0; 5_000]; - let mut rng = StdRng::seed_from_u64(42); - rng.fill_bytes(&mut proof); - - Ok(proof) + fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Self::Error> { + Ok(TxCircuitVec::from_slice(tx_circuit_vec_bytes) + .expect("serialization should be ok") + .to_var_bytes() + .to_vec()) } } @@ -112,7 +113,7 @@ fn new_phoenix_tx( let gas_limit = 50; let gas_price = 1; - Transaction::phoenix::( + Transaction::phoenix::( rng, &sender_sk, change_pk, From d4bb755976716e15fffc06b4e1c5a85d44481693 Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 13:52:43 +0200 Subject: [PATCH 02/15] transfer-contract: Use rusk-prover for tests --- contracts/transfer/Cargo.toml | 2 +- contracts/transfer/build.rs | 8 +- contracts/transfer/src/state.rs | 4 +- contracts/transfer/src/verifier_data.rs | 26 ++--- contracts/transfer/tests/common/mod.rs | 1 - contracts/transfer/tests/common/prove.rs | 127 ----------------------- contracts/transfer/tests/common/utils.rs | 3 +- 7 files changed, 22 insertions(+), 149 deletions(-) delete mode 100644 contracts/transfer/tests/common/prove.rs diff --git a/contracts/transfer/Cargo.toml b/contracts/transfer/Cargo.toml index 58e54c591e..ec98377d4a 100644 --- a/contracts/transfer/Cargo.toml +++ b/contracts/transfer/Cargo.toml @@ -21,7 +21,7 @@ rusk-abi = { version = "0.13.0-rc", path = "../../rusk-abi" } rusk-profile = { version = "0.6", path = "../../rusk-profile" } once_cell = { version = "1.9" } rusk-abi = { version = "0.13.0-rc", path = "../../rusk-abi", default-features = false, features = ["host"] } -execution-core = { version = "0.1.0", path = "../../execution-core", features = ["zk"] } +rusk-prover = { version = "0.3", path = "../../rusk-prover/" } rkyv = { version = "0.7", default-features = false, features = ["size_32"] } bytecheck = { version = "0.6", default-features = false } hex = "0.4" diff --git a/contracts/transfer/build.rs b/contracts/transfer/build.rs index 6975c866c2..2f977a5ca7 100644 --- a/contracts/transfer/build.rs +++ b/contracts/transfer/build.rs @@ -31,10 +31,10 @@ fn main() -> Result<(), Box> { // Set the ID_[circuit_name] variables let circuits = [ - rusk_profile::Circuit::from_name("ExecuteCircuitOneTwo")?, - rusk_profile::Circuit::from_name("ExecuteCircuitTwoTwo")?, - rusk_profile::Circuit::from_name("ExecuteCircuitThreeTwo")?, - rusk_profile::Circuit::from_name("ExecuteCircuitFourTwo")?, + rusk_profile::Circuit::from_name("TxCircuitOneTwo")?, + rusk_profile::Circuit::from_name("TxCircuitTwoTwo")?, + rusk_profile::Circuit::from_name("TxCircuitThreeTwo")?, + rusk_profile::Circuit::from_name("TxCircuitFourTwo")?, ]; for circuit in circuits { set_id_env_var(&circuit); diff --git a/contracts/transfer/src/state.rs b/contracts/transfer/src/state.rs index 7924d81815..521edc7002 100644 --- a/contracts/transfer/src/state.rs +++ b/contracts/transfer/src/state.rs @@ -6,7 +6,7 @@ use crate::error::Error; use crate::tree::Tree; -use crate::verifier_data::*; +use crate::verifier_data::tx_circuit_verifier; use alloc::collections::btree_map::Entry; use alloc::collections::{BTreeMap, BTreeSet}; @@ -681,7 +681,7 @@ impl TransferState { fn verify_tx_proof(tx: &PhoenixTransaction) -> bool { // fetch the verifier data let num_inputs = tx.nullifiers().len(); - let vd = verifier_data_execute(num_inputs) + let vd = tx_circuit_verifier(num_inputs) .expect("No circuit available for given number of inputs!") .to_vec(); diff --git a/contracts/transfer/src/verifier_data.rs b/contracts/transfer/src/verifier_data.rs index ba2e84656d..5632ccb3c7 100644 --- a/contracts/transfer/src/verifier_data.rs +++ b/contracts/transfer/src/verifier_data.rs @@ -5,38 +5,38 @@ // Copyright (c) DUSK NETWORK. All rights reserved. // Note: all ID environment variables are set in the contracts build script -const VD_EXEC_1_2: &[u8] = include_bytes!(concat!( +const TX_CIRCUIT_1_2_VERIFIER: &[u8] = include_bytes!(concat!( env!("RUSK_BUILT_KEYS_PATH"), "/", - env!("ID_EXECUTECIRCUITONETWO"), + env!("ID_TXCIRCUITONETWO"), ".vd" )); -const VD_EXEC_2_2: &[u8] = include_bytes!(concat!( +const TX_CIRCUIT_2_2_VERIFIER: &[u8] = include_bytes!(concat!( env!("RUSK_BUILT_KEYS_PATH"), "/", - env!("ID_EXECUTECIRCUITTWOTWO"), + env!("ID_TXCIRCUITTWOTWO"), ".vd" )); -const VD_EXEC_3_2: &[u8] = include_bytes!(concat!( +const TX_CIRCUIT_3_2_VERIFIER: &[u8] = include_bytes!(concat!( env!("RUSK_BUILT_KEYS_PATH"), "/", - env!("ID_EXECUTECIRCUITTHREETWO"), + env!("ID_TXCIRCUITTHREETWO"), ".vd" )); -const VD_EXEC_4_2: &[u8] = include_bytes!(concat!( +const TX_CIRCUIT_4_2_VERIFIER: &[u8] = include_bytes!(concat!( env!("RUSK_BUILT_KEYS_PATH"), "/", - env!("ID_EXECUTECIRCUITFOURTWO"), + env!("ID_TXCIRCUITFOURTWO"), ".vd" )); /// Verifier data for the phoenix-circuits. -pub const fn verifier_data_execute(inputs: usize) -> Option<&'static [u8]> { +pub const fn tx_circuit_verifier(inputs: usize) -> Option<&'static [u8]> { let vd = match inputs { - 1 => VD_EXEC_1_2, - 2 => VD_EXEC_2_2, - 3 => VD_EXEC_3_2, - 4 => VD_EXEC_4_2, + 1 => TX_CIRCUIT_1_2_VERIFIER, + 2 => TX_CIRCUIT_2_2_VERIFIER, + 3 => TX_CIRCUIT_3_2_VERIFIER, + 4 => TX_CIRCUIT_4_2_VERIFIER, _ => return None, }; diff --git a/contracts/transfer/tests/common/mod.rs b/contracts/transfer/tests/common/mod.rs index 034cda60bb..f2fb50879f 100644 --- a/contracts/transfer/tests/common/mod.rs +++ b/contracts/transfer/tests/common/mod.rs @@ -4,5 +4,4 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -pub mod prove; pub mod utils; diff --git a/contracts/transfer/tests/common/prove.rs b/contracts/transfer/tests/common/prove.rs deleted file mode 100644 index c9f830359f..0000000000 --- a/contracts/transfer/tests/common/prove.rs +++ /dev/null @@ -1,127 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) DUSK NETWORK. All rights reserved. - -use dusk_bytes::Serializable; -use execution_core::{ - plonk::{Error as PlonkError, Prover as PlonkProver}, - transfer::phoenix::{Prove, TxCircuit, TxCircuitVec, NOTES_TREE_DEPTH}, -}; -use once_cell::sync::Lazy; - -use rand::rngs::StdRng; -use rand::SeedableRng; - -static PHOENIX_TX_1_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitOneTwo")); - -static PHOENIX_TX_2_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitTwoTwo")); - -static PHOENIX_TX_3_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitThreeTwo")); - -static PHOENIX_TX_4_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitFourTwo")); - -fn fetch_prover(circuit_name: &str) -> PlonkProver { - let circuit_profile = rusk_profile::Circuit::from_name(circuit_name) - .unwrap_or_else(|_| { - panic!("There should be circuit data stored for {}", circuit_name) - }); - let pk = circuit_profile.get_prover().unwrap_or_else(|_| { - panic!("there should be a prover key stored for {}", circuit_name) - }); - - PlonkProver::try_from_bytes(pk).expect("Prover key is expected to by valid") -} - -pub struct CachedProver(); - -impl Prove for CachedProver { - type Error = PlonkError; - - fn prove(circuit: TxCircuitVec) -> Result, Self::Error> { - let rng = &mut StdRng::seed_from_u64(0xbeef); - - // fetch the prover from the cache and crate the circuit - let (proof, _pi) = match circuit.input_notes_info.len() { - 1 => PHOENIX_TX_1_2_PROVER.prove(rng, &tx_circuit_1_2(circuit))?, - 2 => PHOENIX_TX_2_2_PROVER.prove(rng, &tx_circuit_2_2(circuit))?, - 3 => PHOENIX_TX_3_2_PROVER.prove(rng, &tx_circuit_3_2(circuit))?, - 4 => PHOENIX_TX_4_2_PROVER.prove(rng, &tx_circuit_4_2(circuit))?, - _ => panic!( - "The `TxCircuit` is only implemented for 1, - 2, 3 or 4 input-notes." - ), - }; - - Ok(proof.to_bytes().to_vec()) - } -} - -fn tx_circuit_1_2(circuit: TxCircuitVec) -> TxCircuit { - TxCircuit { - input_notes_info: circuit - .input_notes_info - .try_into() - .expect("There should be exactly one input"), - output_notes_info: circuit.output_notes_info, - payload_hash: circuit.payload_hash, - root: circuit.root, - deposit: circuit.deposit, - max_fee: circuit.max_fee, - sender_pk: circuit.sender_pk, - signatures: circuit.signatures, - } -} - -fn tx_circuit_2_2(circuit: TxCircuitVec) -> TxCircuit { - TxCircuit { - input_notes_info: circuit - .input_notes_info - .try_into() - .expect("There should be exactly two inputs"), - output_notes_info: circuit.output_notes_info, - payload_hash: circuit.payload_hash, - root: circuit.root, - deposit: circuit.deposit, - max_fee: circuit.max_fee, - sender_pk: circuit.sender_pk, - signatures: circuit.signatures, - } -} - -fn tx_circuit_3_2(circuit: TxCircuitVec) -> TxCircuit { - TxCircuit { - input_notes_info: circuit - .input_notes_info - .try_into() - .expect("There should be exactly three inputs"), - output_notes_info: circuit.output_notes_info, - payload_hash: circuit.payload_hash, - root: circuit.root, - deposit: circuit.deposit, - max_fee: circuit.max_fee, - sender_pk: circuit.sender_pk, - signatures: circuit.signatures, - } -} - -fn tx_circuit_4_2(circuit: TxCircuitVec) -> TxCircuit { - TxCircuit { - input_notes_info: circuit - .input_notes_info - .try_into() - .expect("There should be exactly four inputs"), - output_notes_info: circuit.output_notes_info, - payload_hash: circuit.payload_hash, - root: circuit.root, - deposit: circuit.deposit, - max_fee: circuit.max_fee, - sender_pk: circuit.sender_pk, - signatures: circuit.signatures, - } -} diff --git a/contracts/transfer/tests/common/utils.rs b/contracts/transfer/tests/common/utils.rs index 2cd59acb5a..39f0e98d0b 100644 --- a/contracts/transfer/tests/common/utils.rs +++ b/contracts/transfer/tests/common/utils.rs @@ -20,6 +20,7 @@ use execution_core::{ BlsScalar, ContractError, ContractId, }; use rusk_abi::{CallReceipt, PiecrustError, Session}; +use rusk_prover::LocalProver; use poseidon_merkle::Opening as PoseidonOpening; use rand::rngs::StdRng; @@ -213,7 +214,7 @@ pub fn create_phoenix_transaction( inputs.push((note.clone(), opening)); } - PhoenixTransaction::new::( + PhoenixTransaction::new::( rng, sender_sk, change_pk, From b845350479b1e8c43583623eada15a7df6ca18f1 Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 13:53:36 +0200 Subject: [PATCH 03/15] stake-contract: Use rusk-prover for testing --- contracts/stake/Cargo.toml | 1 + contracts/stake/tests/common/mod.rs | 1 - contracts/stake/tests/common/prove.rs | 127 -------------------------- contracts/stake/tests/common/utils.rs | 3 +- 4 files changed, 3 insertions(+), 129 deletions(-) delete mode 100644 contracts/stake/tests/common/prove.rs diff --git a/contracts/stake/Cargo.toml b/contracts/stake/Cargo.toml index 350224985a..eefa752f63 100644 --- a/contracts/stake/Cargo.toml +++ b/contracts/stake/Cargo.toml @@ -19,6 +19,7 @@ rusk-profile = { version = "0.6", path = "../../rusk-profile" } once_cell = { version = "1.9" } rusk-abi = { version = "0.13.0-rc", path = "../../rusk-abi", default-features = false, features = ["host"] } execution-core = { version = "0.1.0", path = "../../execution-core", features = ["zk"] } +rusk-prover = { version = "0.3", path = "../../rusk-prover/" } rkyv = { version = "0.7", default-features = false, features = ["size_32"] } hex = "0.4" rand = "0.8" diff --git a/contracts/stake/tests/common/mod.rs b/contracts/stake/tests/common/mod.rs index fd9a031289..194cc268fe 100644 --- a/contracts/stake/tests/common/mod.rs +++ b/contracts/stake/tests/common/mod.rs @@ -6,5 +6,4 @@ pub mod assert; pub mod init; -pub mod prove; pub mod utils; diff --git a/contracts/stake/tests/common/prove.rs b/contracts/stake/tests/common/prove.rs deleted file mode 100644 index c9f830359f..0000000000 --- a/contracts/stake/tests/common/prove.rs +++ /dev/null @@ -1,127 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) DUSK NETWORK. All rights reserved. - -use dusk_bytes::Serializable; -use execution_core::{ - plonk::{Error as PlonkError, Prover as PlonkProver}, - transfer::phoenix::{Prove, TxCircuit, TxCircuitVec, NOTES_TREE_DEPTH}, -}; -use once_cell::sync::Lazy; - -use rand::rngs::StdRng; -use rand::SeedableRng; - -static PHOENIX_TX_1_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitOneTwo")); - -static PHOENIX_TX_2_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitTwoTwo")); - -static PHOENIX_TX_3_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitThreeTwo")); - -static PHOENIX_TX_4_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitFourTwo")); - -fn fetch_prover(circuit_name: &str) -> PlonkProver { - let circuit_profile = rusk_profile::Circuit::from_name(circuit_name) - .unwrap_or_else(|_| { - panic!("There should be circuit data stored for {}", circuit_name) - }); - let pk = circuit_profile.get_prover().unwrap_or_else(|_| { - panic!("there should be a prover key stored for {}", circuit_name) - }); - - PlonkProver::try_from_bytes(pk).expect("Prover key is expected to by valid") -} - -pub struct CachedProver(); - -impl Prove for CachedProver { - type Error = PlonkError; - - fn prove(circuit: TxCircuitVec) -> Result, Self::Error> { - let rng = &mut StdRng::seed_from_u64(0xbeef); - - // fetch the prover from the cache and crate the circuit - let (proof, _pi) = match circuit.input_notes_info.len() { - 1 => PHOENIX_TX_1_2_PROVER.prove(rng, &tx_circuit_1_2(circuit))?, - 2 => PHOENIX_TX_2_2_PROVER.prove(rng, &tx_circuit_2_2(circuit))?, - 3 => PHOENIX_TX_3_2_PROVER.prove(rng, &tx_circuit_3_2(circuit))?, - 4 => PHOENIX_TX_4_2_PROVER.prove(rng, &tx_circuit_4_2(circuit))?, - _ => panic!( - "The `TxCircuit` is only implemented for 1, - 2, 3 or 4 input-notes." - ), - }; - - Ok(proof.to_bytes().to_vec()) - } -} - -fn tx_circuit_1_2(circuit: TxCircuitVec) -> TxCircuit { - TxCircuit { - input_notes_info: circuit - .input_notes_info - .try_into() - .expect("There should be exactly one input"), - output_notes_info: circuit.output_notes_info, - payload_hash: circuit.payload_hash, - root: circuit.root, - deposit: circuit.deposit, - max_fee: circuit.max_fee, - sender_pk: circuit.sender_pk, - signatures: circuit.signatures, - } -} - -fn tx_circuit_2_2(circuit: TxCircuitVec) -> TxCircuit { - TxCircuit { - input_notes_info: circuit - .input_notes_info - .try_into() - .expect("There should be exactly two inputs"), - output_notes_info: circuit.output_notes_info, - payload_hash: circuit.payload_hash, - root: circuit.root, - deposit: circuit.deposit, - max_fee: circuit.max_fee, - sender_pk: circuit.sender_pk, - signatures: circuit.signatures, - } -} - -fn tx_circuit_3_2(circuit: TxCircuitVec) -> TxCircuit { - TxCircuit { - input_notes_info: circuit - .input_notes_info - .try_into() - .expect("There should be exactly three inputs"), - output_notes_info: circuit.output_notes_info, - payload_hash: circuit.payload_hash, - root: circuit.root, - deposit: circuit.deposit, - max_fee: circuit.max_fee, - sender_pk: circuit.sender_pk, - signatures: circuit.signatures, - } -} - -fn tx_circuit_4_2(circuit: TxCircuitVec) -> TxCircuit { - TxCircuit { - input_notes_info: circuit - .input_notes_info - .try_into() - .expect("There should be exactly four inputs"), - output_notes_info: circuit.output_notes_info, - payload_hash: circuit.payload_hash, - root: circuit.root, - deposit: circuit.deposit, - max_fee: circuit.max_fee, - sender_pk: circuit.sender_pk, - signatures: circuit.signatures, - } -} diff --git a/contracts/stake/tests/common/utils.rs b/contracts/stake/tests/common/utils.rs index 859a08b4a6..63db9ced58 100644 --- a/contracts/stake/tests/common/utils.rs +++ b/contracts/stake/tests/common/utils.rs @@ -22,6 +22,7 @@ use execution_core::{ BlsScalar, ContractError, }; use rusk_abi::{CallReceipt, PiecrustError, Session}; +use rusk_prover::LocalProver; const POINT_LIMIT: u64 = 0x100000000; @@ -178,7 +179,7 @@ pub fn create_transaction( inputs.push((note.clone(), opening)); } - PhoenixTransaction::new::( + PhoenixTransaction::new::( rng, sender_sk, change_pk, From 7d680df9683ea430a703bb64d57c5d28c56151ca Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 13:54:14 +0200 Subject: [PATCH 04/15] rusk-prover: Integrate with execution-core's `Prove` trait --- rusk-prover/Cargo.toml | 26 +--- rusk-prover/Makefile | 1 - rusk-prover/src/errors.rs | 10 ++ rusk-prover/src/lib.rs | 118 +++++++++++++-- rusk-prover/src/prover.rs | 55 ------- rusk-prover/src/prover/execute.rs | 207 --------------------------- rusk-prover/tests/tx_circuit_vec.hex | 1 + rusk-prover/tests/utx.hex | 1 - 8 files changed, 125 insertions(+), 294 deletions(-) delete mode 100644 rusk-prover/src/prover.rs delete mode 100644 rusk-prover/src/prover/execute.rs create mode 100644 rusk-prover/tests/tx_circuit_vec.hex delete mode 100644 rusk-prover/tests/utx.hex diff --git a/rusk-prover/Cargo.toml b/rusk-prover/Cargo.toml index c4cd8a09f1..d59531ba92 100644 --- a/rusk-prover/Cargo.toml +++ b/rusk-prover/Cargo.toml @@ -5,34 +5,20 @@ edition = "2021" autobins = false [dependencies] -dusk-bytes = { version = "0.1" } -poseidon-merkle = { version = "0.7", features = ["rkyv-impl"] } -rand_core = "0.6" - -rkyv = { version = "0.7", default-features = false, features = ["size_32"] } -bytecheck = { version = "0.6", default-features = false } - +dusk-bytes = "0.1" +once_cell = { version = "1.9" } +rand = { version = "0.8", default-features = false, features = ["getrandom"] } +dusk-plonk = { version = "0.20", default-features = false, features = ["rkyv-impl", "alloc"] } +rusk-profile = { version = "0.6", path = "../rusk-profile" } execution-core = { version = "0.1.0", path = "../execution-core", features = ["zk"] } -## feature local_prover -once_cell = { version = "1.9", optional = true } -rand = { version = "0.8", optional = true } -rusk-profile = { version = "0.6", path = "../rusk-profile", optional = true } - [dev-dependencies] hex = "0.4" tokio = { version = "1", features = ["full"] } rand = "0.8" [features] -default = ["local_prover"] -local_prover = [ - "once_cell", - "rand", - "rusk-profile", - "std", -] no_random = [] std = [ - "execution-core/std" + "dusk-plonk/std" ] diff --git a/rusk-prover/Makefile b/rusk-prover/Makefile index f7894ce86a..b2d0abce0b 100644 --- a/rusk-prover/Makefile +++ b/rusk-prover/Makefile @@ -6,7 +6,6 @@ help: ## Display this help screen test: $(SUBDIRS) cargo test --release - cargo test --release --no-default-features clippy: ## Run clippy @cargo clippy --release -- -D warnings diff --git a/rusk-prover/src/errors.rs b/rusk-prover/src/errors.rs index 1acd025ea5..f8506ed88a 100644 --- a/rusk-prover/src/errors.rs +++ b/rusk-prover/src/errors.rs @@ -7,12 +7,15 @@ use alloc::string::String; use core::fmt; +use dusk_plonk::prelude::Error as PlonkError; + #[derive(Debug)] pub enum ProverError { InvalidData { field: &'static str, inner: dusk_bytes::Error, }, + Plonk(PlonkError), Other(String), } @@ -37,12 +40,19 @@ impl std::error::Error for ProverError { } } +impl From for ProverError { + fn from(e: PlonkError) -> ProverError { + ProverError::Plonk(e) + } +} + impl fmt::Display for ProverError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ProverError::InvalidData { field, inner } => { write!(f, "Invalid field '{field}': {inner:?}") } + ProverError::Plonk(plonk_error) => write!(f, "{:?}", plonk_error), ProverError::Other(context) => write!(f, "{context}"), } } diff --git a/rusk-prover/src/lib.rs b/rusk-prover/src/lib.rs index fb7bcfb242..c3486aae76 100644 --- a/rusk-prover/src/lib.rs +++ b/rusk-prover/src/lib.rs @@ -10,21 +10,119 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; +use alloc::format; use alloc::vec::Vec; -mod errors; -mod tx; +use dusk_bytes::{Error as BytesError, Serializable}; +use dusk_plonk::prelude::Prover as PlonkProver; +use once_cell::sync::Lazy; -#[cfg(feature = "local_prover")] -pub mod prover; -#[cfg(feature = "local_prover")] -pub use crate::prover::LocalProver; +use execution_core::transfer::phoenix::{ + Prove, TxCircuit, TxCircuitVec, NOTES_TREE_DEPTH, +}; +mod errors; pub use errors::ProverError; -pub use tx::{UnprovenTransaction, UnprovenTransactionInput}; -pub type ProverResult = Result, ProverError>; +static TX_CIRCUIT_1_2_PROVER: Lazy = + Lazy::new(|| fetch_prover("TxCircuitOneTwo")); + +static TX_CIRCUIT_2_2_PROVER: Lazy = + Lazy::new(|| fetch_prover("TxCircuitTwoTwo")); + +static TX_CIRCUIT_3_2_PROVER: Lazy = + Lazy::new(|| fetch_prover("TxCircuitThreeTwo")); + +static TX_CIRCUIT_4_2_PROVER: Lazy = + Lazy::new(|| fetch_prover("TxCircuitFourTwo")); + +#[derive(Debug, Default)] +pub struct LocalProver; + +impl Prove for LocalProver { + type Error = ProverError; + + fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Self::Error> { + let tx_circuit_vec = TxCircuitVec::from_slice(tx_circuit_vec_bytes) + .map_err(|e| { + ProverError::invalid_data("Invalid tx-circuit bytes", e) + })?; + + #[cfg(not(feature = "no_random"))] + let rng = &mut rand::rngs::OsRng; + + #[cfg(feature = "no_random")] + use rand::{rngs::StdRng, SeedableRng}; + #[cfg(feature = "no_random")] + let rng = &mut StdRng::seed_from_u64(0xbeef); + + let (proof, _pi) = match tx_circuit_vec.input_notes_info.len() { + 1 => TX_CIRCUIT_1_2_PROVER + .prove(rng, &create_circuit::<1>(tx_circuit_vec)?)?, + 2 => TX_CIRCUIT_2_2_PROVER + .prove(rng, &create_circuit::<2>(tx_circuit_vec)?)?, + 3 => TX_CIRCUIT_3_2_PROVER + .prove(rng, &create_circuit::<3>(tx_circuit_vec)?)?, + 4 => TX_CIRCUIT_4_2_PROVER + .prove(rng, &create_circuit::<4>(tx_circuit_vec)?)?, + _ => { + return Err(ProverError::from(format!( + "Invalid I/O count: {}/{}", + tx_circuit_vec.input_notes_info.len(), + tx_circuit_vec.output_notes_info.len() + ))) + } + }; + + Ok(proof.to_bytes().to_vec()) + } +} + +fn fetch_prover(circuit_name: &str) -> PlonkProver { + let circuit_profile = rusk_profile::Circuit::from_name(circuit_name) + .unwrap_or_else(|_| { + panic!( + "There should be tx-circuit data stored for {}", + circuit_name + ) + }); + let pk = circuit_profile.get_prover().unwrap_or_else(|_| { + panic!("there should be a prover key stored for {}", circuit_name) + }); + + PlonkProver::try_from_bytes(pk).expect("Prover key is expected to by valid") +} + +fn create_circuit( + tx_circuit_vec: TxCircuitVec, +) -> Result, ProverError> { + Ok(TxCircuit { + input_notes_info: tx_circuit_vec.input_notes_info.try_into().map_err( + |_| { + ProverError::invalid_data( + "invalid tx-circuit", + BytesError::InvalidData, + ) + }, + )?, + output_notes_info: tx_circuit_vec.output_notes_info, + payload_hash: tx_circuit_vec.payload_hash, + root: tx_circuit_vec.root, + deposit: tx_circuit_vec.deposit, + max_fee: tx_circuit_vec.max_fee, + sender_pk: tx_circuit_vec.sender_pk, + signatures: tx_circuit_vec.signatures, + }) +} + +#[cfg(test)] +mod tests { + use super::*; -pub trait Prover { - fn prove_execute(&self, utx_bytes: &[u8]) -> ProverResult; + #[test] + fn test_prove_tx_circuit() { + let tx_circuit_vec_bytes = + hex::decode(include_str!("../tests/tx_circuit_vec.hex")).unwrap(); + let _proof = LocalProver::prove(&tx_circuit_vec_bytes).unwrap(); + } } diff --git a/rusk-prover/src/prover.rs b/rusk-prover/src/prover.rs deleted file mode 100644 index 81e9069c3e..0000000000 --- a/rusk-prover/src/prover.rs +++ /dev/null @@ -1,55 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) DUSK NETWORK. All rights reserved. - -mod execute; - -use crate::{ProverError, ProverResult}; - -use dusk_bytes::Serializable; -use execution_core::plonk::Prover as PlonkProver; -use once_cell::sync::Lazy; - -#[cfg(not(feature = "no_random"))] -use rand::rngs::OsRng; - -#[cfg(feature = "no_random")] -use rand::{rngs::StdRng, SeedableRng}; - -#[derive(Debug, Default)] -pub struct LocalProver; - -impl crate::Prover for LocalProver { - fn prove_execute(&self, circuit_inputs: &[u8]) -> ProverResult { - self.local_prove_execute(circuit_inputs) - } -} - -pub fn fetch_prover(circuit_name: &str) -> PlonkProver { - let circuit_profile = rusk_profile::Circuit::from_name(circuit_name) - .unwrap_or_else(|_| { - panic!("There should be circuit data stored for {}", circuit_name) - }); - let pk = circuit_profile.get_prover().unwrap_or_else(|_| { - panic!("there should be a prover key stored for {}", circuit_name) - }); - - PlonkProver::try_from_bytes(pk).expect("Prover key is expected to by valid") -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Prover; - - #[test] - fn test_prove_execute() { - let utx_hex = include_str!("../tests/utx.hex"); - let utx_bytes = hex::decode(utx_hex).unwrap(); - let prover = LocalProver {}; - let proof = prover.prove_execute(&utx_bytes).unwrap(); - println!("{}", hex::encode(proof)); - } -} diff --git a/rusk-prover/src/prover/execute.rs b/rusk-prover/src/prover/execute.rs deleted file mode 100644 index 99cb8b0450..0000000000 --- a/rusk-prover/src/prover/execute.rs +++ /dev/null @@ -1,207 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) DUSK NETWORK. All rights reserved. - -use super::*; - -use execution_core::{ - transfer::phoenix::{ - value_commitment, InputNoteInfo, OutputNoteInfo, Sender, TxCircuit, - NOTES_TREE_DEPTH, - }, - JubJubAffine, -}; -use rand::{CryptoRng, RngCore}; - -use crate::prover::fetch_prover; -use crate::UnprovenTransaction; - -pub static EXEC_1_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitOneTwo")); - -pub static EXEC_2_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitTwoTwo")); - -pub static EXEC_3_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitThreeTwo")); - -pub static EXEC_4_2_PROVER: Lazy = - Lazy::new(|| fetch_prover("ExecuteCircuitFourTwo")); - -fn create_circuit( - utx: &UnprovenTransaction, -) -> Result, ProverError> { - // Create the `InputNoteInfo` - let mut tx_input_notes = Vec::with_capacity(utx.inputs().len()); - utx.inputs.iter().for_each(|input| { - tx_input_notes.push(InputNoteInfo { - merkle_opening: input.opening, - note: input.note.clone(), - note_pk_p: input.npk_prime.into(), - value: input.value, - value_blinder: input.value_blinder, - nullifier: input.nullifier, - signature: input.sig, - }); - }); - let tx_input_notes: [InputNoteInfo; I] = tx_input_notes - .try_into() - .expect("the numbers of input-notes should be as expected"); - - // Create the `TxOutputNotes` - let ( - transfer_note, - transfer_value, - transfer_value_blinder, - transfer_sender_blinder, - ) = &utx.outputs[0]; - let transfer_value_commitment = - value_commitment(*transfer_value, *transfer_value_blinder); - let transfer_note_sender_enc = match transfer_note.sender() { - Sender::Encryption(enc) => enc, - Sender::ContractInfo(_) => { - panic!("The sender needs to be an encryption") - } - }; - - let ( - change_note, - change_value, - change_value_blinder, - change_sender_blinder, - ) = &utx.outputs[1]; - let change_value_commitment = - value_commitment(*change_value, *change_value_blinder); - let change_note_sender_enc = match change_note.sender() { - Sender::Encryption(enc) => enc, - Sender::ContractInfo(_) => { - panic!("The sender needs to be an encryption") - } - }; - let tx_output_notes = [ - OutputNoteInfo { - value: *transfer_value, - value_commitment: transfer_value_commitment, - value_blinder: *transfer_value_blinder, - note_pk: JubJubAffine::from( - transfer_note.stealth_address().note_pk().as_ref(), - ), - sender_enc: *transfer_note_sender_enc, - sender_blinder: *transfer_sender_blinder, - }, - OutputNoteInfo { - value: *change_value, - value_commitment: change_value_commitment, - value_blinder: *change_value_blinder, - note_pk: JubJubAffine::from( - change_note.stealth_address().note_pk().as_ref(), - ), - sender_enc: *change_note_sender_enc, - sender_blinder: *change_sender_blinder, - }, - ]; - - // Build the circuit - let circuit: TxCircuit = TxCircuit { - input_notes_info: tx_input_notes, - output_notes_info: tx_output_notes, - payload_hash: utx.payload_hash(), - root: utx.payload.tx_skeleton.root, - deposit: utx.payload.tx_skeleton.deposit, - max_fee: utx.payload.fee.max_fee(), - sender_pk: utx.sender_pk, - signatures: utx.signatures, - }; - - Ok(circuit) -} - -impl LocalProver { - pub(crate) fn local_prove_execute( - &self, - circuit_inputs: &[u8], - ) -> Result, ProverError> { - let utx = UnprovenTransaction::from_slice(circuit_inputs) - .map_err(|e| ProverError::invalid_data("utx", e))?; - - #[cfg(not(feature = "no_random"))] - let rng = &mut OsRng; - - #[cfg(feature = "no_random")] - let rng = &mut StdRng::seed_from_u64(0xbeef); - - match utx.inputs().len() { - 1 => local_prove_exec_1_2(&utx, rng), - 2 => local_prove_exec_2_2(&utx, rng), - 3 => local_prove_exec_3_2(&utx, rng), - 4 => local_prove_exec_4_2(&utx, rng), - _ => Err(ProverError::from(format!( - "Invalid I/O count: {}/{}", - utx.inputs().len(), - utx.outputs().len() - ))), - } - } -} - -fn local_prove_exec_1_2( - utx: &UnprovenTransaction, - rng: &mut R, -) -> Result, ProverError> -where - R: RngCore + CryptoRng, -{ - let circuit = create_circuit::<1>(utx)?; - - let (proof, _) = EXEC_1_2_PROVER.prove(rng, &circuit).map_err(|e| { - ProverError::with_context("Failed proving the circuit", e) - })?; - Ok(proof.to_bytes().to_vec()) -} - -fn local_prove_exec_2_2( - utx: &UnprovenTransaction, - rng: &mut R, -) -> Result, ProverError> -where - R: RngCore + CryptoRng, -{ - let circuit = create_circuit::<2>(utx)?; - - let (proof, _) = EXEC_2_2_PROVER.prove(rng, &circuit).map_err(|e| { - ProverError::with_context("Failed proving the circuit", e) - })?; - Ok(proof.to_bytes().to_vec()) -} - -fn local_prove_exec_3_2( - utx: &UnprovenTransaction, - rng: &mut R, -) -> Result, ProverError> -where - R: RngCore + CryptoRng, -{ - let circuit = create_circuit::<3>(utx)?; - - let (proof, _) = EXEC_3_2_PROVER.prove(rng, &circuit).map_err(|e| { - ProverError::with_context("Failed proving the circuit", e) - })?; - Ok(proof.to_bytes().to_vec()) -} - -fn local_prove_exec_4_2( - utx: &UnprovenTransaction, - rng: &mut R, -) -> Result, ProverError> -where - R: RngCore + CryptoRng, -{ - let circuit = create_circuit::<4>(utx)?; - - let (proof, _) = EXEC_4_2_PROVER.prove(rng, &circuit).map_err(|e| { - ProverError::with_context("Failed proving the circuit", e) - })?; - Ok(proof.to_bytes().to_vec()) -} diff --git a/rusk-prover/tests/tx_circuit_vec.hex b/rusk-prover/tests/tx_circuit_vec.hex new file mode 100644 index 0000000000..736b3c2255 --- /dev/null +++ b/rusk-prover/tests/tx_circuit_vec.hex @@ -0,0 +1 @@ +0400000000000000f6cf8452d87626d2effe978200a5400d60fe904a8d5a535a8d1b7798165d2e4d624320a4b8406b24ab2e4fdd4365f1329f945ad072eb9ddb4bce43381bf13e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e64b29056a9e1b7a4d257958327a38fba2c9989771f86c6859c44161140134a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2063fc7a2b8da9a5e0942bb3f92e4a8c9cebc84c16de583a7bb32cc69022112000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8f49ef20c29db7f9f004d91df36541a90f5d271abf04cc55b067d9ab1366d540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002082c178da62f3450c88d61620a4d1f93a307b829076818689ba07f8f3a4a44f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007237782c35041b0e1262acd1c19f245a8d837e2e5c3dfc70e2b93b53064dc86f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027c9af6cab599d4436a0ce5f96972b4f3f98cd33ec02701108fe66b6c69f3c390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b3b1ac61f676be22094bfda321ed74b8b715e6aa7992e6a1363b845941abb2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000add7f3fb681efac4f1f593fd9d946738504269598e230def59cd7ab6752d0d51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0876cc6cf6e0292703d2a4a56ecc28ec9b27e37c7b53b1f55fc1383e2e9524d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096f3b41cdc1fe0ee7ae86e17dffe70329c9ddf7a9ac9955ff126a1fb4fd8413d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037c5c835fca0aaddc2925fe815ca8fd623c738fc2d1e9c2ad93b4f556e68e81200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057304589a1a164f99f5af2b6a90ac8724a793e1d1cb92c4c35c2227fe813e84a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000178831e44c9616b834251c704ec11e3879e9d1a63d694bc5806addd359fc59230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b219e7ba712fc7647b9726b2ee6de34a50c99f961080259ce9437e9f3a4f84b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df430f4f3a2f701c2c388b34c7ea00e250a57c886855f331c6135ddfa98cc83228e9b1074cd80740a7a99c0ee989bd939f1a712ecd8ee43518e4a950329f74644e440270a99ed22f10d50c124186c58b3c22684132a8172d78b6c9302872d84900000000000000000000000000000000000000000000000000000000000000002fd72260707ae8f633e73c87d8c37d67a1ac079d9238dd3cea5833e43ff482514ff2fba5e71136532148282b2cbc42c7ca9f461af258223c926367435b0da946fefb0b82786dd47745c05c178f4a05a932327d5def84b43a9a5f1e6686f009647e7adf68f91f49bae9ba446767a886c46a9cbc884afcf1178df57d0cdb7ed4360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008aeead3f3f56c278fdbf6293ba8f27466f53e787c112139543bea7f8b11d1558391d595ec6366e6f7dfd01198b00d0c1811c6c826066cf88733e4ddfa8933b5873ba23edd844f9734935991dcf48e080046d9adc086abdcbe8762f5e27f2ec98000000000000000000e40b54020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fc981a85211c64bd18a17d5d5b72395cdd2a8ebdd7200f8112b91cdd23e28e400e40b5402000000000000000000000000000000000000000000000000000000000000000000000005bfb898474fdfbba494090bb456897f7ef4ed062cf25bb2b193d65bff9f1d1e829ed4b010eaaf84ccc03216be14eb959ca4a10819fd2551e99cd4ebfb4651031c044700d1d94c2131884b2cc8dc9353f9e88c444dd1990196c8e8431a005a9b87fa178b821ca7115835528603dcdfe9e5a6ef68ba2614099f4133c552cf7141f6cf8452d87626d2effe978200a5400d60fe904a8d5a535a8d1b7798165d2e4d624320a4b8406b24ab2e4fdd4365f1329f945ad072eb9ddb4bce43381bf13e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e64b29056a9e1b7a4d257958327a38fba2c9989771f86c6859c44161140134a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2063fc7a2b8da9a5e0942bb3f92e4a8c9cebc84c16de583a7bb32cc69022112000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8f49ef20c29db7f9f004d91df36541a90f5d271abf04cc55b067d9ab1366d540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002082c178da62f3450c88d61620a4d1f93a307b829076818689ba07f8f3a4a44f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007237782c35041b0e1262acd1c19f245a8d837e2e5c3dfc70e2b93b53064dc86f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027c9af6cab599d4436a0ce5f96972b4f3f98cd33ec02701108fe66b6c69f3c390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b3b1ac61f676be22094bfda321ed74b8b715e6aa7992e6a1363b845941abb2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000add7f3fb681efac4f1f593fd9d946738504269598e230def59cd7ab6752d0d51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0876cc6cf6e0292703d2a4a56ecc28ec9b27e37c7b53b1f55fc1383e2e9524d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096f3b41cdc1fe0ee7ae86e17dffe70329c9ddf7a9ac9955ff126a1fb4fd8413d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037c5c835fca0aaddc2925fe815ca8fd623c738fc2d1e9c2ad93b4f556e68e81200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057304589a1a164f99f5af2b6a90ac8724a793e1d1cb92c4c35c2227fe813e84a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000178831e44c9616b834251c704ec11e3879e9d1a63d694bc5806addd359fc59230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b219e7ba712fc7647b9726b2ee6de34a50c99f961080259ce9437e9f3a4f84b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df430f4f3a2f701c2c388b34c7ea00e250a57c886855f331c6135ddfa98cc83228e9b1074cd80740a7a99c0ee989bd939f1a712ecd8ee43518e4a950329f74644e440270a99ed22f10d50c124186c58b3c22684132a8172d78b6c9302872d84900000000000000000000000000000000000000000000000000000000000000002fd72260707ae8f633e73c87d8c37d67a1ac079d9238dd3cea5833e43ff482514ff2fba5e71136532148282b2cbc42c7ca9f461af258223c926367435b0da946fefb0b82786dd47745c05c178f4a05a932327d5def84b43a9a5f1e6686f009647e7adf68f91f49bae9ba446767a886c46a9cbc884afcf1178df57d0cdb7ed4360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000008aeead3f3f56c278fdbf6293ba8f27466f53e787c112139543bea7f8b11d1558b96a3c7c1dbcb4d8cdd8ef13e50e84cf6480116311677676269d3e662cea608ca9fc77c7fa13173062ea1ff039d2a65d800c18277cdc3ce924433b5611bed3a1010000000000000000e40b540200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000033b7d0281acd796e9ef59bef3d1b665814a4c5ff0dbd1e3f7ae2d12e56eef01500e40b540200000000000000000000000000000000000000000000000000000000000000000000009004e2be5215e6e68bfa93c9fb264d37a24a00522f13b0e84fb2cd36cbcd4d519236a6e0e1478b16e7759a0dc2cb5aa98d31646ceb12c9778075248196e7320e1f40a7534d52b2d9ec060757a58a52c296c744c803950b22f181739364edfb589fe9f0270777cbb39c825779ef18371718ef1ff42df5a0e6ff4c0675b4bcbbc6f6cf8452d87626d2effe978200a5400d60fe904a8d5a535a8d1b7798165d2e4d624320a4b8406b24ab2e4fdd4365f1329f945ad072eb9ddb4bce43381bf13e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e64b29056a9e1b7a4d257958327a38fba2c9989771f86c6859c44161140134a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2063fc7a2b8da9a5e0942bb3f92e4a8c9cebc84c16de583a7bb32cc69022112000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8f49ef20c29db7f9f004d91df36541a90f5d271abf04cc55b067d9ab1366d540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002082c178da62f3450c88d61620a4d1f93a307b829076818689ba07f8f3a4a44f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007237782c35041b0e1262acd1c19f245a8d837e2e5c3dfc70e2b93b53064dc86f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027c9af6cab599d4436a0ce5f96972b4f3f98cd33ec02701108fe66b6c69f3c390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b3b1ac61f676be22094bfda321ed74b8b715e6aa7992e6a1363b845941abb2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000add7f3fb681efac4f1f593fd9d946738504269598e230def59cd7ab6752d0d51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0876cc6cf6e0292703d2a4a56ecc28ec9b27e37c7b53b1f55fc1383e2e9524d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096f3b41cdc1fe0ee7ae86e17dffe70329c9ddf7a9ac9955ff126a1fb4fd8413d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037c5c835fca0aaddc2925fe815ca8fd623c738fc2d1e9c2ad93b4f556e68e81200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057304589a1a164f99f5af2b6a90ac8724a793e1d1cb92c4c35c2227fe813e84a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000178831e44c9616b834251c704ec11e3879e9d1a63d694bc5806addd359fc59230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b219e7ba712fc7647b9726b2ee6de34a50c99f961080259ce9437e9f3a4f84b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df430f4f3a2f701c2c388b34c7ea00e250a57c886855f331c6135ddfa98cc83228e9b1074cd80740a7a99c0ee989bd939f1a712ecd8ee43518e4a950329f74644e440270a99ed22f10d50c124186c58b3c22684132a8172d78b6c9302872d84900000000000000000000000000000000000000000000000000000000000000002fd72260707ae8f633e73c87d8c37d67a1ac079d9238dd3cea5833e43ff482514ff2fba5e71136532148282b2cbc42c7ca9f461af258223c926367435b0da946fefb0b82786dd47745c05c178f4a05a932327d5def84b43a9a5f1e6686f009647e7adf68f91f49bae9ba446767a886c46a9cbc884afcf1178df57d0cdb7ed4360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000008aeead3f3f56c278fdbf6293ba8f27466f53e787c112139543bea7f8b11d15584bb2c20f5e388d50e610cbcb033301e0dec8c4da0c85b500d9cf228a6210861151405cb5918e5fc928682253973f03d7ea79fa0157b0793c86025b87de54b228020000000000000000e40b540200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015d7358f6bc496aebf93455a395b5cd3d9e7543e742fc042e93249e48fa686d400e40b54020000000000000000000000000000000000000000000000000000000000000000000000e28617bc83449802c3d3c24e01a1e7f454d308e9c179408ebaa19b4237beee04d35e314b6845b65a6ea7fbe430aededb6198b6255bdadc7ec5de7f545cc516021cd74ca0f147888c06513bc256d1c667760bf87e3e0507e8c01292c68e30cbae7d58c9f77a747340cb2cffc989a214959197642ae37e02f4eaff94c6df01efedf6cf8452d87626d2effe978200a5400d60fe904a8d5a535a8d1b7798165d2e4d624320a4b8406b24ab2e4fdd4365f1329f945ad072eb9ddb4bce43381bf13e170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009e64b29056a9e1b7a4d257958327a38fba2c9989771f86c6859c44161140134a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b2063fc7a2b8da9a5e0942bb3f92e4a8c9cebc84c16de583a7bb32cc69022112000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8f49ef20c29db7f9f004d91df36541a90f5d271abf04cc55b067d9ab1366d540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002082c178da62f3450c88d61620a4d1f93a307b829076818689ba07f8f3a4a44f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007237782c35041b0e1262acd1c19f245a8d837e2e5c3dfc70e2b93b53064dc86f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027c9af6cab599d4436a0ce5f96972b4f3f98cd33ec02701108fe66b6c69f3c390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b3b1ac61f676be22094bfda321ed74b8b715e6aa7992e6a1363b845941abb2f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000add7f3fb681efac4f1f593fd9d946738504269598e230def59cd7ab6752d0d51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0876cc6cf6e0292703d2a4a56ecc28ec9b27e37c7b53b1f55fc1383e2e9524d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096f3b41cdc1fe0ee7ae86e17dffe70329c9ddf7a9ac9955ff126a1fb4fd8413d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037c5c835fca0aaddc2925fe815ca8fd623c738fc2d1e9c2ad93b4f556e68e81200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057304589a1a164f99f5af2b6a90ac8724a793e1d1cb92c4c35c2227fe813e84a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000178831e44c9616b834251c704ec11e3879e9d1a63d694bc5806addd359fc59230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004b219e7ba712fc7647b9726b2ee6de34a50c99f961080259ce9437e9f3a4f84b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df430f4f3a2f701c2c388b34c7ea00e250a57c886855f331c6135ddfa98cc83228e9b1074cd80740a7a99c0ee989bd939f1a712ecd8ee43518e4a950329f74644e440270a99ed22f10d50c124186c58b3c22684132a8172d78b6c9302872d84900000000000000000000000000000000000000000000000000000000000000002fd72260707ae8f633e73c87d8c37d67a1ac079d9238dd3cea5833e43ff482514ff2fba5e71136532148282b2cbc42c7ca9f461af258223c926367435b0da946fefb0b82786dd47745c05c178f4a05a932327d5def84b43a9a5f1e6686f009647e7adf68f91f49bae9ba446767a886c46a9cbc884afcf1178df57d0cdb7ed4360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000008aeead3f3f56c278fdbf6293ba8f27466f53e787c112139543bea7f8b11d15582dbbaec8a2acb879b48d311f1264b1aafe6bf26ccc0bb250af7a2e19e8dcdc38909f6752c16e4b44e054622b7c9e76004ff9fb323ed63332ffafcf9d2e2da5ad030000000000000000e40b5402000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8d95e6bd5c8ea1c86ac418c3dd61c86808dcf807dc0f5566321645c737fa6a200e40b54020000000000000000000000000000000000000000000000000000000000000000000000926c2a881448e2d9a29edfdf98336658bd2f671020302569072d6781b592a43034b224ac2ccd660171bb87a199586e7453d24ff5fdee97e2f4e2a2c41620550b3d719348f90a28943743226d09599c4bf5770751b9a48f73b6c65af9f2f0caa2a5e9f05647db6f1f141dbd06b8feb5c185acf85a70a636c7e872f2416d93dc3ae8030000000000007db07dcb2b115d73452d373eccba3c8b55502095b4f23f76f524394aa2f8ca9edd9d50faa9c0caaa2ecf590aa44b9c58e14635257c60d4dbd6517019d442da0a04ed0a4888aa513ea729c646b32bdd5556b0fa3fea55673c9bf2710534f4e56a9d3dd5e17dbb7fa03b93470341977bb2e32c13a6a0c1f1d254a2fe5b8424714aeb8bfc5d1e77774941f456f2066478336363266f8ed9be4fd118fa52da08976ad85dbd596fc0476c779f3e2e7b5e58b732cb71f9ca056a8828cf845885a22f17b924e151fc3caeea7f071f62b70e05a0c86e838f7cb710ab52c357b36e85080dd709a376d587632e3bd514911677a605141c60a5064d9da01f15dfe2c0ca8001c2d274e9cc6e3a69bdd339daaf0bdef37d3d278d6461e6f91bda1e0118e4fe0a18f8f9d8080000009a9b4b7718cb7cb7ee5b3a07086c95f70fd03e0bcbc6bd95090582e4fd1327a8c7b431dfbd52e1fa38dce2244ad525b47e6cf2dba4180d357f79fcfd3f68fe071edc22946dc1dfdd5c088344100f7860e74e5699790c38427246f4288e8a1b36593c527cd9ed46cb3f65887ad6c7391ed7f99eb3ae399205d535f4c36cb1f2503c751e7348c601417e90961d702597370621d00d368fb92a8311db8aa65cbb966185254a600262555177ef42c4ede260249eb20e15f00fb94a1ee07ceb38b2b4921309c3c09d1df9996f48e8046eb924a44ba22a6b33372b2fb4140db2fa4496b3ab17ad2857521903f9192418be17bef70e29cc1c2bc3dea0d6967f4295e70c24c2ea72ea8fd6b56bf13f318bdaeb7dd6aae38766d7fb739c12e8577fed8900c946a849cf9ac45c60134957d5c133d1b8b1346d9ba5b8b0e05ae17614d57d53f6cf8452d87626d2effe978200a5400d60fe904a8d5a535a8d1b7798165d2e4d00000000000000000094357700000000242807afc2af7b1086ea75c69b920c948c22f753ba1624a74b6de9cdb64303335951eeef6a8bb247dfef9141e8524b13d408533a11de737fdc420942175106653ad1850de2ba1102dbf6971221f0bb179b5c91dd1919f3e6a3693af8050a270c826f9f1f7541c82b2cabfd38863b5ad58b3c22eae98634c6b109a244c4ba4c01049d3fd718c3cc6125e18b979c4b85556e71578ed0cde2c8b7374f38a1e46e08911ec5fd4dcf289f24e1a01a0e6cc9e4e392fb65476def465fc1bea9b8fb0f4c \ No newline at end of file diff --git a/rusk-prover/tests/utx.hex b/rusk-prover/tests/utx.hex deleted file mode 100644 index d08514aa67..0000000000 --- a/rusk-prover/tests/utx.hex +++ /dev/null @@ -1 +0,0 @@ -0400000000000000de0a00000000000020af62ca70a615f87cf6da1af29a186bfe6e5d5dc8a8184bd30d7aa8f7d1b81c0146d9adf7ca73132bb63b43581c4e89be8fbc725d120519664abe487673e5c4d1cdd3f37473d2a1702b050995860e94dade1883cbd5319506424060741fc0d65e65cc267dd6fec012f5971e3bbe594e363d6c71152decba2cb50f50da0314f4270d000000000000007f697a85965f97a8e03d085a6ceb52aebfd0b8b839760142c2a96d09f637155849378d9d4062b9d8f99fdd6c1bfdd782b313b585c361e797a5458bd9e772f12c8d85418b007f11196a9312bbaaeb4e59ec28bb3ff599e592dfb29d19a85b6faa56efc2166a4852333abd6c9c8771b40a4698032def816b0621c57084fec181290c3cb726a43d24d2820ffd4b88335d90d6d49aeef232e3eedbcbc3ebe9865f9096900e212d826c7495702fdbaf833eabd992dabc498d656d6badcb81064dbbe417dd5ad21a0000000000000000b990967a9388600e76196bd3f4981404ecb9a172e898e42272d9f87ec69ee807e2fe474d66b554399d8c29ea25919106412ccefc49458fc08e8bc0b1448ae156ffb3e825460d888aeef5fdf30005a74eebe25c7b74c7c5172f1c335255d67c0e0855696e721488a2b0acd9e544423ee6b217461b9a06e53ef47dc2e5493b08c1f43cbe399f263bf78dda0634663a16c3182c1c5fb8048db2807a774f458422446ce2792d9948f73dea713cb619f89cdc0a6899ca6d3f0ce9f34e7df6a007d447000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce1729d6da3ce9d0aa65d195134eace5fc58aa79f6ecf3298fed1852465ded2b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c68a13e4bc0bbabb53384e9fcbea880d3fc9eb24b8fc835de3e5bfeed1e12029000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000322d53d0d0de1fead3fa2834f1b4831f954b48df7606bea92b3c49cd8a99d957000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fdc85bfe0f1fca4914603d82606a800df96b0fb1422dc3735f776cdb89930f510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4f20ac33775d313d66d27ee41272aeab67ffb9f5c2eb3f8854eb6790b48805000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df69af22234b84cac47ff36bea4ddecbada14875be629c97fe16d87a6006855f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea8a7fb53728eec35cc2ba3f5db53ccdd656140d45620e5e111fe220aa131f620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004fa298dce1d7b273bbc2232b719732f1b1248ac90148df4b4a709936496d35160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df6739bfd500c986d7f9c27379add953908ea4324ca512dbcbc30bb2da2a23a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009761276efa9c67cf9be80dfbed113f65cadfa444cac590e8280fa051a44c5163000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d593fd8677afc5b80b6b86e2de4c1ea8add64ad47fb275a33f8c823d77f5f266000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0c8fceaf6e0edde4f160ee09d88a18a85dacea6b9a63bd694cb2cd802cdbe1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029b5406e31d73525f9a5bf54e78534d746479a56dcd02f4142c98d057ff841510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b59138286216630bc0d7bedc910e14f8be819727de272335ce2b242f032646dacfb1729488433aae682a8c73c55ae800925cc7ab668d313e5be143d13f79848000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c223bc5876b3d9004177844e0610b2989153894a43060bab4bc024bb1a0c23a34d9316ba10468648122c48e75314467eb13d82d05011b3848e5ca5114fb245fc37e28c21a55b1567cee18dfe0d63a7b7a40e671a982cab89d1de1748c46033e771c458d91a78f73dc91540dfdc1e05a732ce899874539ab30aa9e692ec46a0c5afcaf7b57b159786aed222f49bda98085f2ae34b7b5ecc3c9796c1d02a6bd5319eeb47b614632564b94191a2f5a92027952453b238886df376daee74fa36444f4a9dce13766015171033ad6cd6d79d748f260c920c486fa9604cc61b205553ccd26695d8438ce7a1823a92abd41610285cd1e28578ca089c645b142bb8ef94a6cbaf3ab02eae727dc996c1917122849e973e933b2df2a8eb212145bef07ab65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000100000000000000de0a000000000000a9703982147815a4b026ffe1f76538ee01b24ce794d176cfc9334e08b723904400785fbdb639a3bce9de8444095230a2ef4b1f000e9831e77743fe63695296999c41d49da2feabaf750fec9c4dfc6673fbc08a7900208465fc228666f800c5cd6bcf7dc5aa5a5cfec3d54225ba01083854abb68510147edadf90bf8123643ec4dd10000000000000006406d351020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035956fc4d19f9a2528078cf6051385c020fd2b6088bd5fe70259304e183e1d836b8a0a0a70d20dbfce061078c2ad93ffe042d38e40ff23bfa9486dff59579660f0900fb64098bc14031732f111cffc6541e2e2d7ce182af91f1d960ce5283d0f06f15fd3d27ef3a81469039dfb75face5dca75cdfec911f6074a6cc5bf4a14ae6406d35102000000000000000000000000000000000000000000000000000000000000000000000035ddd235e90f5a252282d295bb0b4c118dbfa45b905be945f395064bbb2d6be10f4c0a9cc583182d085b2eb19808af8ebf6bd8c5e62edcb67635c9ca12295f083cc66f8d9b53960d2bd036f44f205a743e300edc64a246f2c8fcbbc3ad73af9a90cee026b54fa888f436797f12875d53ebb824161467692f05e0f20248dfd9476ce2792d9948f73dea713cb619f89cdc0a6899ca6d3f0ce9f34e7df6a007d447000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce1729d6da3ce9d0aa65d195134eace5fc58aa79f6ecf3298fed1852465ded2b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c68a13e4bc0bbabb53384e9fcbea880d3fc9eb24b8fc835de3e5bfeed1e12029000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000322d53d0d0de1fead3fa2834f1b4831f954b48df7606bea92b3c49cd8a99d957000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fdc85bfe0f1fca4914603d82606a800df96b0fb1422dc3735f776cdb89930f510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4f20ac33775d313d66d27ee41272aeab67ffb9f5c2eb3f8854eb6790b48805000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df69af22234b84cac47ff36bea4ddecbada14875be629c97fe16d87a6006855f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea8a7fb53728eec35cc2ba3f5db53ccdd656140d45620e5e111fe220aa131f620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004fa298dce1d7b273bbc2232b719732f1b1248ac90148df4b4a709936496d35160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df6739bfd500c986d7f9c27379add953908ea4324ca512dbcbc30bb2da2a23a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009761276efa9c67cf9be80dfbed113f65cadfa444cac590e8280fa051a44c5163000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d593fd8677afc5b80b6b86e2de4c1ea8add64ad47fb275a33f8c823d77f5f266000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0c8fceaf6e0edde4f160ee09d88a18a85dacea6b9a63bd694cb2cd802cdbe1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029b5406e31d73525f9a5bf54e78534d746479a56dcd02f4142c98d057ff841510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b59138286216630bc0d7bedc910e14f8be819727de272335ce2b242f032646dacfb1729488433aae682a8c73c55ae800925cc7ab668d313e5be143d13f79848000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a7d137f25b276d688a788c6df921df21cd4f12a7615c9041cc8552917195660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e25cb53a3d9ec4d8c072605cf030dd4c076425fe957b22629736a8295b6324080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cbaf3ab02eae727dc996c1917122849e973e933b2df2a8eb212145bef07ab65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000de0a000000000000434475899b54265e35677d5584faa04a1b5078a3ea5bb152fc96f78cda30a4170096c51fa6cdd2e77632835327c3381b8ca36d42fafca7bc03bd74e83a4c56a653fb3406aebe70f15146433a92f74386d30b980261e9d11547ec011237136632e517970c0984d75d4821a3b7ff893badd3ba0c69999ba394dbfb099b289e9cfcaf0f000000000000000010a5d4e80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000102000000000000000000000000000000000000000000000000000000000000008d31d41cb190cb54fab965d3edba820a8e8fc5cdf5df2ef0b17ed7b318c290808564ec96651502cb086f4e2317de88441376dca3359fcf084a904343591c9c6d420dea1e576a957120af2ea4c46ef9d3bb1394ad3cbc92413c59b1ce518c08500010a5d4e80000000000000000000000000000000000000000000000000000000000000000000000d158af2ae6e5824dc7db74957d7e234fbfba98b57c6517c9a12e36c6d7c94482c5db172c370d647a1075eccc4c2184b2a0de78b07695c9717efeb7e173110d0ad8f75cc86dfa4cea0b55ecf3aa905a670fad7b0668a0801fa975e8986602f4e53e322a36b9aada644ff553428e951f3a9f991cb47067b8ad442c03aa63c5d8546ce2792d9948f73dea713cb619f89cdc0a6899ca6d3f0ce9f34e7df6a007d447000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce1729d6da3ce9d0aa65d195134eace5fc58aa79f6ecf3298fed1852465ded2b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c68a13e4bc0bbabb53384e9fcbea880d3fc9eb24b8fc835de3e5bfeed1e12029000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000322d53d0d0de1fead3fa2834f1b4831f954b48df7606bea92b3c49cd8a99d957000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fdc85bfe0f1fca4914603d82606a800df96b0fb1422dc3735f776cdb89930f510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4f20ac33775d313d66d27ee41272aeab67ffb9f5c2eb3f8854eb6790b48805000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df69af22234b84cac47ff36bea4ddecbada14875be629c97fe16d87a6006855f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea8a7fb53728eec35cc2ba3f5db53ccdd656140d45620e5e111fe220aa131f620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004fa298dce1d7b273bbc2232b719732f1b1248ac90148df4b4a709936496d35160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df6739bfd500c986d7f9c27379add953908ea4324ca512dbcbc30bb2da2a23a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009761276efa9c67cf9be80dfbed113f65cadfa444cac590e8280fa051a44c5163000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d593fd8677afc5b80b6b86e2de4c1ea8add64ad47fb275a33f8c823d77f5f266000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0c8fceaf6e0edde4f160ee09d88a18a85dacea6b9a63bd694cb2cd802cdbe1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029b5406e31d73525f9a5bf54e78534d746479a56dcd02f4142c98d057ff841510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b59138286216630bc0d7bedc910e14f8be819727de272335ce2b242f032646dacfb1729488433aae682a8c73c55ae800925cc7ab668d313e5be143d13f79848000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c223bc5876b3d9004177844e0610b2989153894a43060bab4bc024bb1a0c23a34d9316ba10468648122c48e75314467eb13d82d05011b3848e5ca5114fb245fc37e28c21a55b1567cee18dfe0d63a7b7a40e671a982cab89d1de1748c46033e771c458d91a78f73dc91540dfdc1e05a732ce899874539ab30aa9e692ec46a0c5afcaf7b57b159786aed222f49bda98085f2ae34b7b5ecc3c9796c1d02a6bd5319eeb47b614632564b94191a2f5a92027952453b238886df376daee74fa36444f4a9dce13766015171033ad6cd6d79d748f260c920c486fa9604cc61b205553ccd26695d8438ce7a1823a92abd41610285cd1e28578ca089c645b142bb8ef94a6cbaf3ab02eae727dc996c1917122849e973e933b2df2a8eb212145bef07ab65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000300000000000000de0a000000000000bca132581320eaf40ebf8463bbbb5e2a6ffc4f8e2a2166c5dc6f0866eb057137007d0b024c8590c49e53c04c832c15a30bfc1d245ef1f2aa6f3a889785a5c27dc022a56c0378b25d8801c8f2ec3a3cf08bd788812e36c99044260bafb1328156b39c698dd12e5cb4630b6ef4c459035388834209eee16eaeac770e2edd913acb8a060000000000000000a0724e18090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000b42234e0165f976f8574243de6f958e8ed4ef574b2ff1f0fd173128289023585c3990d4f8adf70164087fadfe117ddf30b16f7aab6dbb2bdf023fdacb07307e1676a1a15174adcd73461a229c730612cc3666ce0694ecdeeff8e23e405408df700a0724e180900000000000000000000000000000000000000000000000000000000000000000000407ce11e216e503e64852a21b678352b27d46e89c2ebc98806aba5172e2c5203891ed5490e7c160ffacb606073882504947fd17e235fdd58eff2c3df408ae307fa6651c5c6fd2d291ecbf86f72a8e0fe8b93df3adc9b00c8f822b4f281522be6b0a8e436610d967af758a8e05cdb2af374ca81c26400f3fe25550e77abe46aa16ce2792d9948f73dea713cb619f89cdc0a6899ca6d3f0ce9f34e7df6a007d447000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce1729d6da3ce9d0aa65d195134eace5fc58aa79f6ecf3298fed1852465ded2b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c68a13e4bc0bbabb53384e9fcbea880d3fc9eb24b8fc835de3e5bfeed1e12029000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000322d53d0d0de1fead3fa2834f1b4831f954b48df7606bea92b3c49cd8a99d957000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fdc85bfe0f1fca4914603d82606a800df96b0fb1422dc3735f776cdb89930f510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d4f20ac33775d313d66d27ee41272aeab67ffb9f5c2eb3f8854eb6790b48805000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df69af22234b84cac47ff36bea4ddecbada14875be629c97fe16d87a6006855f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea8a7fb53728eec35cc2ba3f5db53ccdd656140d45620e5e111fe220aa131f620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004fa298dce1d7b273bbc2232b719732f1b1248ac90148df4b4a709936496d35160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df6739bfd500c986d7f9c27379add953908ea4324ca512dbcbc30bb2da2a23a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009761276efa9c67cf9be80dfbed113f65cadfa444cac590e8280fa051a44c5163000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d593fd8677afc5b80b6b86e2de4c1ea8add64ad47fb275a33f8c823d77f5f266000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0c8fceaf6e0edde4f160ee09d88a18a85dacea6b9a63bd694cb2cd802cdbe1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029b5406e31d73525f9a5bf54e78534d746479a56dcd02f4142c98d057ff841510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b59138286216630bc0d7bedc910e14f8be819727de272335ce2b242f032646dacfb1729488433aae682a8c73c55ae800925cc7ab668d313e5be143d13f79848000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c223bc5876b3d9004177844e0610b2989153894a43060bab4bc024bb1a0c23a34d9316ba10468648122c48e75314467eb13d82d05011b3848e5ca5114fb245fc37e28c21a55b1567cee18dfe0d63a7b7a40e671a982cab89d1de1748c46033e771c458d91a78f73dc91540dfdc1e05a732ce899874539ab30aa9e692ec46a0c971be1d39388534b90c42a458bda1521c21550f0486ab3cbb6ba22367cdce05495a8b985f73eb8940e63db843c23c7ebb5f384038dbdcdd355262f83315df324b605eba05bfcddaa5d038e676dc1bc04a4e5b556f15f688d8aff5d29853f480be87a7681377217c22631c1089606ab33b4b05e790b3d32d9197e1be5f2cf56286cbaf3ab02eae727dc996c1917122849e973e933b2df2a8eb212145bef07ab65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000200000000000000020000000000000001225e295731033ef1d495ecfad57912ff306e2dc67609e4fa1827a160563a193620281b0a1712c722138bc0fdd295024677e54762a00a52ee871d4c62114b4ed0a0167660d23e0c7693abcf7c57014ab2fe95d9f1bbe1507e4c731c3005c86b0fffffffffffffffff9049cc4f267bacdab84251b26ed472c2f924b43ead5b4c5672145c01df04a9eff3e4c278ba1b3b670e96a7f6d65112ee18d02de8e8658ee654d8f8824a74b48885f4dd51003b92c3eb0bb323f255b774ab0c94cb8a395108898ff34cb9441a765cd2655d0b34d78f40ca5e63c48a46e8a180597a81758bb2cd8a07e16490fe8fd73d08950091513ffdeddfce7a21634d89bc1ecb9a0dc79fa2d593b9e35ce71693d7463d48679034d175f88e0f000a4ce7ad88851134546691a55b92aa3dff12afdb9e528a0000000000000000954c70d5cb220e0e8fba54db012e06b762102344f65f56cc881b98ad01fb820371a9f7c2dbbc5d52852e03276a101fcb5f07a222b32bcd56586159a7bffb93033825b9a4d3d28544cd8ca4bf20fd40f9319e03dc3f119985a60398111b771d0a00aa72a53e5f2313f049ffe4919d2e9f6f8407217cb3d108c15e6f04676c6e2f6d1dccaa6ac8b0baa7fef54f78416fffe3b031342a1a85218f136291371d724a64f600c029b1894cb719dba17bfc0b361bf6b67b3a50dae6d6665c187c58b4ebacffffffffffffffff64d2de20010a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000254896ec224c9b2d74c4a8db676a93e3ecc0f96b65c5941a3975840bca9cd885f15a3c1806bfb8c78f5c2b7a3e9fccbe21a6058af1f955dc546f8ee767d361d8c6e591e93a840345072199dfb9550aff39636e6b77fc773e6d5b942011a3b9dd80e9b9b7b0c546bd7010fde73e261baf3acdfb1d8ad079af02ccf246f4de3f8764d2de20010a0000000000000000000000000000000000000000000000000000000000000000000089f3d97d2cb1b31a19a31c4edc25c2b5d2df1fe9d006a781cb37a99538fd8e063375aa7d8052f0ecebeacfc0c8b2e35832ed740e89fcb72a2a565f33bd77b709b60600000000000014030000000000001875469976a92eb68b1c7a18275a7a622c01081b07fd96d9833939463e230d6e040000000000000020af62ca70a615f87cf6da1af29a186bfe6e5d5dc8a8184bd30d7aa8f7d1b81ca9703982147815a4b026ffe1f76538ee01b24ce794d176cfc9334e08b7239044434475899b54265e35677d5584faa04a1b5078a3ea5bb152fc96f78cda30a417bca132581320eaf40ebf8463bbbb5e2a6ffc4f8e2a2166c5dc6f0866eb05713701225e295731033ef1d495ecfad57912ff306e2dc67609e4fa1827a160563a193620281b0a1712c722138bc0fdd295024677e54762a00a52ee871d4c62114b4ed0a0167660d23e0c7693abcf7c57014ab2fe95d9f1bbe1507e4c731c3005c86b0fffffffffffffffff9049cc4f267bacdab84251b26ed472c2f924b43ead5b4c5672145c01df04a9eff3e4c278ba1b3b670e96a7f6d65112ee18d02de8e8658ee654d8f8824a74b48885f4dd51003b92c3eb0bb323f255b774ab0c94cb8a395108898ff34cb9441a765cd2655d0b34d78f40ca5e63c48a46e8a180597a81758bb2cd8a07e16490fe8fd73d08950091513ffdeddfce7a21634d89bc1ecb9a0dc79fa2d593b9e35ce71693d7463d48679034d175f88e0f000a4ce7ad88851134546691a55b92aa3dff12afdb9e528a00aa72a53e5f2313f049ffe4919d2e9f6f8407217cb3d108c15e6f04676c6e2f6d1dccaa6ac8b0baa7fef54f78416fffe3b031342a1a85218f136291371d724a64f600c029b1894cb719dba17bfc0b361bf6b67b3a50dae6d6665c187c58b4ebacffffffffffffffff64d2de20010a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000254896ec224c9b2d74c4a8db676a93e3ecc0f96b65c5941a3975840bca9cd885f15a3c1806bfb8c78f5c2b7a3e9fccbe21a6058af1f955dc546f8ee767d361d8c6e591e93a840345072199dfb9550aff39636e6b77fc773e6d5b942011a3b9dd80e9b9b7b0c546bd7010fde73e261baf3acdfb1d8ad079af02ccf246f4de3f8700e40b5402000000000000000000000000e40b54020000000100000000000000a1efe9c81e86d3810f678cc8496773df5ef769eb7f62016d44975c2302174bf3dbf8b985dba81df59cce6c1ba9a8aa4da703cf1ced452a7e833cc1495c4a8fa90087da7399d01834a0a22d085694fa0aebc2593f6755507027760df182ecc383120c1ebb0df5ddb30a32eb36066b98948c5741f936ab83df2936fceae7884a7ab7c71d51679ff6bc160dd84ceac53b2e167bffe9679cb28ceea582f92b0f47c8bcde4bcc39b13cab36333d40c53f7054359e702069a65ed20675cb86eb8005ff890102000000000000000000000000000000000000000000000000000000000000000800000000000000776974686472617790020000000000004bc665f6a2503a3d71784ff4c403c70d1a7bee8cff3447979a63c0460d810b5932a5236771b1769f31627179c29a0858479e9720b7fdb01309830c8dbdb4552a9cfb16cf8d366d1e9c35f6dff235ba15a618422def10788217be2cf4bdef7d14c5fc9e6814455a8633d4a946ee369acb7c6c53b1cd7fcb43e1bd3843bbf22a02fd3b3b38ceae1c92540bcce8b31beb3f1ed73739536d3d9be3d7733a3ae43a5d36d1b18acd7d2096938c0404d949960c37b09a7dbdc75608de04859933a7865dc3e9cbf4b28f5496f83dbe899da717335fd2fbfe2088b06bb8fc754fe4f0141150544f022f777d510c7e50c3796c82d326453231e5d2ad200d155ff297a2753b6f0262db3ae4f045d689d1e7ebd1a4f2725c80e5a428fbd1f16c21f6f1e539186437b609606bf79e3fe5c8d72811af9eea5b8442a6dfca6cd98fb692acc82611e6be5ef74d66122eeecd2b5501d1b857a64253d6f3f11534bfce088ed5ac4c318bbfee571147d5ff06b11bcc8323d176b54837ddac1e2ea61f12f2c418b938f1ec4e235c29d61d1456dce7232cf282135be2fbf27ac0b42828d42539c62be97c3282f6d8feec12f05fe13eb0b8c4643ea3600ae66d3bddffc88e9cb040066e056831afd2b3fba8704270a2c125731597057ccc6829aea05fb9a6308605a54c5c512bfa8dcb2948ed5acd43f0568a450ba4635093a541aa7395520f2f803a0ce7eb6443a28d0c77271047cb43792fd5cebab2555d47aceacf56070944c99389160000000000000000ab2029eaa12b7dfaf512f3ebe940166ecc69505903659245150b628f312ad3ccbe98e5aa5b784a900eb52f23ae26ff0f2f75211fbb3db7c7a5ba57c60284fac87fdc87cb2d88c9467a75938de3389f93c04c8fd0059b1e0afaa8208750be000d0000000000000000242807afc2af7b1086ea75c69b920c948c22f753ba1624a74b6de9cdb64303335951eeef6a8bb247dfef9141e8524b13d408533a11de737fdc4209421751066544bac7593c39d823d6f48de476538ae926dfde5053ec5066b0c53020e08391090e4d7d7d5c3b7a397045b8e1d4363e01d14bf3b53ef6daf50e672fece065bda557f27e7a90d3f38a8d672128ca7cf6ffa24a95017bdae7850b175348843b1c051217be6ac4489e0735c5011fbb7953fc0465a3abf48ffb4e21e01333bc0ccbd50000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file From 6a10f4687ab25f51b68e33733cbc31b5314108e5 Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 13:55:13 +0200 Subject: [PATCH 05/15] rusk-recovery: Rename circuits --- rusk-recovery/src/keys.rs | 8 ++++---- rusk-recovery/src/keys/circuits.rs | 20 ++++++++------------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/rusk-recovery/src/keys.rs b/rusk-recovery/src/keys.rs index 34f4fbf370..aa99217b0a 100644 --- a/rusk-recovery/src/keys.rs +++ b/rusk-recovery/src/keys.rs @@ -128,10 +128,10 @@ pub fn exec(keep_circuits: bool) -> Result<(), Box> { // it is also possible to fetch a circuit by its ID, however that ID changes // when the circuit changes. let circuits = circuits_from_names(&[ - "ExecuteCircuitOneTwo", - "ExecuteCircuitTwoTwo", - "ExecuteCircuitThreeTwo", - "ExecuteCircuitFourTwo", + "TxCircuitOneTwo", + "TxCircuitTwoTwo", + "TxCircuitThreeTwo", + "TxCircuitFourTwo", "LicenseCircuit", ])?; diff --git a/rusk-recovery/src/keys/circuits.rs b/rusk-recovery/src/keys/circuits.rs index 5e1aab7dae..77a03235c0 100644 --- a/rusk-recovery/src/keys/circuits.rs +++ b/rusk-recovery/src/keys/circuits.rs @@ -14,24 +14,20 @@ use execution_core::transfer::phoenix::{TxCircuit, NOTES_TREE_DEPTH}; use license_circuits::LicenseCircuit; -type ExecuteCircuitOneTwo = TxCircuit; -type ExecuteCircuitTwoTwo = TxCircuit; -type ExecuteCircuitThreeTwo = TxCircuit; -type ExecuteCircuitFourTwo = TxCircuit; +type TxCircuitOneTwo = TxCircuit; +type TxCircuitTwoTwo = TxCircuit; +type TxCircuitThreeTwo = TxCircuit; +type TxCircuitFourTwo = TxCircuit; use rusk_profile::{Circuit as CircuitProfile, Theme}; pub fn cache_all() -> io::Result<()> { // cache the circuit description, this only updates the circuit description // if the new circuit is different from a previously cached version - cache::(Some(String::from("ExecuteCircuitOneTwo")))?; - cache::(Some(String::from("ExecuteCircuitTwoTwo")))?; - cache::(Some(String::from( - "ExecuteCircuitThreeTwo", - )))?; - cache::(Some(String::from( - "ExecuteCircuitFourTwo", - )))?; + cache::(Some(String::from("TxCircuitOneTwo")))?; + cache::(Some(String::from("TxCircuitTwoTwo")))?; + cache::(Some(String::from("TxCircuitThreeTwo")))?; + cache::(Some(String::from("TxCircuitFourTwo")))?; cache::(Some(String::from("LicenseCircuit")))?; Ok(()) From 2ed5759c8682ed6dd83689011e8ecaa4fb84e5b1 Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 13:55:49 +0200 Subject: [PATCH 06/15] wallet-core: Add transaction creation functionalities --- wallet-core/Cargo.toml | 6 +- wallet-core/src/keys.rs | 10 +- wallet-core/src/lib.rs | 45 ++++- wallet-core/src/transaction.rs | 320 +++++++++++++++++++++++++++++++++ wallet-core/tests/keys.rs | 56 +++--- 5 files changed, 398 insertions(+), 39 deletions(-) create mode 100644 wallet-core/src/transaction.rs diff --git a/wallet-core/Cargo.toml b/wallet-core/Cargo.toml index 74b5a46a9c..9007a178ef 100644 --- a/wallet-core/Cargo.toml +++ b/wallet-core/Cargo.toml @@ -6,13 +6,15 @@ edition = "2021" [dependencies] dusk-bytes = "0.1" bytecheck = { version = "0.6", default-features = false } -execution-core = { version = "0.1", path = "../execution-core/" } zeroize = { version = "1", default-features = false, features = ["derive"] } rand_chacha = { version = "0.3", default-features = false } sha2 = { version = "0.10", default-features = false } +rand = { version = "0.8", default-features = false } +ff = { version = "0.13", default-features = false } +poseidon-merkle = { version = "0.7", features = ["rkyv-impl"] } +execution-core = { version = "0.1", path = "../execution-core/" } [dev-dependencies] rand = "0.8" -ff = { version = "0.13", default-features = false } [features] diff --git a/wallet-core/src/keys.rs b/wallet-core/src/keys.rs index 76535042c7..357e08dc49 100644 --- a/wallet-core/src/keys.rs +++ b/wallet-core/src/keys.rs @@ -30,7 +30,8 @@ use crate::RNG_SEED; pub fn derive_bls_sk(seed: &[u8; RNG_SEED], index: u8) -> BlsSecretKey { // note that if we change the string used for the rng, all previously // generated keys will become invalid - BlsSecretKey::random(&mut rng_with_index(seed, index, b"BSK")) + // NOTE: When breaking the keys, we will want to change the string too + BlsSecretKey::random(&mut rng_with_index(seed, index, b"SK")) } /// Generates a [`PhoenixSecretKey`] from a seed and index. @@ -40,7 +41,8 @@ pub fn derive_bls_sk(seed: &[u8; RNG_SEED], index: u8) -> BlsSecretKey { pub fn derive_phoenix_sk(seed: &[u8; RNG_SEED], index: u8) -> PhoenixSecretKey { // note that if we change the string used for the rng, all previously // generated keys will become invalid - PhoenixSecretKey::random(&mut rng_with_index(seed, index, b"PSK")) + // NOTE: When breaking the keys, we will want to change the string too + PhoenixSecretKey::random(&mut rng_with_index(seed, index, b"SSK")) } /// Generates multiple [`PhoenixSecretKey`] from a seed and a range of indices. @@ -96,6 +98,10 @@ pub fn rng_with_index( index: u8, termination: &[u8], ) -> ChaCha12Rng { + // NOTE: to not break the test-keys, we cast to a u64 here. Once we are + // ready to use the new keys, the index should not be cast to a u64 + // anymore. + let index = u64::from(index); let mut hash = Sha256::new(); hash.update(seed); diff --git a/wallet-core/src/lib.rs b/wallet-core/src/lib.rs index 69612a40dd..98e64c467b 100644 --- a/wallet-core/src/lib.rs +++ b/wallet-core/src/lib.rs @@ -14,6 +14,7 @@ extern crate alloc; pub mod keys; +pub mod transaction; /// Length of the seed of the generated rng. pub const RNG_SEED: usize = 64; @@ -25,6 +26,8 @@ const MAX_INPUT_NOTES: usize = 4; use alloc::collections::btree_map::BTreeMap; use alloc::vec::Vec; +use dusk_bytes::{DeserializableSlice, Serializable, Write}; + use execution_core::{ transfer::phoenix::{ Note, SecretKey as PhoenixSecretKey, ViewKey as PhoenixViewKey, @@ -56,19 +59,20 @@ pub fn map_owned( /// [`PhoenixViewKey`]. pub fn phoenix_balance( phoenix_vk: &PhoenixViewKey, - notes: impl AsRef<[Note]>, + unspent_notes: impl AsRef<[Note]>, ) -> BalanceInfo { let mut values: Vec = Vec::new(); - notes.as_ref().iter().for_each(|note| { + let unspent_notes = unspent_notes.as_ref(); + for note in unspent_notes { values.push(note.value(Some(phoenix_vk)).unwrap_or_default()); - }); + } values.sort_by(|a, b| b.cmp(a)); - BalanceInfo { - value: values.iter().sum(), - spendable: values[..MAX_INPUT_NOTES].iter().sum(), - } + let spendable = values.iter().take(MAX_INPUT_NOTES).sum(); + let value = spendable + values.iter().skip(MAX_INPUT_NOTES).sum::(); + + BalanceInfo { value, spendable } } /// Information about the balance of a particular key. @@ -81,3 +85,30 @@ pub struct BalanceInfo { /// spend. pub spendable: u64, } + +impl Serializable<{ 2 * u64::SIZE }> for BalanceInfo { + type Error = dusk_bytes::Error; + + fn from_bytes(buf: &[u8; Self::SIZE]) -> Result + where + Self: Sized, + { + let mut reader = &buf[..]; + + let value = u64::from_reader(&mut reader)?; + let spendable = u64::from_reader(&mut reader)?; + + Ok(Self { value, spendable }) + } + + #[allow(unused_must_use)] + fn to_bytes(&self) -> [u8; Self::SIZE] { + let mut buf = [0u8; Self::SIZE]; + let mut writer = &mut buf[..]; + + writer.write(&self.value.to_bytes()); + writer.write(&self.spendable.to_bytes()); + + buf + } +} diff --git a/wallet-core/src/transaction.rs b/wallet-core/src/transaction.rs new file mode 100644 index 0000000000..f55f6f7288 --- /dev/null +++ b/wallet-core/src/transaction.rs @@ -0,0 +1,320 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +//! Implementations of basic wallet functionalities to create transactions. + +use alloc::vec::Vec; +use core::fmt::Debug; + +use rand::{CryptoRng, RngCore}; + +use ff::Field; +use poseidon_merkle::Opening; +use zeroize::Zeroize; + +use execution_core::{ + signatures::bls::SecretKey as BlsSecretKey, + stake::{Stake, Withdraw as StakeWithdraw, STAKE_CONTRACT}, + transfer::{ + contract_exec::{ContractCall, ContractExec}, + phoenix::{ + Note, Prove, PublicKey as PhoenixPublicKey, + SecretKey as PhoenixSecretKey, Transaction as PhoenixTransaction, + NOTES_TREE_DEPTH, + }, + withdraw::{Withdraw, WithdrawReceiver, WithdrawReplayToken}, + Transaction, + }, + BlsScalar, ContractId, JubJubScalar, +}; + +/// Create a [`Transaction`] that is paid in phoenix-notes. +#[allow(clippy::too_many_arguments)] +#[allow(clippy::module_name_repetitions)] +pub fn proven_phoenix_transaction( + rng: &mut R, + sender_sk: &PhoenixSecretKey, + change_pk: &PhoenixPublicKey, + receiver_pk: &PhoenixPublicKey, + inputs: Vec<(Note, Opening<(), NOTES_TREE_DEPTH>)>, + root: BlsScalar, + transfer_value: u64, + obfuscated_transaction: bool, + deposit: u64, + gas_limit: u64, + gas_price: u64, + exec: Option>, +) -> Transaction +where +

::Error: Debug, +{ + PhoenixTransaction::new::( + rng, + sender_sk, + change_pk, + receiver_pk, + inputs, + root, + transfer_value, + obfuscated_transaction, + deposit, + gas_limit, + gas_price, + exec, + ) + .into() +} + +/// An unproven-transaction is nearly identical to a [`PhoenixTransaction`] with +/// the only difference being that it carries a serialized [`TxCircuitVec`] +/// instead of the proof bytes. +/// This way it is possible to delegate the proof generation of the +/// [`TxCircuitVec`] after the unproven transaction was created while at the +/// same time ensuring non-malleability of the transaction, as the transaction's +/// payload-hash is part of the public inputs of the circuit. +/// Once the proof is generated from the [`TxCircuitVec`] bytes, it can +/// replace the serialized circuit in the transaction by calling +/// [`Transaction::replace_proof`]. +#[allow(clippy::too_many_arguments)] +#[allow(clippy::module_name_repetitions)] +pub fn phoenix_transaction( + rng: &mut R, + sender_sk: &PhoenixSecretKey, + change_pk: &PhoenixPublicKey, + receiver_pk: &PhoenixPublicKey, + inputs: Vec<(Note, Opening<(), NOTES_TREE_DEPTH>)>, + root: BlsScalar, + transfer_value: u64, + obfuscated_transaction: bool, + deposit: u64, + gas_limit: u64, + gas_price: u64, + exec: Option>, +) -> PhoenixTransaction { + PhoenixTransaction::new::( + rng, + sender_sk, + change_pk, + receiver_pk, + inputs, + root, + transfer_value, + obfuscated_transaction, + deposit, + gas_limit, + gas_price, + exec, + ) +} + +/// Implementation of the Prove trait that adds the serialized circuit instead +/// of a proof. This way the proof creation can be delegated to a 3rd party. +struct UnprovenProver(); + +impl Prove for UnprovenProver { + // this implementation of the trait will never error. + type Error = (); + + fn prove(circuit: &[u8]) -> Result, Self::Error> { + Ok(circuit.to_vec()) + } +} + +/// Create a [`Transaction`] to stake from phoenix-notes. +#[allow(clippy::too_many_arguments)] +#[allow(clippy::missing_panics_doc)] +pub fn phoenix_stake( + rng: &mut R, + phoenix_sender_sk: &PhoenixSecretKey, + stake_sk: &BlsSecretKey, + inputs: Vec<(Note, Opening<(), NOTES_TREE_DEPTH>)>, + root: BlsScalar, + gas_limit: u64, + gas_price: u64, + stake_value: u64, + current_nonce: u64, +) -> PhoenixTransaction { + let receiver_pk = PhoenixPublicKey::from(phoenix_sender_sk); + let change_pk = receiver_pk; + + let transfer_value = 0; + let obfuscated_transaction = false; + let deposit = stake_value; + + let stake = Stake::new(stake_sk, stake_value, current_nonce + 1); + + let contract_call = ContractCall::new(STAKE_CONTRACT, "stake", &stake) + .expect("rkyv serialization of the stake struct should work."); + + phoenix_transaction( + rng, + phoenix_sender_sk, + &change_pk, + &receiver_pk, + inputs, + root, + transfer_value, + obfuscated_transaction, + deposit, + gas_limit, + gas_price, + Some(contract_call), + ) +} + +/// Create an unproven [`Transaction`] to withdraw stake rewards into a +/// phoenix-note. +#[allow(clippy::too_many_arguments)] +#[allow(clippy::missing_panics_doc)] +pub fn phoenix_withdraw_stake_reward( + rng: &mut R, + phoenix_sender_sk: &PhoenixSecretKey, + stake_sk: &BlsSecretKey, + inputs: Vec<(Note, Opening<(), NOTES_TREE_DEPTH>, BlsScalar)>, + root: BlsScalar, + reward_amount: u64, + gas_limit: u64, + gas_price: u64, +) -> PhoenixTransaction { + let receiver_pk = PhoenixPublicKey::from(phoenix_sender_sk); + let change_pk = receiver_pk; + + let transfer_value = 0; + let obfuscated_transaction = false; + let deposit = 0; + + // split the input notes and openings from the nullifiers + let mut nullifiers = Vec::with_capacity(inputs.len()); + let inputs = inputs + .into_iter() + .map(|(note, opening, nullifier)| { + nullifiers.push(nullifier); + (note, opening) + }) + .collect(); + + let gas_payment_token = WithdrawReplayToken::Phoenix(nullifiers); + + let withdraw = withdraw_to_phoenix( + rng, + phoenix_sender_sk, + STAKE_CONTRACT, + gas_payment_token, + reward_amount, + ); + + let reward_withdraw = StakeWithdraw::new(stake_sk, withdraw); + + let contract_call = + ContractCall::new(STAKE_CONTRACT, "withdraw", &reward_withdraw) + .expect("rkyv should serialize the reward_withdraw correctly"); + + phoenix_transaction( + rng, + phoenix_sender_sk, + &change_pk, + &receiver_pk, + inputs, + root, + transfer_value, + obfuscated_transaction, + deposit, + gas_limit, + gas_price, + Some(contract_call), + ) +} + +/// Create an unproven [`Transaction`] to unstake into a phoenix-note. +#[allow(clippy::too_many_arguments)] +#[allow(clippy::missing_panics_doc)] +pub fn phoenix_unstake( + rng: &mut R, + phoenix_sender_sk: &PhoenixSecretKey, + stake_sk: &BlsSecretKey, + inputs: Vec<(Note, Opening<(), NOTES_TREE_DEPTH>, BlsScalar)>, + root: BlsScalar, + unstake_value: u64, + gas_limit: u64, + gas_price: u64, +) -> PhoenixTransaction { + let receiver_pk = PhoenixPublicKey::from(phoenix_sender_sk); + let change_pk = receiver_pk; + + let transfer_value = 0; + let obfuscated_transaction = false; + let deposit = 0; + + // split the input notes and openings from the nullifiers + let mut nullifiers = Vec::with_capacity(inputs.len()); + let inputs = inputs + .into_iter() + .map(|(note, opening, nullifier)| { + nullifiers.push(nullifier); + (note, opening) + }) + .collect(); + + let gas_payment_token = WithdrawReplayToken::Phoenix(nullifiers); + + let withdraw = withdraw_to_phoenix( + rng, + phoenix_sender_sk, + STAKE_CONTRACT, + gas_payment_token, + unstake_value, + ); + + let unstake = StakeWithdraw::new(stake_sk, withdraw); + + let contract_call = ContractCall::new(STAKE_CONTRACT, "unstake", &unstake) + .expect("unstake should serialize correctly"); + + phoenix_transaction( + rng, + phoenix_sender_sk, + &change_pk, + &receiver_pk, + inputs, + root, + transfer_value, + obfuscated_transaction, + deposit, + gas_limit, + gas_price, + Some(contract_call), + ) +} + +/// Create a [`Withdraw`] struct to be used to withdraw funds from a contract +/// into a phoenix-note. +/// +/// The gas payment can be done by either phoenix or moonlight by setting the +/// `gas_payment_token` accordingly. +fn withdraw_to_phoenix( + rng: &mut R, + receiver_sk: &PhoenixSecretKey, + contract: impl Into, + gas_payment_token: WithdrawReplayToken, + value: u64, +) -> Withdraw { + let withdraw_address = PhoenixPublicKey::from(receiver_sk) + .gen_stealth_address(&JubJubScalar::random(&mut *rng)); + let mut withdraw_note_sk = receiver_sk.gen_note_sk(&withdraw_address); + + let withdraw = Withdraw::new( + rng, + &withdraw_note_sk, + contract.into(), + value, + WithdrawReceiver::Phoenix(withdraw_address), + gas_payment_token, + ); + + withdraw_note_sk.zeroize(); + + withdraw +} diff --git a/wallet-core/tests/keys.rs b/wallet-core/tests/keys.rs index 681592c49f..2e1cfb8752 100644 --- a/wallet-core/tests/keys.rs +++ b/wallet-core/tests/keys.rs @@ -18,11 +18,11 @@ const INDEX: u8 = 42; fn test_derive_phoenix_sk() { // it is important that we always derive the same key from a fixed seed let sk_bytes = [ - 160, 210, 234, 8, 94, 23, 76, 60, 130, 143, 137, 225, 37, 83, 68, 218, - 207, 192, 171, 235, 252, 130, 133, 62, 18, 232, 6, 49, 245, 123, 220, - 12, 250, 111, 39, 88, 24, 41, 156, 174, 241, 14, 118, 173, 11, 53, 192, - 126, 7, 119, 70, 69, 212, 230, 124, 79, 223, 140, 93, 153, 33, 147, - 163, 0, + 12, 16, 72, 188, 33, 76, 44, 178, 86, 123, 107, 153, 230, 149, 238, + 131, 87, 30, 94, 88, 52, 129, 247, 167, 30, 167, 163, 246, 68, 254, 14, + 9, 218, 135, 245, 104, 11, 190, 143, 129, 83, 202, 64, 179, 157, 248, + 175, 120, 157, 220, 98, 211, 141, 50, 224, 8, 1, 125, 29, 180, 206, + 195, 34, 0, ]; assert_eq!(derive_phoenix_sk(&SEED, INDEX).to_bytes(), sk_bytes); } @@ -31,18 +31,18 @@ fn test_derive_phoenix_sk() { fn test_derive_multiple_phoenix_sk() { // it is important that we always derive the same key from a fixed seed let sk_bytes_0 = [ - 160, 210, 234, 8, 94, 23, 76, 60, 130, 143, 137, 225, 37, 83, 68, 218, - 207, 192, 171, 235, 252, 130, 133, 62, 18, 232, 6, 49, 245, 123, 220, - 12, 250, 111, 39, 88, 24, 41, 156, 174, 241, 14, 118, 173, 11, 53, 192, - 126, 7, 119, 70, 69, 212, 230, 124, 79, 223, 140, 93, 153, 33, 147, - 163, 0, + 12, 16, 72, 188, 33, 76, 44, 178, 86, 123, 107, 153, 230, 149, 238, + 131, 87, 30, 94, 88, 52, 129, 247, 167, 30, 167, 163, 246, 68, 254, 14, + 9, 218, 135, 245, 104, 11, 190, 143, 129, 83, 202, 64, 179, 157, 248, + 175, 120, 157, 220, 98, 211, 141, 50, 224, 8, 1, 125, 29, 180, 206, + 195, 34, 0, ]; let sk_bytes_1 = [ - 0, 229, 97, 222, 152, 25, 163, 173, 84, 216, 211, 69, 205, 122, 63, - 227, 98, 234, 163, 66, 145, 71, 217, 221, 29, 78, 36, 77, 68, 29, 144, - 2, 235, 80, 237, 21, 95, 54, 16, 89, 74, 200, 124, 248, 119, 216, 38, - 167, 19, 129, 205, 138, 218, 57, 198, 4, 4, 202, 115, 62, 55, 213, 141, - 0, + 185, 163, 40, 99, 29, 14, 67, 189, 145, 215, 252, 61, 146, 211, 135, + 55, 80, 69, 220, 183, 145, 4, 252, 186, 244, 79, 124, 177, 227, 35, + 209, 5, 100, 181, 254, 1, 111, 180, 155, 211, 140, 23, 252, 248, 103, + 44, 132, 14, 19, 18, 204, 101, 4, 200, 125, 185, 143, 68, 157, 251, + 129, 238, 137, 5, ]; let keys = derive_multiple_phoenix_sk(&SEED, INDEX..INDEX + 2); @@ -54,11 +54,11 @@ fn test_derive_multiple_phoenix_sk() { fn test_derive_phoenix_pk() { // it is important that we always derive the same key from a fixed seed let pk_bytes = [ - 59, 192, 170, 209, 99, 97, 60, 124, 218, 81, 61, 102, 25, 235, 14, 87, - 219, 234, 56, 102, 10, 111, 22, 189, 171, 101, 180, 168, 17, 70, 72, - 101, 135, 243, 55, 243, 138, 103, 185, 26, 196, 219, 84, 126, 33, 115, - 84, 60, 38, 41, 79, 104, 232, 222, 105, 2, 60, 185, 149, 50, 207, 43, - 89, 100, + 51, 204, 45, 112, 212, 44, 118, 183, 148, 176, 254, 135, 253, 117, 230, + 62, 177, 139, 2, 57, 21, 150, 41, 86, 118, 239, 75, 194, 148, 129, 225, + 38, 132, 140, 106, 77, 181, 217, 196, 50, 135, 177, 158, 153, 43, 147, + 159, 217, 0, 160, 89, 95, 67, 160, 42, 74, 19, 1, 221, 216, 126, 204, + 206, 209, ]; assert_eq!(derive_phoenix_pk(&SEED, INDEX).to_bytes(), pk_bytes); } @@ -67,11 +67,11 @@ fn test_derive_phoenix_pk() { fn test_derive_phoenix_vk() { // it is important that we always derive the same key from a fixed seed let vk_bytes = [ - 160, 210, 234, 8, 94, 23, 76, 60, 130, 143, 137, 225, 37, 83, 68, 218, - 207, 192, 171, 235, 252, 130, 133, 62, 18, 232, 6, 49, 245, 123, 220, - 12, 135, 243, 55, 243, 138, 103, 185, 26, 196, 219, 84, 126, 33, 115, - 84, 60, 38, 41, 79, 104, 232, 222, 105, 2, 60, 185, 149, 50, 207, 43, - 89, 100, + 12, 16, 72, 188, 33, 76, 44, 178, 86, 123, 107, 153, 230, 149, 238, + 131, 87, 30, 94, 88, 52, 129, 247, 167, 30, 167, 163, 246, 68, 254, 14, + 9, 132, 140, 106, 77, 181, 217, 196, 50, 135, 177, 158, 153, 43, 147, + 159, 217, 0, 160, 89, 95, 67, 160, 42, 74, 19, 1, 221, 216, 126, 204, + 206, 209, ]; assert_eq!(derive_phoenix_vk(&SEED, INDEX).to_bytes(), vk_bytes); } @@ -80,9 +80,9 @@ fn test_derive_phoenix_vk() { fn test_derive_bls_sk() { // it is important that we always derive the same key from a fixed seed let sk_bytes = [ - 130, 180, 24, 224, 131, 143, 97, 18, 120, 53, 37, 39, 251, 44, 121, - 168, 4, 248, 29, 176, 142, 136, 224, 188, 159, 246, 73, 6, 112, 174, 6, - 7, + 95, 35, 167, 191, 106, 171, 71, 158, 159, 39, 84, 1, 132, 238, 152, + 235, 154, 5, 250, 158, 255, 195, 79, 95, 193, 58, 36, 189, 0, 99, 230, + 86, ]; assert_eq!(derive_bls_sk(&SEED, INDEX).to_bytes(), sk_bytes); } From aacc6b94d91a98d9d9ef562ac4de461add3bb31c Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 13:56:31 +0200 Subject: [PATCH 07/15] test-wallet: Integrate the new wallet-core --- test-wallet/Cargo.toml | 3 +- test-wallet/src/imp.rs | 920 ++++++++++++++++------------------------- test-wallet/src/lib.rs | 185 ++++----- 3 files changed, 431 insertions(+), 677 deletions(-) diff --git a/test-wallet/Cargo.toml b/test-wallet/Cargo.toml index a7e59cef5a..f9b8b25eaf 100644 --- a/test-wallet/Cargo.toml +++ b/test-wallet/Cargo.toml @@ -12,11 +12,12 @@ sha2 = { version = "^0.10", default-features = false } dusk-bytes = "^0.1" poseidon-merkle = { version = "0.7", features = ["rkyv-impl"] } rkyv = { version = "0.7", default-features = false } -rusk-prover = { version = "0.3.0", path = "../rusk-prover", default-features = false } ff = { version = "0.13", default-features = false } +zeroize = { version = "1", default-features = false, features = ["derive"] } # rusk dependencies execution-core = { version = "0.1.0", path = "../execution-core" } +wallet-core = { version = "0.1", path = "../wallet-core/" } [dev-dependencies] rand = "^0.8" diff --git a/test-wallet/src/imp.rs b/test-wallet/src/imp.rs index 566f72b32b..2ed5c01e64 100644 --- a/test-wallet/src/imp.rs +++ b/test-wallet/src/imp.rs @@ -4,45 +4,48 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use crate::{BalanceInfo, ProverClient, StateClient, Store}; +use crate::{ProverClient, StateClient, Store}; use core::convert::Infallible; -use alloc::string::{FromUtf8Error, String}; +use alloc::string::FromUtf8Error; use alloc::vec::Vec; use dusk_bytes::Error as BytesError; +use poseidon_merkle::Opening; +use rand_core::{CryptoRng, Error as RngError, RngCore}; +use rkyv::ser::serializers::{ + AllocScratchError, CompositeSerializerError, SharedSerializeMapError, +}; +use rkyv::validation::validators::CheckDeserializeError; +use zeroize::Zeroize; + use execution_core::{ - signatures::{ - bls::PublicKey as BlsPublicKey, schnorr::SecretKey as SchnorrSecretKey, - }, - stake::{Stake, StakeData, Withdraw as StakeWithdraw, STAKE_CONTRACT}, + signatures::bls::{PublicKey as BlsPublicKey, SecretKey as BlsSecretKey}, + stake::StakeData, transfer::{ - contract_exec::{ContractCall, ContractDeploy, ContractExec}, + contract_exec::ContractExec, moonlight::{AccountData, Transaction as MoonlightTransaction}, phoenix::{ - Error as PhoenixError, Fee, Note, Payload as PhoenixPayload, - PublicKey, SecretKey, TxSkeleton, ViewKey, OUTPUT_NOTES, + Error as PhoenixError, Note, PublicKey as PhoenixPublicKey, + SecretKey as PhoenixSecretKey, ViewKey as PhoenixViewKey, + NOTES_TREE_DEPTH, }, - withdraw::{Withdraw, WithdrawReceiver, WithdrawReplayToken}, Transaction, }, - BlsScalar, JubJubScalar, + BlsScalar, }; -use ff::Field; -use rand_core::{CryptoRng, Error as RngError, RngCore}; -use rkyv::ser::serializers::{ - AllocScratchError, CompositeSerializerError, SharedSerializeMapError, +use wallet_core::{ + keys::{derive_bls_sk, derive_phoenix_sk}, + phoenix_balance, + transaction::{ + phoenix_stake, phoenix_transaction, phoenix_unstake, + phoenix_withdraw_stake_reward, + }, + BalanceInfo, }; -use rkyv::validation::validators::CheckDeserializeError; -use rusk_prover::{UnprovenTransaction, UnprovenTransactionInput}; const MAX_INPUT_NOTES: usize = 4; -const SCRATCH_SIZE: usize = 2048; - -const TX_STAKE: &str = "stake"; -const TX_UNSTAKE: &str = "unstake"; -const TX_WITHDRAW: &str = "withdraw"; type SerializerError = CompositeSerializerError< Infallible, @@ -68,12 +71,12 @@ pub enum Error { Bytes(BytesError), /// Bytes were meant to be utf8 but aren't. Utf8(FromUtf8Error), - /// Originating from the transaction model. + /// Originating from the phoenix transaction model. Phoenix(PhoenixError), - /// Not enough balance to perform transaction. + /// Not enough balance to perform phoenix transaction. NotEnoughBalance, /// Note combination for the given value is impossible given the maximum - /// amount if inputs in a transaction. + /// amount if inputs in a phoenix transaction. NoteCombinationProblem, /// The key is already staked. This happens when there already is an amount /// staked for a key and the user tries to make a stake transaction. @@ -92,7 +95,7 @@ pub enum Error { stake: StakeData, }, /// The key has no reward. This happens when a key has no reward in the - /// stake contract and the user tries to make a withdraw transaction. + /// stake contract and the user tries to make a stake withdraw transaction. NoReward { /// The key that has no reward. key: BlsPublicKey, @@ -206,228 +209,214 @@ where SC: StateClient, PC: ProverClient, { + /// Retrieve the secret key with the given index. + pub fn phoenix_secret_key( + &self, + index: u8, + ) -> Result> { + self.store + .phoenix_secret_key(index) + .map_err(Error::from_store_err) + } + /// Retrieve the public key with the given index. - pub fn public_key( + pub fn phoenix_public_key( + &self, + index: u8, + ) -> Result> { + self.store + .phoenix_public_key(index) + .map_err(Error::from_store_err) + } + + /// Retrieve the account secret key with the given index. + pub fn account_secret_key( &self, - index: u64, - ) -> Result> { + index: u8, + ) -> Result> { self.store - .fetch_secret_key(index) - .map(|sk| PublicKey::from(&sk)) + .account_secret_key(index) .map_err(Error::from_store_err) } /// Retrieve the account public key with the given index. pub fn account_public_key( &self, - index: u64, + index: u8, ) -> Result> { self.store - .fetch_account_secret_key(index) - .map(|stake_sk| From::from(&stake_sk)) + .account_public_key(index) .map_err(Error::from_store_err) } /// Fetches the notes and nullifiers in the state and returns the notes that /// are still available for spending. - fn unspent_notes( + fn unspent_notes_and_nullifiers( &self, - sk: &SecretKey, - ) -> Result, Error> { - let vk = ViewKey::from(sk); + sk: &PhoenixSecretKey, + ) -> Result, Error> { + let vk = PhoenixViewKey::from(sk); - let notes = - self.state.fetch_notes(&vk).map_err(Error::from_state_err)?; + let notes: Vec = self + .state + .fetch_notes(&vk) + .map_err(Error::from_state_err)? + .into_iter() + .map(|(note, _bh)| note) + .collect(); let nullifiers: Vec<_> = - notes.iter().map(|(n, _)| n.gen_nullifier(sk)).collect(); + notes.iter().map(|n| n.gen_nullifier(sk)).collect(); let existing_nullifiers = self .state .fetch_existing_nullifiers(&nullifiers) .map_err(Error::from_state_err)?; - let unspent_notes = notes + let unspent_notes_and_nullifiers = notes .into_iter() .zip(nullifiers.into_iter()) - .filter(|(_, nullifier)| !existing_nullifiers.contains(nullifier)) - .map(|((note, _), _)| note) + .filter(|(_note, nullifier)| { + !existing_nullifiers.contains(nullifier) + }) .collect(); - Ok(unspent_notes) + Ok(unspent_notes_and_nullifiers) } - /// Here we fetch the notes and perform a "minimum number of notes - /// required" algorithm to select which ones to use for this TX. This is - /// done by picking notes largest to smallest until they combined have - /// enough accumulated value. - /// - /// We also return the outputs with a possible change note (if applicable). + /// Here we fetch the notes and their nullifiers to cover the + /// transaction-costs. #[allow(clippy::type_complexity)] - fn inputs_and_change_output( + fn input_notes_nullifiers( &self, - rng: &mut Rng, - sender_sk: &SecretKey, - sender_pk: &PublicKey, - receiver_pk: &PublicKey, - transfer_value: u64, - max_fee: u64, - deposit: u64, - ) -> Result< - ( - Vec<(Note, u64, JubJubScalar)>, - [(Note, u64, JubJubScalar, [JubJubScalar; 2]); OUTPUT_NOTES], - ), - Error, - > { - let notes = self.unspent_notes(sender_sk)?; - let mut notes_and_values = Vec::with_capacity(notes.len()); + sender_sk: &PhoenixSecretKey, + transaction_cost: u64, + ) -> Result, Error> { + let sender_vk = PhoenixViewKey::from(sender_sk); - let sender_vk = ViewKey::from(sender_sk); + // decrypt the value of all unspent note + let unspent_notes_nullifiers = + self.unspent_notes_and_nullifiers(sender_sk)?; + let mut notes_values_nullifiers = + Vec::with_capacity(unspent_notes_nullifiers.len()); let mut accumulated_value = 0; - for note in notes.into_iter() { + for (note, nullifier) in unspent_notes_nullifiers { let val = note.value(Some(&sender_vk))?; - let value_blinder = note.value_blinder(Some(&sender_vk))?; - accumulated_value += val; - notes_and_values.push((note, val, value_blinder)); + notes_values_nullifiers.push((note, val, nullifier)); } - if accumulated_value < transfer_value + max_fee { + if accumulated_value < transaction_cost { return Err(Error::NotEnoughBalance); } - let inputs = - pick_notes(transfer_value + max_fee + deposit, notes_and_values); + // pick the four smallest notes that cover the costs + let inputs = pick_notes(transaction_cost, notes_values_nullifiers); if inputs.is_empty() { return Err(Error::NoteCombinationProblem); } - let (transfer_note, transfer_value_blinder, transfer_sender_blinder) = - generate_obfuscated_note( - rng, - sender_pk, - receiver_pk, - transfer_value, - ); - - let change = inputs.iter().map(|v| v.1).sum::() - - transfer_value - - max_fee - - deposit; - let change_sender_blinder = [ - JubJubScalar::random(&mut *rng), - JubJubScalar::random(&mut *rng), - ]; - let change_note = Note::transparent( - rng, - sender_pk, - sender_pk, - change, - change_sender_blinder, - ); - - let outputs = [ - ( - transfer_note, - transfer_value, - transfer_value_blinder, - transfer_sender_blinder, - ), - ( - change_note, - change, - JubJubScalar::zero(), - change_sender_blinder, - ), - ]; - - Ok((inputs, outputs)) + Ok(inputs) } - fn phoenix_transaction( + /// Here we fetch the notes, their openings and nullifiers to cover the + /// transfer-costs. + #[allow(clippy::type_complexity)] + fn input_notes_openings_nullifiers( &self, - rng: &mut Rng, - sender_sk: &SecretKey, - receiver_pk: &PublicKey, - value: u64, - gas_limit: u64, - gas_price: u64, - deposit: u64, - exec: MaybeExec, - ) -> Result> - where - Rng: RngCore + CryptoRng, - MaybeExec: MaybePhoenixExec, - { - let sender_pk = PublicKey::from(sender_sk); - - let (inputs, outputs) = self.inputs_and_change_output( - rng, - &sender_sk, - &sender_pk, - &receiver_pk, - value, - gas_limit * gas_price, - deposit, - )?; + sender_sk: &PhoenixSecretKey, + transaction_cost: u64, + ) -> Result< + Vec<(Note, Opening<(), NOTES_TREE_DEPTH>, BlsScalar)>, + Error, + > { + let notes_and_nullifiers = + self.input_notes_nullifiers(sender_sk, transaction_cost)?; + + let mut notes_openings_nullifiers = + Vec::with_capacity(notes_and_nullifiers.len()); + for (note, nullifier) in notes_and_nullifiers.into_iter() { + let opening = self + .state + .fetch_opening(¬e) + .map_err(Error::from_state_err)?; + notes_openings_nullifiers.push((note, opening, nullifier)); + } - let fee = Fee::new(rng, &sender_pk, gas_limit, gas_price); - let contract_call = exec.maybe_phoenix_exec( - rng, - inputs.iter().map(|(n, _, _)| n.clone()).collect(), - ); + Ok(notes_openings_nullifiers) + } - let utx = new_unproven_tx( - rng, - &self.state, - &sender_sk, - inputs, - outputs, - fee, - deposit, - contract_call, - ) - .map_err(Error::from_state_err)?; + /// Here we fetch the notes and their openings to cover the + /// transfer-costs. + #[allow(clippy::type_complexity)] + fn input_notes_openings( + &self, + sender_sk: &PhoenixSecretKey, + transaction_cost: u64, + ) -> Result)>, Error> + { + let notes_and_nullifiers = + self.input_notes_nullifiers(sender_sk, transaction_cost)?; + + let mut notes_openings = Vec::with_capacity(notes_and_nullifiers.len()); + for (note, _nullifier) in notes_and_nullifiers.into_iter() { + let opening = self + .state + .fetch_opening(¬e) + .map_err(Error::from_state_err)?; + notes_openings.push((note, opening)); + } - self.prover - .compute_proof_and_propagate(&utx) - .map_err(Error::from_prover_err) + Ok(notes_openings) } /// Execute a generic contract call or deployment, using Phoenix notes to /// pay for gas. #[allow(clippy::too_many_arguments)] - pub fn phoenix_execute( + pub fn phoenix_execute( &self, rng: &mut Rng, - exec: impl Into, - sender_index: u64, + sender_index: u8, gas_limit: u64, gas_price: u64, deposit: u64, - ) -> Result> - where - Rng: RngCore + CryptoRng, - { - let sender_sk = self - .store - .fetch_secret_key(sender_index) - .map_err(Error::from_store_err)?; - let receiver_pk = PublicKey::from(&sender_sk); + exec: impl Into, + ) -> Result> { + let mut sender_sk = self.phoenix_secret_key(sender_index)?; + let receiver_pk = self.phoenix_public_key(sender_index)?; + let change_pk = receiver_pk; + + let input_notes_openings = self.input_notes_openings( + &sender_sk, + gas_limit * gas_price + deposit, + )?; + + let root = self.state.fetch_root().map_err(Error::from_state_err)?; + + let transfer_value = 0; + let obfuscated_transaction = false; - self.phoenix_transaction( + let utx = phoenix_transaction( rng, &sender_sk, + &change_pk, &receiver_pk, - 0, + input_notes_openings, + root, + transfer_value, + obfuscated_transaction, + deposit, gas_limit, gas_price, - deposit, - exec.into(), - ) + Some(exec), + ); + + sender_sk.zeroize(); + + PC::compute_proof_and_propagate(&utx) + .map_err(|e| Error::from_prover_err(e)) } /// Transfer Dusk in the form of Phoenix notes from one key to another. @@ -435,27 +424,46 @@ where pub fn phoenix_transfer( &self, rng: &mut Rng, - sender_index: u64, - receiver_pk: &PublicKey, - value: u64, + sender_index: u8, + receiver_pk: &PhoenixPublicKey, + transfer_value: u64, gas_limit: u64, gas_price: u64, ) -> Result> { - let sender_sk = self - .store - .fetch_secret_key(sender_index) - .map_err(Error::from_store_err)?; + let mut sender_sk = self.phoenix_secret_key(sender_index)?; + let change_pk = self.phoenix_public_key(sender_index)?; + + let input_notes_openings = self.input_notes_openings( + &sender_sk, + transfer_value + gas_limit * gas_price, + )?; + + let root = self.state.fetch_root().map_err(Error::from_state_err)?; - self.phoenix_transaction( + let obfuscated_transaction = true; + let deposit = 0; + + let exec: Option = None; + + let utx = phoenix_transaction( rng, &sender_sk, - receiver_pk, - value, + &change_pk, + &receiver_pk, + input_notes_openings, + root, + transfer_value, + obfuscated_transaction, + deposit, gas_limit, gas_price, - 0, - None, - ) + exec, + ); + + sender_sk.zeroize(); + + PC::compute_proof_and_propagate(&utx) + .map_err(|e| Error::from_prover_err(e)) } /// Stakes an amount of Dusk using Phoenix notes. @@ -463,220 +471,167 @@ where pub fn phoenix_stake( &self, rng: &mut Rng, - sender_index: u64, - staker_index: u64, - value: u64, + sender_index: u8, + staker_index: u8, + stake_value: u64, gas_limit: u64, gas_price: u64, ) -> Result> { - let sender_sk = self - .store - .fetch_secret_key(sender_index) - .map_err(Error::from_store_err)?; - let receiver_pk = PublicKey::from(&sender_sk); - - let stake_sk = self - .store - .fetch_account_secret_key(staker_index) - .map_err(Error::from_store_err)?; - let stake_pk = BlsPublicKey::from(&stake_sk); + let mut phoenix_sender_sk = self.phoenix_secret_key(sender_index)?; + let mut stake_sk = self.account_secret_key(staker_index)?; - let stake = self - .state - .fetch_stake(&stake_pk) - .map_err(Error::from_state_err)?; + let stake_pk = BlsPublicKey::from(&stake_sk); - let stake = Stake::new(&stake_sk, value, stake.nonce + 1); + let inputs = self.input_notes_openings( + &phoenix_sender_sk, + gas_limit * gas_price + stake_value, + )?; - let stake_bytes = rkyv::to_bytes::<_, SCRATCH_SIZE>(&stake) - .expect("Should serialize Stake correctly") - .to_vec(); + let root = self.state.fetch_root().map_err(Error::from_state_err)?; - let contract_call = ContractCall { - contract: STAKE_CONTRACT, - fn_name: String::from(TX_STAKE), - fn_args: stake_bytes, - }; + let current_nonce = self + .state + .fetch_stake(&stake_pk) + .map_err(Error::from_state_err)? + .nonce; - self.phoenix_transaction( + let utx = phoenix_stake( rng, - &sender_sk, - &receiver_pk, - 0, + &phoenix_sender_sk, + &stake_sk, + inputs, + root, gas_limit, gas_price, - value, - Some(ContractExec::Call(contract_call)), - ) + stake_value, + current_nonce, + ); + + stake_sk.zeroize(); + phoenix_sender_sk.zeroize(); + + PC::compute_proof_and_propagate(&utx) + .map_err(|e| Error::from_prover_err(e)) } /// Unstakes a key from the stake contract, using Phoenix notes. pub fn phoenix_unstake( &self, rng: &mut Rng, - sender_index: u64, - staker_index: u64, + sender_index: u8, + staker_index: u8, gas_limit: u64, gas_price: u64, ) -> Result> { - let sender_sk = self - .store - .fetch_secret_key(sender_index) - .map_err(Error::from_store_err)?; - let receiver_pk = PublicKey::from(&sender_sk); - - let stake_sk = self - .store - .fetch_account_secret_key(staker_index) - .map_err(Error::from_store_err)?; + let mut phoenix_sender_sk = self.phoenix_secret_key(sender_index)?; + let mut stake_sk = self.account_secret_key(staker_index)?; + let stake_pk = BlsPublicKey::from(&stake_sk); + let inputs = self.input_notes_openings_nullifiers( + &phoenix_sender_sk, + gas_limit * gas_price, + )?; + + let root = self.state.fetch_root().map_err(Error::from_state_err)?; + let stake = self .state .fetch_stake(&stake_pk) .map_err(Error::from_state_err)?; - let amount = stake.amount.ok_or(Error::NotStaked { - key: stake_pk, - stake, - })?; + let staked_amount = stake + .amount + .ok_or(Error::NotStaked { + key: stake_pk, + stake, + })? + .value; - self.phoenix_transaction( + let utx = phoenix_unstake( rng, - &sender_sk, - &receiver_pk, - 0, + &phoenix_sender_sk, + &stake_sk, + inputs, + root, + staked_amount, gas_limit, gas_price, - 0, - |rng: &mut Rng, input_notes: Vec| { - let address = receiver_pk - .gen_stealth_address(&JubJubScalar::random(&mut *rng)); - let note_sk = sender_sk.gen_note_sk(&address); - - let nullifiers = input_notes - .into_iter() - .map(|note| note.gen_nullifier(&sender_sk)) - .collect(); - - let withdraw = Withdraw::new( - rng, - ¬e_sk, - STAKE_CONTRACT, - amount.value, - WithdrawReceiver::Phoenix(address), - WithdrawReplayToken::Phoenix(nullifiers), - ); - - let withdraw = StakeWithdraw::new(&stake_sk, withdraw); - - let withdraw_bytes = - rkyv::to_bytes::<_, SCRATCH_SIZE>(&withdraw) - .expect("Serializing Withdraw should succeed") - .to_vec(); - - ContractCall { - contract: STAKE_CONTRACT, - fn_name: String::from(TX_UNSTAKE), - fn_args: withdraw_bytes, - } - }, - ) + ); + + stake_sk.zeroize(); + phoenix_sender_sk.zeroize(); + + PC::compute_proof_and_propagate(&utx) + .map_err(|e| Error::from_prover_err(e)) } /// Withdraw the accumulated staking reward for a key, into Phoenix notes. /// Rewards are accumulated by participating in the consensus. - pub fn phoenix_withdraw( + pub fn phoenix_stake_withdraw( &self, rng: &mut Rng, - sender_index: u64, - staker_index: u64, + sender_index: u8, + staker_index: u8, gas_limit: u64, gas_price: u64, ) -> Result> { - let sender_sk = self - .store - .fetch_secret_key(sender_index) - .map_err(Error::from_store_err)?; - let receiver_pk = PublicKey::from(&sender_sk); - - let stake_sk = self - .store - .fetch_account_secret_key(staker_index) - .map_err(Error::from_store_err)?; + let mut phoenix_sender_sk = self.phoenix_secret_key(sender_index)?; + let mut stake_sk = self.account_secret_key(staker_index)?; + let stake_pk = BlsPublicKey::from(&stake_sk); - let stake = self + let inputs = self.input_notes_openings_nullifiers( + &phoenix_sender_sk, + gas_limit * gas_price, + )?; + + let root = self.state.fetch_root().map_err(Error::from_state_err)?; + + let stake_reward = self .state .fetch_stake(&stake_pk) - .map_err(Error::from_state_err)?; - - if stake.reward == 0 { - return Err(Error::NoReward { - key: stake_pk, - stake, - }); - } + .map_err(Error::from_state_err)? + .reward; - self.phoenix_transaction( + let utx = phoenix_withdraw_stake_reward( rng, - &sender_sk, - &receiver_pk, - 0, + &phoenix_sender_sk, + &stake_sk, + inputs, + root, + stake_reward, gas_limit, gas_price, - 0, - |rng: &mut Rng, input_notes: Vec| { - let address = receiver_pk - .gen_stealth_address(&JubJubScalar::random(&mut *rng)); - let note_sk = sender_sk.gen_note_sk(&address); - - let nullifiers = input_notes - .into_iter() - .map(|note| note.gen_nullifier(&sender_sk)) - .collect(); - - let withdraw = Withdraw::new( - rng, - ¬e_sk, - STAKE_CONTRACT, - stake.reward, - WithdrawReceiver::Phoenix(address), - WithdrawReplayToken::Phoenix(nullifiers), - ); - - let unstake = StakeWithdraw::new(&stake_sk, withdraw); - - let unstake_bytes = rkyv::to_bytes::<_, SCRATCH_SIZE>(&unstake) - .expect("Serializing Withdraw should succeed") - .to_vec(); - - ContractCall { - contract: STAKE_CONTRACT, - fn_name: String::from(TX_WITHDRAW), - fn_args: unstake_bytes, - } - }, - ) + ); + + stake_sk.zeroize(); + phoenix_sender_sk.zeroize(); + + PC::compute_proof_and_propagate(&utx) + .map_err(|e| Error::from_prover_err(e)) } - /// Transfer Dusk from one key to another using moonlight. + /// Transfer Dusk from one account to another using moonlight. pub fn moonlight_transfer( &self, - from: u64, - to: BlsPublicKey, + from_index: u8, + to_account: BlsPublicKey, value: u64, gas_limit: u64, gas_price: u64, - ) -> Result> { + ) -> Result> { + let deposit = 0; + let exec: Option = None; + self.moonlight_transaction( - from, - Some(to), + from_index, + Some(to_account), value, - 0, + deposit, gas_limit, gas_price, - None::, + exec, ) } @@ -684,24 +639,21 @@ where #[allow(clippy::too_many_arguments)] pub fn moonlight_transaction( &self, - from: u64, - to: Option, + from_index: u8, + to_account: Option, value: u64, deposit: u64, gas_limit: u64, gas_price: u64, exec: Option>, - ) -> Result> { - let from_sk = self - .store - .fetch_account_secret_key(from) - .map_err(Error::from_store_err)?; - - let from = BlsPublicKey::from(&from_sk); + ) -> Result> { + let mut seed = self.store.get_seed().map_err(Error::from_store_err)?; + let mut from_sk = derive_bls_sk(&seed, from_index); + let from_account = BlsPublicKey::from(&from_sk); let account = self .state - .fetch_account(&from) + .fetch_account(&from_account) .map_err(Error::from_state_err)?; // technically this check is not necessary, but it's nice to not spam @@ -712,47 +664,46 @@ where } let nonce = account.nonce + 1; - Ok(MoonlightTransaction::new( - &from_sk, to, value, deposit, gas_limit, gas_price, nonce, exec, - ) - .into()) + let tx = MoonlightTransaction::new( + &from_sk, to_account, value, deposit, gas_limit, gas_price, nonce, + exec, + ); + + seed.zeroize(); + from_sk.zeroize(); + + Ok(tx.into()) } /// Gets the balance of a key. pub fn get_balance( &self, - sk_index: u64, + sk_index: u8, ) -> Result> { - let sender_sk = self - .store - .fetch_secret_key(sk_index) - .map_err(Error::from_store_err)?; - let vk = ViewKey::from(&sender_sk); - - let notes = self.unspent_notes(&sender_sk)?; - let mut values = Vec::with_capacity(notes.len()); + let mut seed = self.store.get_seed().map_err(Error::from_store_err)?; + let mut phoenix_sk = derive_phoenix_sk(&seed, sk_index); + let phoenix_vk = PhoenixViewKey::from(&phoenix_sk); - for note in notes.into_iter() { - values.push(note.value(Some(&vk))?); - } - values.sort_by(|a, b| b.cmp(a)); + let unspent_notes: Vec = self + .unspent_notes_and_nullifiers(&phoenix_sk)? + .into_iter() + .map(|(note, _nullifier)| note) + .collect(); + let balance = phoenix_balance(&phoenix_vk, unspent_notes); - let spendable = values.iter().take(MAX_INPUT_NOTES).sum(); - let value = - spendable + values.iter().skip(MAX_INPUT_NOTES).sum::(); + seed.zeroize(); + phoenix_sk.zeroize(); - Ok(BalanceInfo { value, spendable }) + Ok(balance) } /// Gets the stake and the expiration of said stake for a key. pub fn get_stake( &self, - sk_index: u64, + sk_index: u8, ) -> Result> { - let account_sk = self - .store - .fetch_account_secret_key(sk_index) - .map_err(Error::from_store_err)?; + let mut seed = self.store.get_seed().map_err(Error::from_store_err)?; + let mut account_sk = derive_bls_sk(&seed, sk_index); let account_pk = BlsPublicKey::from(&account_sk); @@ -761,18 +712,19 @@ where .fetch_stake(&account_pk) .map_err(Error::from_state_err)?; + seed.zeroize(); + account_sk.zeroize(); + Ok(stake) } /// Gets the account data for a key. pub fn get_account( &self, - sk_index: u64, + sk_index: u8, ) -> Result> { - let account_sk = self - .store - .fetch_account_secret_key(sk_index) - .map_err(Error::from_store_err)?; + let mut seed = self.store.get_seed().map_err(Error::from_store_err)?; + let mut account_sk = derive_bls_sk(&seed, sk_index); let account_pk = BlsPublicKey::from(&account_sk); @@ -781,149 +733,14 @@ where .fetch_account(&account_pk) .map_err(Error::from_state_err)?; - Ok(account) - } -} - -/// Creates an unproven transaction that conforms to the transfer contract. -#[allow(clippy::too_many_arguments)] -fn new_unproven_tx( - rng: &mut Rng, - state: &SC, - sender_sk: &SecretKey, - inputs: Vec<(Note, u64, JubJubScalar)>, - outputs: [(Note, u64, JubJubScalar, [JubJubScalar; 2]); OUTPUT_NOTES], - fee: Fee, - deposit: u64, - exec: Option>, -) -> Result { - let nullifiers: Vec = inputs - .iter() - .map(|(note, _, _)| note.gen_nullifier(sender_sk)) - .collect(); - - let mut openings = Vec::with_capacity(inputs.len()); - for (note, _, _) in &inputs { - let opening = state.fetch_opening(note)?; - openings.push(opening); - } - - let root = state.fetch_root()?; - - let tx_skeleton = TxSkeleton { - root, - nullifiers, - outputs: [outputs[0].0.clone(), outputs[1].0.clone()], - max_fee: fee.max_fee(), - deposit, - }; - - let payload = PhoenixPayload { - tx_skeleton, - fee, - exec: exec.map(Into::into), - }; - let payload_hash = payload.hash(); - - let inputs: Vec = inputs - .into_iter() - .zip(openings.into_iter()) - .map(|((note, value, value_blinder), opening)| { - UnprovenTransactionInput::new( - rng, - sender_sk, - note, - value, - value_blinder, - opening, - payload_hash, - ) - }) - .collect(); - - let schnorr_sk_a = SchnorrSecretKey::from(sender_sk.a()); - let sig_a = schnorr_sk_a.sign(rng, payload_hash); - let schnorr_sk_b = SchnorrSecretKey::from(sender_sk.b()); - let sig_b = schnorr_sk_b.sign(rng, payload_hash); - - Ok(UnprovenTransaction { - inputs, - outputs, - payload, - sender_pk: PublicKey::from(sender_sk), - signatures: (sig_a, sig_b), - }) -} - -/// Optionally produces contract calls/executions for Phoenix transactions. -trait MaybePhoenixExec { - fn maybe_phoenix_exec( - self, - rng: &mut R, - inputs: Vec, - ) -> Option; -} - -impl MaybePhoenixExec for Option { - fn maybe_phoenix_exec( - self, - _rng: &mut R, - _inputs: Vec, - ) -> Option { - self - } -} - -impl MaybePhoenixExec for ContractExec { - fn maybe_phoenix_exec( - self, - _rng: &mut R, - _inputs: Vec, - ) -> Option { - Some(self) - } -} - -impl MaybePhoenixExec for ContractCall { - fn maybe_phoenix_exec( - self, - _rng: &mut R, - _inputs: Vec, - ) -> Option { - Some(ContractExec::Call(self)) - } -} - -impl MaybePhoenixExec for ContractDeploy { - fn maybe_phoenix_exec( - self, - _rng: &mut R, - _inputs: Vec, - ) -> Option { - Some(ContractExec::Deploy(self)) - } -} + seed.zeroize(); + account_sk.zeroize(); -impl MaybePhoenixExec for F -where - F: FnOnce(&mut R, Vec) -> M, - M: MaybePhoenixExec, -{ - fn maybe_phoenix_exec( - self, - rng: &mut R, - inputs: Vec, - ) -> Option { - // NOTE: it may be (pun intended) possible to get rid of this clone if - // we use a lifetime into a slice of `Note`s. However, it comes at the - // cost of clarity. This is more important here, since this is testing - // infrastructure, and not production code. - let maybe = self(rng, inputs.clone()); - maybe.maybe_phoenix_exec(rng, inputs) + Ok(account) } } -/// Pick the notes to be used in a transaction from a vector of notes. +/// Pick the notes to be used in a phoenix transaction from a vector of notes. /// /// The notes are picked in a way to maximize the number of notes used, while /// minimizing the value employed. To do this we sort the notes in ascending @@ -935,28 +752,36 @@ where /// the given `value`. fn pick_notes( value: u64, - notes_and_values: Vec<(Note, u64, JubJubScalar)>, -) -> Vec<(Note, u64, JubJubScalar)> { - let mut notes_and_values = notes_and_values; - let len = notes_and_values.len(); + notes_values_nullifiers: Vec<(Note, u64, BlsScalar)>, +) -> Vec<(Note, BlsScalar)> { + let mut notes_values_nullifiers = notes_values_nullifiers; + let len = notes_values_nullifiers.len(); if len <= MAX_INPUT_NOTES { - return notes_and_values; + return notes_values_nullifiers + .into_iter() + .map(|(note, _value, nullifier)| (note, nullifier)) + .collect(); } - notes_and_values.sort_by(|(_, aval, _), (_, bval, _)| aval.cmp(bval)); + notes_values_nullifiers + .sort_by(|(_, aval, _), (_, bval, _)| aval.cmp(bval)); - pick_lexicographic(notes_and_values.len(), |indices| { + pick_lexicographic(notes_values_nullifiers.len(), |indices| { indices .iter() - .map(|index| notes_and_values[*index].1) + .map(|index| ¬es_values_nullifiers[*index].1) .sum::() >= value }) .map(|indices| { indices .into_iter() - .map(|index| notes_and_values[index].clone()) + .map(|index| { + let (note, _value, nullifier) = + notes_values_nullifiers[index].clone(); + (note, nullifier) + }) .collect() }) .unwrap_or_default() @@ -999,30 +824,3 @@ fn pick_lexicographic bool>( None } - -/// Generates an obfuscated note for the given public spend key. -fn generate_obfuscated_note( - rng: &mut Rng, - sender_pk: &PublicKey, - receiver_pk: &PublicKey, - value: u64, -) -> (Note, JubJubScalar, [JubJubScalar; 2]) { - let value_blinder = JubJubScalar::random(&mut *rng); - let sender_blinder = [ - JubJubScalar::random(&mut *rng), - JubJubScalar::random(&mut *rng), - ]; - - ( - Note::obfuscated( - rng, - sender_pk, - receiver_pk, - value, - value_blinder, - sender_blinder, - ), - value_blinder, - sender_blinder, - ) -} diff --git a/test-wallet/src/lib.rs b/test-wallet/src/lib.rs index 5db7beab69..6a25b4c903 100644 --- a/test-wallet/src/lib.rs +++ b/test-wallet/src/lib.rs @@ -15,29 +15,45 @@ extern crate alloc; mod imp; use alloc::vec::Vec; -use dusk_bytes::{DeserializableSlice, Serializable, Write}; +use poseidon_merkle::Opening as PoseidonOpening; +use zeroize::Zeroize; + use execution_core::{ signatures::bls::{PublicKey as BlsPublicKey, SecretKey as BlsSecretKey}, stake::StakeData, transfer::{ moonlight::AccountData, phoenix::{ - Note, SecretKey as PhoenixSecretKey, ViewKey, NOTES_TREE_DEPTH, + Note, PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey, + Transaction as PhoenixTransaction, ViewKey as PhoenixViewKey, + NOTES_TREE_DEPTH, }, Transaction, }, BlsScalar, }; -use poseidon_merkle::Opening as PoseidonOpening; -use rand_chacha::ChaCha12Rng; -use rand_core::SeedableRng; -use sha2::{Digest, Sha256}; + +pub use wallet_core::keys::{ + derive_bls_sk, derive_phoenix_pk, derive_phoenix_sk, +}; pub use imp::*; -pub use rusk_prover::UnprovenTransaction; -/// The maximum size of call data. -pub const MAX_CALL_SIZE: usize = execution_core::ARGBUF_LEN; +/// Types that are client of the prover. +pub trait ProverClient { + /// Error returned by the node client. + type Error; + + /// Requests that a node prove the given unproven [`PhoenixTransaction`] and + /// later propagates it. + /// + /// # Errors + /// This function will error if the proof can not be generated from the + /// unproven transaction. + fn compute_proof_and_propagate( + utx: &PhoenixTransaction, + ) -> Result; +} /// Stores the cryptographic material necessary to derive cryptographic keys. pub trait Store { @@ -47,90 +63,67 @@ pub trait Store { /// Retrieves the seed used to derive keys. fn get_seed(&self) -> Result<[u8; 64], Self::Error>; - /// Retrieves a derived secret key from the store. - /// - /// The provided implementation simply gets the seed and regenerates the key - /// every time with [`generate_sk`]. It may be reimplemented to - /// provide a cache for keys, or implement a different key generation - /// algorithm. - fn fetch_secret_key( + /// Retrieve the secret key with the given index. + fn phoenix_secret_key( &self, - index: u64, + index: u8, ) -> Result { - let seed = self.get_seed()?; - Ok(derive_sk(&seed, index)) + let mut seed = self.get_seed()?; + + let sk = derive_phoenix_sk(&seed, index); + + seed.zeroize(); + + Ok(sk) } - /// Retrieves a derived account secret key from the store. - /// - /// The provided implementation simply gets the seed and regenerates the key - /// every time with [`generate_sk`]. It may be reimplemented to - /// provide a cache for keys, or implement a different key generation - /// algorithm. - fn fetch_account_secret_key( + /// Retrieve the public key with the given index. + fn phoenix_public_key( &self, - index: u64, - ) -> Result { - let seed = self.get_seed()?; - Ok(derive_stake_sk(&seed, index)) - } -} + index: u8, + ) -> Result { + let mut seed = self.get_seed()?; -/// Generates a secret spend key from its seed and index. -/// -/// First the `seed` and then the little-endian representation of the key's -/// `index` are passed through SHA-256. A constant is then mixed in and the -/// resulting hash is then used to seed a `ChaCha12` CSPRNG, which is -/// subsequently used to generate the key. -pub fn derive_sk(seed: &[u8; 64], index: u64) -> PhoenixSecretKey { - let mut hash = Sha256::new(); + let pk = derive_phoenix_pk(&seed, index); - hash.update(seed); - hash.update(index.to_le_bytes()); - hash.update(b"SSK"); + seed.zeroize(); - let hash = hash.finalize().into(); - let mut rng = ChaCha12Rng::from_seed(hash); + Ok(pk) + } - PhoenixSecretKey::random(&mut rng) -} + /// Retrieve the account secret key with the given index. + fn account_secret_key( + &self, + index: u8, + ) -> Result { + let mut seed = self.get_seed()?; -/// Generates a secret key from its seed and index. -/// -/// First the `seed` and then the little-endian representation of the key's -/// `index` are passed through SHA-256. A constant is then mixed in and the -/// resulting hash is then used to seed a `ChaCha12` CSPRNG, which is -/// subsequently used to generate the key. -pub fn derive_stake_sk(seed: &[u8; 64], index: u64) -> BlsSecretKey { - let mut hash = Sha256::new(); + let sk = derive_bls_sk(&seed, index); - hash.update(seed); - hash.update(index.to_le_bytes()); - hash.update(b"SK"); + seed.zeroize(); - let hash = hash.finalize().into(); - let mut rng = ChaCha12Rng::from_seed(hash); + Ok(sk) + } - BlsSecretKey::random(&mut rng) -} + /// Retrieve the account public key with the given index. + fn account_public_key( + &self, + index: u8, + ) -> Result { + let mut seed = self.get_seed()?; -/// Types that are client of the prover. -pub trait ProverClient { - /// Error returned by the node client. - type Error; + let mut sk = derive_bls_sk(&seed, index); + let pk = BlsPublicKey::from(&sk); - /// Requests that a node prove the given transaction and later propagates it - fn compute_proof_and_propagate( - &self, - utx: &UnprovenTransaction, - ) -> Result; -} + seed.zeroize(); + sk.zeroize(); -/// Block height representation -pub type BlockHeight = u64; + Ok(pk) + } +} -/// Tuple containing Note and Block height -pub type EnrichedNote = (Note, BlockHeight); +/// Tuple containing Note and block height +pub type EnrichedNote = (Note, u64); /// Types that are clients of the state API. pub trait StateClient { @@ -140,7 +133,7 @@ pub trait StateClient { /// Find notes for a view key. fn fetch_notes( &self, - vk: &ViewKey, + vk: &PhoenixViewKey, ) -> Result, Self::Error>; /// Fetch the current root of the state. @@ -169,41 +162,3 @@ pub trait StateClient { pk: &BlsPublicKey, ) -> Result; } - -/// Information about the balance of a particular key. -#[derive(Debug, Default, Hash, Clone, Copy, PartialEq, Eq)] -pub struct BalanceInfo { - /// The total value of the balance. - pub value: u64, - /// The maximum _spendable_ value in a single transaction. This is - /// different from `value` since there is a maximum number of notes one can - /// spend. - pub spendable: u64, -} - -impl Serializable<16> for BalanceInfo { - type Error = dusk_bytes::Error; - - fn from_bytes(buf: &[u8; Self::SIZE]) -> Result - where - Self: Sized, - { - let mut reader = &buf[..]; - - let value = u64::from_reader(&mut reader)?; - let spendable = u64::from_reader(&mut reader)?; - - Ok(Self { value, spendable }) - } - - #[allow(unused_must_use)] - fn to_bytes(&self) -> [u8; Self::SIZE] { - let mut buf = [0u8; Self::SIZE]; - let mut writer = &mut buf[..]; - - writer.write(&self.value.to_bytes()); - writer.write(&self.spendable.to_bytes()); - - buf - } -} From fde6e090437a4a04f4e3865dcbd51f3610aeea20 Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 13:56:57 +0200 Subject: [PATCH 08/15] rusk: Integrate wallet-core --- rusk/Cargo.toml | 2 +- rusk/src/lib/http/prover.rs | 5 ++-- rusk/src/lib/verifier.rs | 8 +++--- rusk/tests/common/wallet.rs | 29 +++++++++++----------- rusk/tests/rusk-state.rs | 10 ++++---- rusk/tests/services/contract_deployment.rs | 14 +++++------ rusk/tests/services/gas_behavior.rs | 18 ++++++++------ rusk/tests/services/multi_transfer.rs | 4 ++- rusk/tests/services/stake.rs | 24 ++++++++++++------ rusk/tests/services/transfer.rs | 4 ++- rusk/tests/services/unspendable.rs | 26 ++++++++++--------- 11 files changed, 81 insertions(+), 63 deletions(-) diff --git a/rusk/Cargo.toml b/rusk/Cargo.toml index 3eba219178..4139d1095e 100644 --- a/rusk/Cargo.toml +++ b/rusk/Cargo.toml @@ -61,7 +61,7 @@ async-trait = "0.1" execution-core = { version = "0.1.0", path = "../execution-core", features = ["zk"] } rusk-profile = { version = "0.6", path = "../rusk-profile" } rusk-abi = { version = "0.13.0-rc", path = "../rusk-abi", default-features = false, features = ["host"] } -rusk-prover = { version = "0.3", path = "../rusk-prover", optional = true } +rusk-prover = { version = "0.3", path = "../rusk-prover", features = ["std"], optional = true } ## node dependencies node = { version = "0.1", path = "../node", optional = true } diff --git a/rusk/src/lib/http/prover.rs b/rusk/src/lib/http/prover.rs index 69bb82610f..b24852cf00 100644 --- a/rusk/src/lib/http/prover.rs +++ b/rusk/src/lib/http/prover.rs @@ -4,7 +4,8 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use rusk_prover::{LocalProver, Prover}; +use execution_core::transfer::phoenix::Prove; +use rusk_prover::LocalProver; use super::*; @@ -20,7 +21,7 @@ impl HandleRequest for LocalProver { ) -> anyhow::Result { let topic = request.event.topic.as_str(); let response = match topic { - "prove_execute" => self.prove_execute(request.event_data())?, + "prove_tx_circuit" => LocalProver::prove(request.event_data())?, _ => anyhow::bail!("Unsupported"), }; Ok(ResponseData::new(response)) diff --git a/rusk/src/lib/verifier.rs b/rusk/src/lib/verifier.rs index c3b6541d15..1b323d5123 100644 --- a/rusk/src/lib/verifier.rs +++ b/rusk/src/lib/verifier.rs @@ -18,16 +18,16 @@ use rusk_profile::Circuit as CircuitProfile; use std::sync::LazyLock; pub static VD_EXEC_1_2: LazyLock> = - LazyLock::new(|| fetch_verifier("ExecuteCircuitOneTwo")); + LazyLock::new(|| fetch_verifier("TxCircuitOneTwo")); pub static VD_EXEC_2_2: LazyLock> = - LazyLock::new(|| fetch_verifier("ExecuteCircuitTwoTwo")); + LazyLock::new(|| fetch_verifier("TxCircuitTwoTwo")); pub static VD_EXEC_3_2: LazyLock> = - LazyLock::new(|| fetch_verifier("ExecuteCircuitThreeTwo")); + LazyLock::new(|| fetch_verifier("TxCircuitThreeTwo")); pub static VD_EXEC_4_2: LazyLock> = - LazyLock::new(|| fetch_verifier("ExecuteCircuitFourTwo")); + LazyLock::new(|| fetch_verifier("TxCircuitFourTwo")); /// Verifies the proof of the incoming transaction. pub fn verify_proof(tx: &PhoenixTransaction) -> Result { diff --git a/rusk/tests/common/wallet.rs b/rusk/tests/common/wallet.rs index 5e514df10b..4712d047aa 100644 --- a/rusk/tests/common/wallet.rs +++ b/rusk/tests/common/wallet.rs @@ -10,14 +10,16 @@ use std::sync::{Arc, RwLock}; use crate::common::block::Block as BlockAwait; -use dusk_bytes::{DeserializableSlice, Serializable}; +use dusk_bytes::Serializable; use execution_core::{ - plonk::Proof, signatures::bls::PublicKey as BlsPublicKey, stake::StakeData, transfer::{ moonlight::AccountData, - phoenix::{Note, ViewKey, NOTES_TREE_DEPTH}, + phoenix::{ + Note, Prove, Transaction as PhoenixTransaction, ViewKey, + NOTES_TREE_DEPTH, + }, Transaction, }, BlsScalar, @@ -25,8 +27,8 @@ use execution_core::{ use futures::StreamExt; use poseidon_merkle::Opening as PoseidonOpening; use rusk::{Error, Result, Rusk}; -use rusk_prover::{LocalProver, Prover}; -use test_wallet::{self as wallet, Store, UnprovenTransaction}; +use rusk_prover::LocalProver; +use test_wallet::{self as wallet, Store}; use tracing::info; #[derive(Debug, Clone)] @@ -127,9 +129,7 @@ impl wallet::StateClient for TestStateClient { } #[derive(Default)] -pub struct TestProverClient { - pub prover: LocalProver, -} +pub struct TestProverClient(); impl Debug for TestProverClient { fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -141,14 +141,13 @@ impl wallet::ProverClient for TestProverClient { type Error = Error; /// Requests that a node prove the given transaction and later propagates it fn compute_proof_and_propagate( - &self, - utx: &UnprovenTransaction, + utx: &PhoenixTransaction, ) -> Result { - let utx_bytes = &utx.to_var_bytes()[..]; - let proof = self.prover.prove_execute(utx_bytes)?; - info!("UTX: {}", hex::encode(utx_bytes)); - let proof = Proof::from_slice(&proof).map_err(Error::Serialization)?; - let tx = utx.clone().gen_transaction(proof); + let circuit_bytes = &utx.proof()[..]; + let proof_bytes = LocalProver::prove(circuit_bytes)?; + info!("circuit: {}", hex::encode(circuit_bytes)); + let mut tx = utx.clone(); + tx.replace_proof(proof_bytes); //Propagate is not required yet diff --git a/rusk/tests/rusk-state.rs b/rusk/tests/rusk-state.rs index ecdb1982a3..c34b87238a 100644 --- a/rusk/tests/rusk-state.rs +++ b/rusk/tests/rusk-state.rs @@ -204,12 +204,12 @@ async fn generate_phoenix_txs() -> Result<(), Box> { let mut txs_file = std::fs::File::create("phoenix-txs")?; - for sender_index in 0..N_ADDRESSES as u64 { + for sender_index in 0..N_ADDRESSES as u8 { let wallet = wallet.clone(); let mut rng = StdRng::seed_from_u64(0xdead); - let receiver_index = (sender_index + 1) % N_ADDRESSES as u64; - let receiver = wallet.public_key(receiver_index).unwrap(); + let receiver_index = (sender_index + 1) % N_ADDRESSES as u8; + let receiver = wallet.phoenix_public_key(receiver_index).unwrap(); let task = tokio::task::spawn_blocking(move || { wallet @@ -269,10 +269,10 @@ async fn generate_moonlight_txs() -> Result<(), Box> { let mut txs_file = std::fs::File::create("moonlight-txs")?; - for sender_index in 0..N_ADDRESSES as u64 { + for sender_index in 0..N_ADDRESSES as u8 { let wallet = wallet.clone(); - let receiver_index = (sender_index + 1) % N_ADDRESSES as u64; + let receiver_index = (sender_index + 1) % N_ADDRESSES as u8; let receiver = wallet.account_public_key(receiver_index).unwrap(); let task = tokio::task::spawn_blocking(move || { diff --git a/rusk/tests/services/contract_deployment.rs b/rusk/tests/services/contract_deployment.rs index 59db166d7d..aa4269770d 100644 --- a/rusk/tests/services/contract_deployment.rs +++ b/rusk/tests/services/contract_deployment.rs @@ -30,12 +30,12 @@ use crate::common::wallet::{TestProverClient, TestStateClient, TestStore}; const BLOCK_HEIGHT: u64 = 1; const BLOCK_GAS_LIMIT: u64 = 1_000_000_000_000; const GAS_LIMIT: u64 = 200_000_000; -const GAS_LIMIT_NOT_ENOUGH_TO_SPEND: u64 = 11_000_000; -const GAS_LIMIT_NOT_ENOUGH_TO_SPEND_AND_DEPLOY: u64 = 12_000_000; +const GAS_LIMIT_NOT_ENOUGH_TO_SPEND: u64 = 10_000_000; +const GAS_LIMIT_NOT_ENOUGH_TO_SPEND_AND_DEPLOY: u64 = 11_000_000; const GAS_LIMIT_NOT_ENOUGH_TO_DEPLOY: u64 = 1_200_000; const GAS_PRICE: u64 = 2; const POINT_LIMIT: u64 = 0x10000000; -const SENDER_INDEX: u64 = 0; +const SENDER_INDEX: u8 = 0; const ALICE_CONTRACT_ID: ContractId = { let mut bytes = [0u8; 32]; @@ -122,6 +122,10 @@ fn make_and_execute_transaction_deploy( let tx = wallet .phoenix_execute( &mut rng, + SENDER_INDEX, + gas_limit, + GAS_PRICE, + 0u64, ContractExec::Deploy(ContractDeploy { bytecode: ContractBytecode { hash, @@ -131,10 +135,6 @@ fn make_and_execute_transaction_deploy( constructor_args, nonce: 0, }), - SENDER_INDEX, - gas_limit, - GAS_PRICE, - 0u64, ) .expect("Making transaction should succeed"); diff --git a/rusk/tests/services/gas_behavior.rs b/rusk/tests/services/gas_behavior.rs index d6f1ce9b4f..c8fa0e7ad7 100644 --- a/rusk/tests/services/gas_behavior.rs +++ b/rusk/tests/services/gas_behavior.rs @@ -29,6 +29,8 @@ const INITIAL_BALANCE: u64 = 10_000_000_000; const GAS_LIMIT_0: u64 = 100_000_000; const GAS_LIMIT_1: u64 = 300_000_000; +const GAS_PRICE: u64 = 1; +const DEPOSIT: u64 = 0; // Creates the Rusk initial state for the tests below fn initial_state>(dir: P) -> Result { @@ -38,8 +40,8 @@ fn initial_state>(dir: P) -> Result { new_state(dir, &snapshot, BLOCK_GAS_LIMIT) } -const SENDER_INDEX_0: u64 = 0; -const SENDER_INDEX_1: u64 = 1; +const SENDER_INDEX_0: u8 = 0; +const SENDER_INDEX_1: u8 = 1; fn make_transactions( rusk: &Rusk, @@ -77,11 +79,11 @@ fn make_transactions( let tx_0 = wallet .phoenix_execute( &mut rng, - ContractExec::Call(contract_call.clone()), SENDER_INDEX_0, GAS_LIMIT_0, - 1, - 0, + GAS_PRICE, + DEPOSIT, + ContractExec::Call(contract_call.clone()), ) .expect("Making the transaction should succeed"); @@ -91,11 +93,11 @@ fn make_transactions( let tx_1 = wallet .phoenix_execute( &mut rng, - contract_call, SENDER_INDEX_1, GAS_LIMIT_1, - 1, - 0, + GAS_PRICE, + DEPOSIT, + contract_call, ) .expect("Making the transaction should succeed"); diff --git a/rusk/tests/services/multi_transfer.rs b/rusk/tests/services/multi_transfer.rs index a51c0c0439..92d0aa5fe5 100644 --- a/rusk/tests/services/multi_transfer.rs +++ b/rusk/tests/services/multi_transfer.rs @@ -43,7 +43,9 @@ fn wallet_transfer( amount: u64, ) { // Generate a receiver pk - let receiver = wallet.public_key(3).expect("Failed to get public key"); + let receiver = wallet + .phoenix_public_key(3) + .expect("Failed to get public key"); let mut rng = StdRng::seed_from_u64(0xdead); diff --git a/rusk/tests/services/stake.rs b/rusk/tests/services/stake.rs index 790e973de9..9e6124575e 100644 --- a/rusk/tests/services/stake.rs +++ b/rusk/tests/services/stake.rs @@ -14,12 +14,13 @@ use execution_core::{ stake::{StakeAmount, STAKE_CONTRACT}, transfer::contract_exec::ContractCall, }; + use rand::prelude::*; use rand::rngs::StdRng; use rusk::{Result, Rusk}; use std::collections::HashMap; use tempfile::tempdir; -use test_wallet::{self as wallet, Store}; +use test_wallet::{self as wallet}; use tracing::info; use crate::common::state::{generator_procedure, new_state}; @@ -29,6 +30,8 @@ use crate::common::*; const BLOCK_HEIGHT: u64 = 1; const BLOCK_GAS_LIMIT: u64 = 100_000_000_000; const GAS_LIMIT: u64 = 10_000_000_000; +const GAS_PRICE: u64 = 1; +const DEPOSIT: u64 = 0; // Creates the Rusk initial state for the tests below fn stake_state>(dir: P) -> Result { @@ -68,11 +71,11 @@ fn wallet_stake( .expect("stakeinfo to be found") .amount .is_none(), - "stake amount to be found" + "stake amount not to be found" ); let tx = wallet - .phoenix_stake(&mut rng, 0, 2, value, GAS_LIMIT, 1) + .phoenix_stake(&mut rng, 0, 2, value, GAS_LIMIT, GAS_PRICE) .expect("Failed to create a stake transaction"); let executed_txs = generator_procedure( rusk, @@ -103,7 +106,7 @@ fn wallet_stake( .expect("stake amount to be found"); let tx = wallet - .phoenix_unstake(&mut rng, 0, 0, GAS_LIMIT, 1) + .phoenix_unstake(&mut rng, 0, 0, GAS_LIMIT, GAS_PRICE) .expect("Failed to unstake"); let spent_txs = generator_procedure( rusk, @@ -121,7 +124,7 @@ fn wallet_stake( assert_eq!(stake.amount, None); let tx = wallet - .phoenix_withdraw(&mut rng, 0, 1, GAS_LIMIT, 1) + .phoenix_stake_withdraw(&mut rng, 0, 1, GAS_LIMIT, GAS_PRICE) .expect("failed to withdraw reward"); generator_procedure( rusk, @@ -189,7 +192,7 @@ fn wallet_reward( ) { let mut rng = StdRng::seed_from_u64(0xdead); - let stake_sk = wallet.store().fetch_account_secret_key(2).unwrap(); + let stake_sk = wallet.account_secret_key(2).unwrap(); let stake_pk = BlsPublicKey::from(&stake_sk); let reward_calldata = (stake_pk, 6u32); @@ -203,7 +206,14 @@ fn wallet_reward( ) .expect("calldata should serialize"); let tx = wallet - .phoenix_execute(&mut rng, contract_call, 0, GAS_LIMIT, 1, 0) + .phoenix_execute( + &mut rng, + 0, + GAS_LIMIT, + GAS_PRICE, + DEPOSIT, + contract_call, + ) .expect("Failed to create a reward transaction"); let executed_txs = generator_procedure( rusk, diff --git a/rusk/tests/services/transfer.rs b/rusk/tests/services/transfer.rs index 6bd77a7d03..f4978efcff 100644 --- a/rusk/tests/services/transfer.rs +++ b/rusk/tests/services/transfer.rs @@ -42,7 +42,9 @@ fn wallet_transfer( block_height: u64, ) { // Generate a receiver pk - let receiver_pk = wallet.public_key(1).expect("Failed to get public key"); + let receiver_pk = wallet + .phoenix_public_key(1) + .expect("Failed to get public key"); let mut rng = StdRng::seed_from_u64(0xdead); diff --git a/rusk/tests/services/unspendable.rs b/rusk/tests/services/unspendable.rs index 2f34669a07..67a565ed23 100644 --- a/rusk/tests/services/unspendable.rs +++ b/rusk/tests/services/unspendable.rs @@ -30,6 +30,8 @@ const INITIAL_BALANCE: u64 = 10_000_000_000; const GAS_LIMIT_0: u64 = 20_000_000; // Enough to spend, but OOG during ICC const GAS_LIMIT_1: u64 = 1_000; // Not enough to spend const GAS_LIMIT_2: u64 = 300_000_000; // All ok +const GAS_PRICE: u64 = 1; +const DEPOSIT: u64 = 0; // Creates the Rusk initial state for the tests below fn initial_state>(dir: P) -> Result { @@ -39,9 +41,9 @@ fn initial_state>(dir: P) -> Result { new_state(dir, &snapshot, BLOCK_GAS_LIMIT) } -const SENDER_INDEX_0: u64 = 0; -const SENDER_INDEX_1: u64 = 1; -const SENDER_INDEX_2: u64 = 2; +const SENDER_INDEX_0: u8 = 0; +const SENDER_INDEX_1: u8 = 1; +const SENDER_INDEX_2: u8 = 2; fn make_transactions( rusk: &Rusk, @@ -89,11 +91,11 @@ fn make_transactions( let tx_0 = wallet .phoenix_execute( &mut rng, - ContractExec::Call(contract_call.clone()), SENDER_INDEX_0, GAS_LIMIT_0, - 1, - 0, + GAS_PRICE, + DEPOSIT, + ContractExec::Call(contract_call.clone()), ) .expect("Making the transaction should succeed"); @@ -103,11 +105,11 @@ fn make_transactions( let tx_1 = wallet .phoenix_execute( &mut rng, - ContractExec::Call(contract_call.clone()), SENDER_INDEX_1, GAS_LIMIT_1, - 1, - 0, + GAS_PRICE, + DEPOSIT, + ContractExec::Call(contract_call.clone()), ) .expect("Making the transaction should succeed"); @@ -117,11 +119,11 @@ fn make_transactions( let tx_2 = wallet .phoenix_execute( &mut rng, - contract_call, SENDER_INDEX_2, GAS_LIMIT_2, - 1, - 0, + GAS_PRICE, + DEPOSIT, + contract_call, ) .expect("Making the transaction should succeed"); From 1dfbea212842bef9e079b75cc9e9a98005aeffc7 Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 15:59:02 +0200 Subject: [PATCH 09/15] execution-core: Add `Error` type --- execution-core/src/error.rs | 71 +++++++++++++ execution-core/src/lib.rs | 3 + execution-core/src/transfer.rs | 20 ++-- execution-core/src/transfer/contract_exec.rs | 12 ++- execution-core/src/transfer/phoenix.rs | 106 ++++++++++--------- execution-core/tests/serialization.rs | 9 +- 6 files changed, 154 insertions(+), 67 deletions(-) create mode 100644 execution-core/src/error.rs diff --git a/execution-core/src/error.rs b/execution-core/src/error.rs new file mode 100644 index 0000000000..b2b68e5dfe --- /dev/null +++ b/execution-core/src/error.rs @@ -0,0 +1,71 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +//! Error-type for execution-core. + +use alloc::string::String; +use core::fmt; + +/// The execution-core error type. +#[derive(Debug, Clone)] +pub enum Error { + /// There is not sufficient balance to cover the transaction costs. + InsufficientBalance, + /// A transaction input has been used already. + Replay, + /// The input-note doesn't belong to the given key. + PhoenixOwnership, + /// The transaction circuit wasn't found or is incorrect. + PhoenixCircuit(String), + /// The transaction circuit prover wasn't found or couldn't be created. + PhoenixProver(String), + /// Dusk-bytes InvalidData error + InvalidData, + /// Dusk-bytes BadLength error + BadLength(usize, usize), + /// Dusk-bytes InvalidChar error + InvalidChar(char, usize), + /// Rkyv serialization. + Rkyv(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Execution-Core Error: {:?}", &self) + } +} + +impl From for Error { + fn from(core_error: phoenix_core::Error) -> Self { + #[allow(clippy::match_same_arms)] + match core_error { + phoenix_core::Error::InvalidNoteType(_) => Self::InvalidData, + phoenix_core::Error::MissingViewKey => Self::PhoenixOwnership, + phoenix_core::Error::InvalidEncryption => Self::PhoenixOwnership, + phoenix_core::Error::InvalidData => Self::InvalidData, + phoenix_core::Error::BadLength(found, expected) => { + Self::BadLength(found, expected) + } + phoenix_core::Error::InvalidChar(ch, index) => { + Self::InvalidChar(ch, index) + } + } + } +} + +impl From for Error { + fn from(bytes_error: dusk_bytes::Error) -> Self { + match bytes_error { + dusk_bytes::Error::InvalidData => Self::InvalidData, + dusk_bytes::Error::BadLength { found, expected } => { + Self::BadLength(found, expected) + } + dusk_bytes::Error::InvalidChar { ch, index } => { + Self::InvalidChar(ch, index) + } + } + } +} diff --git a/execution-core/src/lib.rs b/execution-core/src/lib.rs index 1847ad9af8..f66f2e4e08 100644 --- a/execution-core/src/lib.rs +++ b/execution-core/src/lib.rs @@ -19,6 +19,9 @@ pub mod license; pub mod stake; pub mod transfer; +mod error; +pub use error::Error; + mod dusk; pub use dusk::{dusk, from_dusk, Dusk, LUX}; diff --git a/execution-core/src/transfer.rs b/execution-core/src/transfer.rs index 78e8c1e510..910457e4d8 100644 --- a/execution-core/src/transfer.rs +++ b/execution-core/src/transfer.rs @@ -20,7 +20,7 @@ use crate::{ signatures::bls::{ PublicKey as AccountPublicKey, SecretKey as AccountSecretKey, }, - BlsScalar, ContractId, + BlsScalar, ContractId, Error, }; pub mod contract_exec; @@ -52,7 +52,14 @@ pub enum Transaction { impl Transaction { /// Create a new phoenix transaction. - #[must_use] + /// + /// # Errors + /// The creation of a transaction is not possible and will error if: + /// - one of the input-notes doesn't belong to the `sender_sk` + /// - the transaction input doesn't cover the transaction costs + /// - the `inputs` vector is either empty or larger than 4 elements + /// - the `inputs` vector contains duplicate `Note`s + /// - the `Prove` trait is implemented incorrectly #[allow(clippy::too_many_arguments)] pub fn phoenix( rng: &mut R, @@ -67,11 +74,8 @@ impl Transaction { gas_limit: u64, gas_price: u64, exec: Option>, - ) -> Self - where -

::Error: Debug, - { - Self::Phoenix(PhoenixTransaction::new::( + ) -> Result { + Ok(Self::Phoenix(PhoenixTransaction::new::( rng, sender_sk, change_pk, @@ -84,7 +88,7 @@ impl Transaction { gas_limit, gas_price, exec, - )) + )?)) } /// Create a new moonlight transaction. diff --git a/execution-core/src/transfer/contract_exec.rs b/execution-core/src/transfer/contract_exec.rs index 30762d149f..ba744d3859 100644 --- a/execution-core/src/transfer/contract_exec.rs +++ b/execution-core/src/transfer/contract_exec.rs @@ -6,17 +6,17 @@ //! Wrapper for a strip-able bytecode that we want to keep the integrity of. +use alloc::format; use alloc::string::String; use alloc::vec::Vec; use bytecheck::CheckBytes; use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable}; use rkyv::{ - ser::serializers::AllocSerializer, Archive, Deserialize, Fallible, - Serialize, + ser::serializers::AllocSerializer, Archive, Deserialize, Serialize, }; -use crate::{ContractId, ARGBUF_LEN}; +use crate::{ContractId, Error, ARGBUF_LEN}; /// Data for either contract call or contract deployment. #[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)] @@ -128,11 +128,13 @@ impl ContractCall { contract: impl Into, fn_name: impl Into, fn_args: &impl Serialize>, - ) -> Result as Fallible>::Error> { + ) -> Result { Ok(Self { contract: contract.into(), fn_name: fn_name.into(), - fn_args: rkyv::to_bytes::<_, ARGBUF_LEN>(fn_args)?.to_vec(), + fn_args: rkyv::to_bytes::<_, ARGBUF_LEN>(fn_args) + .map_err(|e| Error::Rkyv(format!("{e:?}")))? + .to_vec(), }) } diff --git a/execution-core/src/transfer/phoenix.rs b/execution-core/src/transfer/phoenix.rs index 119ad9faaf..ebd2cafbc1 100644 --- a/execution-core/src/transfer/phoenix.rs +++ b/execution-core/src/transfer/phoenix.rs @@ -25,12 +25,12 @@ use crate::{ transfer::contract_exec::{ ContractBytecode, ContractCall, ContractDeploy, ContractExec, }, - BlsScalar, JubJubAffine, JubJubScalar, + BlsScalar, Error, JubJubAffine, JubJubScalar, }; // phoenix types pub use phoenix_core::{ - value_commitment, Error, Note, PublicKey, SecretKey, Sender, + value_commitment, Error as CoreError, Note, PublicKey, SecretKey, Sender, StealthAddress, TxSkeleton, ViewKey, NOTE_VAL_ENC_SIZE, OUTPUT_NOTES, }; @@ -64,13 +64,13 @@ impl Transaction { /// public-key, the input note positions in the transaction tree and the /// new output-notes. /// - /// # Panics - /// The creation of a transaction is not possible and will panic if: + /// # Errors + /// The creation of a transaction is not possible and will error if: /// - one of the input-notes doesn't belong to the `sender_sk` - /// - the sum of value of the input-notes is smaller than: - /// `transfer_value` + `deposit` + `gas_limit` * `gas_price` + /// - the transaction input doesn't cover the transaction costs /// - the `inputs` vector is either empty or larger than 4 elements - #[must_use] + /// - the `inputs` vector contains duplicate `Note`s + /// - the `Prove` trait is implemented incorrectly #[allow(clippy::too_many_lines)] #[allow(clippy::too_many_arguments)] #[allow(clippy::similar_names)] @@ -87,23 +87,25 @@ impl Transaction { gas_limit: u64, gas_price: u64, exec: Option>, - ) -> Self - where -

::Error: Debug, - { + ) -> Result { let sender_pk = PublicKey::from(sender_sk); let sender_vk = ViewKey::from(sender_sk); - // get input note values and nullifiers + // get input note values, value-blinders and nullifiers let input_len = inputs.len(); let mut input_values = Vec::with_capacity(input_len); + let mut input_value_blinders = Vec::with_capacity(input_len); let mut input_nullifiers = Vec::with_capacity(input_len); for (note, _opening) in &inputs { - input_nullifiers.push(note.gen_nullifier(sender_sk)); - input_values.push( - note.value(Some(&sender_vk)) - .expect("Note should be belonging to the sender"), - ); + let note_nullifier = note.gen_nullifier(sender_sk); + for nullifier in &input_nullifiers { + if note_nullifier == *nullifier { + return Err(Error::Replay); + } + } + input_nullifiers.push(note_nullifier); + input_values.push(note.value(Some(&sender_vk))?); + input_value_blinders.push(note.value_blinder(Some(&sender_vk))?); } let input_value: u64 = input_values.iter().sum(); @@ -113,8 +115,11 @@ impl Transaction { let fee = Fee::new(rng, change_pk, gas_limit, gas_price); let max_fee = fee.max_fee(); + if input_value < transfer_value + max_fee + deposit { + return Err(Error::InsufficientBalance); + } + // Generate output notes: - assert!(input_value >= transfer_value + max_fee + deposit); let transfer_value_blinder = if obfuscated_transaction { JubJubScalar::random(&mut *rng) } else { @@ -163,7 +168,7 @@ impl Transaction { // Now we can set the tx-skeleton, payload and get the payload-hash let tx_skeleton = TxSkeleton { root, - // we also need the nullifiers for the tx-circuit, hence the copy + // we also need the nullifiers for the tx-circuit, hence the clone nullifiers: input_nullifiers.clone(), outputs, max_fee, @@ -184,25 +189,28 @@ impl Transaction { .into_iter() .zip(input_nullifiers) .zip(input_values) - .for_each(|(((note, merkle_opening), nullifier), value)| { - let value_blinder = note - .value_blinder(Some(&sender_vk)) - .expect("the sender should own the input-notes"); - let note_sk = sender_sk.gen_note_sk(note.stealth_address()); - let note_pk_p = JubJubAffine::from( - crate::GENERATOR_NUMS_EXTENDED * note_sk.as_ref(), - ); - let signature = note_sk.sign_double(rng, payload_hash); - input_notes_info.push(InputNoteInfo { - merkle_opening, - note, - note_pk_p, - value, + .zip(input_value_blinders) + .for_each( + |( + (((note, merkle_opening), nullifier), value), value_blinder, - nullifier, - signature, - }); - }); + )| { + let note_sk = sender_sk.gen_note_sk(note.stealth_address()); + let note_pk_p = JubJubAffine::from( + crate::GENERATOR_NUMS_EXTENDED * note_sk.as_ref(), + ); + let signature = note_sk.sign_double(rng, payload_hash); + input_notes_info.push(InputNoteInfo { + merkle_opening, + note, + note_pk_p, + value, + value_blinder, + nullifier, + signature, + }); + }, + ); // Create the information for the output-notes let transfer_value_commitment = @@ -246,7 +254,7 @@ impl Transaction { let schnorr_sk_b = SchnorrSecretKey::from(sender_sk.b()); let sig_b = schnorr_sk_b.sign(rng, payload_hash); - Self { + Ok(Self { payload, proof: P::prove( &TxCircuitVec { @@ -260,9 +268,8 @@ impl Transaction { signatures: (sig_a, sig_b), } .to_var_bytes(), - ) - .expect("The proof generation shouldn't fail with a valid circuit"), - } + )?, + }) } /// Creates a new phoenix transaction given the [`Payload`] and proof. Note @@ -274,9 +281,15 @@ impl Transaction { } /// Replaces the inner `proof` bytes for a given `proof`. - /// Note: This method is likely to invalidate the transaction and should - /// only be used with care. - pub fn replace_proof(&mut self, proof: Vec) { + /// + /// This can be used to delegate the proof generation after a + /// [`Transaction`] is created. + /// In order to do that, the transaction would be created using the + /// serialized circuit-bytes for the proof-field. Those bytes can be + /// sent to a 3rd-party prover-service that generates the proof-bytes + /// and sends them back. The proof-bytes will then replace the + /// circuit-bytes in the transaction using this function. + pub fn set_proof(&mut self, proof: Vec) { self.proof = proof; } @@ -926,14 +939,11 @@ impl From> for TxCircuitVec { /// This trait can be used to implement different methods to generate a proof /// from the circuit-bytes. pub trait Prove { - /// The type returned in the event of an error during proof generation. - type Error; - /// Generate a transaction proof from all the information needed to create a /// tx-circuit. /// /// # Errors /// This function errors in case of an incorrect circuit or of an /// unobtainable prover-key. - fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Self::Error>; + fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Error>; } diff --git a/execution-core/tests/serialization.rs b/execution-core/tests/serialization.rs index 0896d01330..22827c652e 100644 --- a/execution-core/tests/serialization.rs +++ b/execution-core/tests/serialization.rs @@ -4,9 +4,6 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use dusk_bls12_381::BlsScalar; -use dusk_bytes::Error; -use dusk_jubjub::JubJubScalar; use execution_core::{ signatures::bls::{ PublicKey as AccountPublicKey, SecretKey as AccountSecretKey, @@ -21,6 +18,7 @@ use execution_core::{ }, Transaction, }, + BlsScalar, Error, JubJubScalar, }; use ff::Field; use poseidon_merkle::{Item, Tree}; @@ -32,9 +30,7 @@ struct TxCircuitVecProver(); // use the serialized TxCircuitVec as proof. This way that serialization is also // tested. impl Prove for TxCircuitVecProver { - type Error = (); - - fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Self::Error> { + fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Error> { Ok(TxCircuitVec::from_slice(tx_circuit_vec_bytes) .expect("serialization should be ok") .to_var_bytes() @@ -127,6 +123,7 @@ fn new_phoenix_tx( gas_price, exec, ) + .expect("transcaction generation should work") } fn new_moonlight_tx( From 303da2eb329eb8bd1ab6be361e3687061bdeda7f Mon Sep 17 00:00:00 2001 From: moana Date: Fri, 23 Aug 2024 17:14:05 +0200 Subject: [PATCH 10/15] rusk-prover: Integrate `execution-core::Error` --- rusk-prover/Cargo.toml | 1 - rusk-prover/src/errors.rs | 65 ----- rusk-prover/src/lib.rs | 53 ++-- rusk-prover/src/tx.rs | 516 -------------------------------------- 4 files changed, 20 insertions(+), 615 deletions(-) delete mode 100644 rusk-prover/src/errors.rs delete mode 100644 rusk-prover/src/tx.rs diff --git a/rusk-prover/Cargo.toml b/rusk-prover/Cargo.toml index d59531ba92..c1eff3fb58 100644 --- a/rusk-prover/Cargo.toml +++ b/rusk-prover/Cargo.toml @@ -14,7 +14,6 @@ execution-core = { version = "0.1.0", path = "../execution-core", features = ["z [dev-dependencies] hex = "0.4" -tokio = { version = "1", features = ["full"] } rand = "0.8" [features] diff --git a/rusk-prover/src/errors.rs b/rusk-prover/src/errors.rs deleted file mode 100644 index f8506ed88a..0000000000 --- a/rusk-prover/src/errors.rs +++ /dev/null @@ -1,65 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) DUSK NETWORK. All rights reserved. - -use alloc::string::String; -use core::fmt; - -use dusk_plonk::prelude::Error as PlonkError; - -#[derive(Debug)] -pub enum ProverError { - InvalidData { - field: &'static str, - inner: dusk_bytes::Error, - }, - Plonk(PlonkError), - Other(String), -} - -impl ProverError { - pub fn invalid_data(field: &'static str, inner: dusk_bytes::Error) -> Self { - Self::InvalidData { field, inner } - } - - #[cfg(feature = "std")] - pub fn with_context( - context: &'static str, - err: E, - ) -> Self { - Self::from(format!("{context} - {err:?}")) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for ProverError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - -impl From for ProverError { - fn from(e: PlonkError) -> ProverError { - ProverError::Plonk(e) - } -} - -impl fmt::Display for ProverError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ProverError::InvalidData { field, inner } => { - write!(f, "Invalid field '{field}': {inner:?}") - } - ProverError::Plonk(plonk_error) => write!(f, "{:?}", plonk_error), - ProverError::Other(context) => write!(f, "{context}"), - } - } -} - -impl From for ProverError { - fn from(desc: String) -> Self { - ProverError::Other(desc) - } -} diff --git a/rusk-prover/src/lib.rs b/rusk-prover/src/lib.rs index c3486aae76..cfeb8fdf4c 100644 --- a/rusk-prover/src/lib.rs +++ b/rusk-prover/src/lib.rs @@ -13,17 +13,15 @@ extern crate std; use alloc::format; use alloc::vec::Vec; -use dusk_bytes::{Error as BytesError, Serializable}; +use dusk_bytes::Serializable; use dusk_plonk::prelude::Prover as PlonkProver; use once_cell::sync::Lazy; -use execution_core::transfer::phoenix::{ - Prove, TxCircuit, TxCircuitVec, NOTES_TREE_DEPTH, +use execution_core::{ + transfer::phoenix::{Prove, TxCircuit, TxCircuitVec, NOTES_TREE_DEPTH}, + Error, }; -mod errors; -pub use errors::ProverError; - static TX_CIRCUIT_1_2_PROVER: Lazy = Lazy::new(|| fetch_prover("TxCircuitOneTwo")); @@ -40,13 +38,8 @@ static TX_CIRCUIT_4_2_PROVER: Lazy = pub struct LocalProver; impl Prove for LocalProver { - type Error = ProverError; - - fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Self::Error> { - let tx_circuit_vec = TxCircuitVec::from_slice(tx_circuit_vec_bytes) - .map_err(|e| { - ProverError::invalid_data("Invalid tx-circuit bytes", e) - })?; + fn prove(tx_circuit_vec_bytes: &[u8]) -> Result, Error> { + let tx_circuit_vec = TxCircuitVec::from_slice(tx_circuit_vec_bytes)?; #[cfg(not(feature = "no_random"))] let rng = &mut rand::rngs::OsRng; @@ -58,20 +51,18 @@ impl Prove for LocalProver { let (proof, _pi) = match tx_circuit_vec.input_notes_info.len() { 1 => TX_CIRCUIT_1_2_PROVER - .prove(rng, &create_circuit::<1>(tx_circuit_vec)?)?, + .prove(rng, &create_circuit::<1>(tx_circuit_vec)?) + .map_err(|e| Error::PhoenixProver(format!("{e:?}")))?, 2 => TX_CIRCUIT_2_2_PROVER - .prove(rng, &create_circuit::<2>(tx_circuit_vec)?)?, + .prove(rng, &create_circuit::<2>(tx_circuit_vec)?) + .map_err(|e| Error::PhoenixProver(format!("{e:?}")))?, 3 => TX_CIRCUIT_3_2_PROVER - .prove(rng, &create_circuit::<3>(tx_circuit_vec)?)?, + .prove(rng, &create_circuit::<3>(tx_circuit_vec)?) + .map_err(|e| Error::PhoenixProver(format!("{e:?}")))?, 4 => TX_CIRCUIT_4_2_PROVER - .prove(rng, &create_circuit::<4>(tx_circuit_vec)?)?, - _ => { - return Err(ProverError::from(format!( - "Invalid I/O count: {}/{}", - tx_circuit_vec.input_notes_info.len(), - tx_circuit_vec.output_notes_info.len() - ))) - } + .prove(rng, &create_circuit::<4>(tx_circuit_vec)?) + .map_err(|e| Error::PhoenixProver(format!("{e:?}")))?, + _ => return Err(Error::InvalidData), }; Ok(proof.to_bytes().to_vec()) @@ -95,16 +86,12 @@ fn fetch_prover(circuit_name: &str) -> PlonkProver { fn create_circuit( tx_circuit_vec: TxCircuitVec, -) -> Result, ProverError> { +) -> Result, Error> { Ok(TxCircuit { - input_notes_info: tx_circuit_vec.input_notes_info.try_into().map_err( - |_| { - ProverError::invalid_data( - "invalid tx-circuit", - BytesError::InvalidData, - ) - }, - )?, + input_notes_info: tx_circuit_vec + .input_notes_info + .try_into() + .map_err(|e| Error::PhoenixCircuit(format!("{e:?}")))?, output_notes_info: tx_circuit_vec.output_notes_info, payload_hash: tx_circuit_vec.payload_hash, root: tx_circuit_vec.root, diff --git a/rusk-prover/src/tx.rs b/rusk-prover/src/tx.rs deleted file mode 100644 index f4453ff9ed..0000000000 --- a/rusk-prover/src/tx.rs +++ /dev/null @@ -1,516 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) DUSK NETWORK. All rights reserved. - -use alloc::vec; -use alloc::vec::Vec; - -use dusk_bytes::{ - DeserializableSlice, Error as BytesError, Serializable, Write, -}; -use execution_core::{ - plonk::Proof, - signatures::schnorr::{ - Signature as SchnorrSignature, - SignatureDouble as SchnorrSignatureDouble, - }, - transfer::phoenix::{ - Note, Payload as PhoenixPayload, PublicKey as PhoenixPublicKey, - SecretKey as PhoenixSecretKey, Transaction as PhoenixTransaction, - NOTES_TREE_DEPTH, OUTPUT_NOTES, - }, - BlsScalar, JubJubAffine, JubJubExtended, JubJubScalar, - GENERATOR_NUMS_EXTENDED, -}; - -use poseidon_merkle::Opening as PoseidonOpening; -use rand_core::{CryptoRng, RngCore}; - -/// An input to a transaction that is yet to be proven. -#[derive(PartialEq, Debug, Clone)] -pub struct UnprovenTransactionInput { - pub nullifier: BlsScalar, - pub opening: PoseidonOpening<(), NOTES_TREE_DEPTH>, - pub note: Note, - pub value: u64, - pub value_blinder: JubJubScalar, - pub npk_prime: JubJubExtended, - pub sig: SchnorrSignatureDouble, -} - -impl UnprovenTransactionInput { - pub fn new( - rng: &mut Rng, - sender_sk: &PhoenixSecretKey, - note: Note, - value: u64, - value_blinder: JubJubScalar, - opening: PoseidonOpening<(), NOTES_TREE_DEPTH>, - payload_hash: BlsScalar, - ) -> Self { - let nullifier = note.gen_nullifier(sender_sk); - let nsk = sender_sk.gen_note_sk(note.stealth_address()); - let sig = nsk.sign_double(rng, payload_hash); - - let npk_prime = GENERATOR_NUMS_EXTENDED * nsk.as_ref(); - - Self { - note, - value, - value_blinder, - sig, - nullifier, - opening, - npk_prime, - } - } - - /// Serialize the input to a variable size byte buffer. - pub fn to_var_bytes(&self) -> Vec { - let affine_npk_p = JubJubAffine::from(&self.npk_prime); - - let opening_bytes = rkyv::to_bytes::<_, 256>(&self.opening) - .expect("Rkyv serialization should always succeed for an opening") - .to_vec(); - - let mut bytes = Vec::with_capacity( - BlsScalar::SIZE - + Note::SIZE - + JubJubAffine::SIZE - + SchnorrSignatureDouble::SIZE - + u64::SIZE - + JubJubScalar::SIZE - + opening_bytes.len(), - ); - - bytes.extend_from_slice(&self.nullifier.to_bytes()); - bytes.extend_from_slice(&self.note.to_bytes()); - bytes.extend_from_slice(&self.value.to_bytes()); - bytes.extend_from_slice(&self.value_blinder.to_bytes()); - bytes.extend_from_slice(&affine_npk_p.to_bytes()); - bytes.extend_from_slice(&self.sig.to_bytes()); - bytes.extend(opening_bytes); - - bytes - } - - /// Deserializes the the input from bytes. - pub fn from_slice(buf: &[u8]) -> Result { - let mut bytes = buf; - - let nullifier = BlsScalar::from_reader(&mut bytes)?; - let note = Note::from_reader(&mut bytes)?; - let value = u64::from_reader(&mut bytes)?; - let value_blinder = JubJubScalar::from_reader(&mut bytes)?; - let npk_prime = - JubJubExtended::from(JubJubAffine::from_reader(&mut bytes)?); - let sig = SchnorrSignatureDouble::from_reader(&mut bytes)?; - - // `to_vec` is required here otherwise `rkyv` will throw an alignment - // error - #[allow(clippy::unnecessary_to_owned)] - let opening = rkyv::from_bytes(&bytes.to_vec()) - .map_err(|_| BytesError::InvalidData)?; - - Ok(Self { - note, - value, - value_blinder, - sig, - nullifier, - opening, - npk_prime, - }) - } - - /// Returns the nullifier of the input. - pub fn nullifier(&self) -> BlsScalar { - self.nullifier - } - - /// Returns the opening of the input. - pub fn opening(&self) -> &PoseidonOpening<(), NOTES_TREE_DEPTH> { - &self.opening - } - - /// Returns the note of the input. - pub fn note(&self) -> &Note { - &self.note - } - - /// Returns the value of the input. - pub fn value(&self) -> u64 { - self.value - } - - /// Returns the blinding factor for the value of the input. - pub fn value_blinder(&self) -> JubJubScalar { - self.value_blinder - } - - /// Returns the input's note public key prime. - pub fn note_pk_prime(&self) -> JubJubExtended { - self.npk_prime - } - - /// Returns the input's signature. - pub fn signature(&self) -> &SchnorrSignatureDouble { - &self.sig - } -} - -/// A transaction that is yet to be proven. The purpose of this is solely to -/// send to the node to perform a circuit proof. -#[derive(PartialEq, Debug, Clone)] -pub struct UnprovenTransaction { - pub inputs: Vec, - pub outputs: [(Note, u64, JubJubScalar, [JubJubScalar; 2]); OUTPUT_NOTES], - pub payload: PhoenixPayload, - pub sender_pk: PhoenixPublicKey, - pub signatures: (SchnorrSignature, SchnorrSignature), -} - -impl UnprovenTransaction { - /// Consumes self and a proof to generate a transaction. - pub fn gen_transaction(self, proof: Proof) -> PhoenixTransaction { - PhoenixTransaction::from_payload_and_proof( - self.payload, - proof.to_bytes().to_vec(), - ) - } - - /// Serialize the transaction to a variable length byte buffer. - #[allow(unused_must_use)] - pub fn to_var_bytes(&self) -> Vec { - let serialized_inputs: Vec> = self - .inputs - .iter() - .map(UnprovenTransactionInput::to_var_bytes) - .collect(); - let num_inputs = self.inputs.len(); - let total_input_len = serialized_inputs - .iter() - .fold(0, |len, input| len + input.len()); - - const OUTPUT_SIZE: usize = Note::SIZE - + u64::SIZE - + JubJubScalar::SIZE - + 2 * JubJubScalar::SIZE; - let serialized_outputs: Vec<[u8; OUTPUT_SIZE]> = self - .outputs - .iter() - .map(|(note, value, value_blinder, sender_blinder)| { - let mut buf = [0; OUTPUT_SIZE]; - - buf[..Note::SIZE].copy_from_slice(¬e.to_bytes()); - buf[Note::SIZE..Note::SIZE + u64::SIZE] - .copy_from_slice(&value.to_bytes()); - buf[Note::SIZE + u64::SIZE - ..Note::SIZE + u64::SIZE + JubJubScalar::SIZE] - .copy_from_slice(&value_blinder.to_bytes()); - let mut start = Note::SIZE + u64::SIZE + JubJubScalar::SIZE; - buf[start..start + JubJubScalar::SIZE] - .copy_from_slice(&sender_blinder[0].to_bytes()); - start += JubJubScalar::SIZE; - buf[start..start + JubJubScalar::SIZE] - .copy_from_slice(&sender_blinder[1].to_bytes()); - - buf - }) - .collect(); - let num_outputs = self.outputs.len(); - let total_output_len = serialized_outputs - .iter() - .fold(0, |len, output| len + output.len()); - - let payload_bytes = self.payload.to_var_bytes(); - - let size = - // the amount of inputs - u64::SIZE - // the len of each input item - + num_inputs * u64::SIZE - // the total amount of bytes of the inputs - + total_input_len - // the amount of outputs - + u64::SIZE - // the total amount of bytes of the outputs - + total_output_len - // the total amount of bytes of the payload - + u64::SIZE - // the payload - + payload_bytes.len() - // the payload-hash - + BlsScalar::SIZE - // the sender-pk - + PhoenixPublicKey::SIZE - // the two signatures - + 2 * SchnorrSignature::SIZE; - - let mut buf = vec![0; size]; - let mut writer = &mut buf[..]; - - writer.write(&(num_inputs as u64).to_bytes()); - for sinput in serialized_inputs { - writer.write(&(sinput.len() as u64).to_bytes()); - writer.write(&sinput); - } - - writer.write(&(num_outputs as u64).to_bytes()); - for soutput in serialized_outputs { - writer.write(&soutput); - } - - writer.write(&(payload_bytes.len() as u64).to_bytes()); - writer.write(&payload_bytes[..]); - - writer.write(&self.sender_pk.to_bytes()); - writer.write(&self.signatures.0.to_bytes()); - writer.write(&self.signatures.1.to_bytes()); - - buf - } - - /// Deserialize the transaction from a bytes buffer. - pub fn from_slice(buf: &[u8]) -> Result { - let mut buffer = buf; - - let num_inputs = u64::from_reader(&mut buffer)?; - let mut inputs = Vec::with_capacity(num_inputs as usize); - for _ in 0..num_inputs { - let size = u64::from_reader(&mut buffer)? as usize; - inputs.push(UnprovenTransactionInput::from_slice(&buffer[..size])?); - buffer = &buffer[size..]; - } - - let num_outputs = u64::from_reader(&mut buffer)?; - let mut outputs = Vec::with_capacity(num_outputs as usize); - for _ in 0..num_outputs { - let note = Note::from_reader(&mut buffer)?; - let value = u64::from_reader(&mut buffer)?; - let value_blinder = JubJubScalar::from_reader(&mut buffer)?; - let sender_blinder_a = JubJubScalar::from_reader(&mut buffer)?; - let sender_blinder_b = JubJubScalar::from_reader(&mut buffer)?; - - outputs.push(( - note, - value, - value_blinder, - [sender_blinder_a, sender_blinder_b], - )); - } - let outputs: [(Note, u64, JubJubScalar, [JubJubScalar; 2]); - OUTPUT_NOTES] = - outputs.try_into().map_err(|_| BytesError::InvalidData)?; - - let payload_len = u64::from_reader(&mut buffer)?; - let payload = PhoenixPayload::from_slice(buffer)?; - let mut buffer = &buffer[payload_len as usize..]; - - let sender_pk = PhoenixPublicKey::from_reader(&mut buffer)?; - let sig_a = SchnorrSignature::from_reader(&mut buffer)?; - let sig_b = SchnorrSignature::from_reader(&mut buffer)?; - - Ok(Self { - inputs, - outputs, - payload, - sender_pk, - signatures: (sig_a, sig_b), - }) - } - - /// Returns the inputs to the transaction. - pub fn inputs(&self) -> &[UnprovenTransactionInput] { - &self.inputs - } - - /// Returns the outputs of the transaction. - pub fn outputs(&self) -> &[(Note, u64, JubJubScalar, [JubJubScalar; 2])] { - &self.outputs - } - - /// Returns the payload of the transaction. - pub fn payload(&self) -> &PhoenixPayload { - &self.payload - } - - /// Returns the payload-hash of the transaction. - pub fn payload_hash(&self) -> BlsScalar { - self.payload.hash() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use execution_core::{ - signatures::schnorr::SecretKey as SchnorrSecretKey, - transfer::{ - contract_exec::{ContractCall, ContractExec}, - phoenix::{Fee, TxSkeleton}, - }, - }; - use poseidon_merkle::{Item, Tree}; - use rand::{rngs::StdRng, SeedableRng}; - - #[test] - fn serialize_deserialize() -> Result<(), BytesError> { - let mut rng = StdRng::seed_from_u64(0xbeef); - let sender_sk = PhoenixSecretKey::random(&mut rng); - let sender_pk = PhoenixPublicKey::from(&sender_sk); - let receiver_pk = - PhoenixPublicKey::from(&PhoenixSecretKey::random(&mut rng)); - - let transfer_value = 42; - let transfer_value_blinder = JubJubScalar::from(5647890216u64); - let transfer_sender_blinder = - [JubJubScalar::from(57u64), JubJubScalar::from(789u64)]; - let transfer_note = Note::obfuscated( - &mut rng, - &sender_pk, - &receiver_pk, - transfer_value, - transfer_value_blinder, - transfer_sender_blinder, - ); - let change_value = 24; - let change_sender_blinder = - [JubJubScalar::from(7483u64), JubJubScalar::from(265829u64)]; - let change_note = Note::transparent( - &mut rng, - &sender_pk, - &receiver_pk, - change_value, - transfer_sender_blinder, - ); - let tx_skeleton = TxSkeleton { - root: BlsScalar::from(1), - nullifiers: vec![ - BlsScalar::from(2), - BlsScalar::from(3), - BlsScalar::from(4), - BlsScalar::from(5), - ], - outputs: [transfer_note.clone(), change_note.clone()], - max_fee: 10000, - deposit: 20, - }; - - let fee = Fee::new(&mut rng, &sender_pk, 4242, 42); - let call = ContractCall::new( - [10; 32], - "some method", - &vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - ) - .unwrap(); - - let payload = PhoenixPayload { - tx_skeleton, - fee, - exec: Some(ContractExec::Call(call)), - }; - - let sender_blinder_1 = - [JubJubScalar::from(521u64), JubJubScalar::from(6521u64)]; - let sender_blinder_2 = [ - JubJubScalar::from(585631u64), - JubJubScalar::from(65658151u64), - ]; - let value1 = 100; - let value2 = 200; - let note1 = Note::transparent( - &mut rng, - &sender_pk, - &receiver_pk, - value1, - sender_blinder_1, - ); - let note2 = Note::transparent( - &mut rng, - &sender_pk, - &receiver_pk, - value2, - sender_blinder_2, - ); - let mut tree = Tree::new(); - let pos1 = 12; - tree.insert( - pos1, - Item { - hash: note1.hash(), - data: (), - }, - ); - let pos2 = 13; - tree.insert( - pos2, - Item { - hash: note2.hash(), - data: (), - }, - ); - let opening1 = tree.opening(pos1).unwrap(); - let opening2 = tree.opening(pos2).unwrap(); - - let payload_hash = payload.hash(); - let inputs = vec![ - UnprovenTransactionInput::new( - &mut rng, - &sender_sk, - note1, - value1, - JubJubScalar::zero(), - opening1, - payload_hash, - ), - UnprovenTransactionInput::new( - &mut rng, - &sender_sk, - note2, - value2, - JubJubScalar::zero(), - opening2, - payload_hash, - ), - ]; - - let schnorr_sk_a = SchnorrSecretKey::from(sender_sk.a()); - let sig_a = schnorr_sk_a.sign(&mut rng, payload_hash); - let schnorr_sk_b = SchnorrSecretKey::from(sender_sk.b()); - let sig_b = schnorr_sk_b.sign(&mut rng, payload_hash); - - let utx = UnprovenTransaction { - inputs, - outputs: [ - ( - transfer_note, - transfer_value, - transfer_value_blinder, - transfer_sender_blinder, - ), - ( - change_note, - change_value, - JubJubScalar::zero(), - change_sender_blinder, - ), - ], - payload, - sender_pk, - signatures: (sig_a, sig_b), - }; - - let utx_bytes = utx.to_var_bytes(); - let deserialized_utx = UnprovenTransaction::from_slice(&utx_bytes[..])?; - assert_eq!(utx.inputs, deserialized_utx.inputs); - assert_eq!(utx.outputs, deserialized_utx.outputs); - assert_eq!(utx.payload, deserialized_utx.payload); - assert_eq!(utx.sender_pk, deserialized_utx.sender_pk); - assert_eq!(utx.signatures, deserialized_utx.signatures); - - Ok(()) - } -} From ebbd8fcdd134addce26ff5c2cd40ea2eb2c96a73 Mon Sep 17 00:00:00 2001 From: moana Date: Mon, 26 Aug 2024 13:00:49 +0200 Subject: [PATCH 11/15] transfer-contract: Integrate execution-core error --- contracts/transfer/src/error.rs | 12 +++++++----- contracts/transfer/tests/common/utils.rs | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/contracts/transfer/src/error.rs b/contracts/transfer/src/error.rs index 01cfdf9e3b..bba46564d1 100644 --- a/contracts/transfer/src/error.rs +++ b/contracts/transfer/src/error.rs @@ -5,17 +5,19 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use core::fmt; -use execution_core::transfer::phoenix::Error as PhoenixError; +use execution_core::Error as ExecutionError; #[derive(Debug, Clone)] pub enum Error { - Phoenix(PhoenixError), + /// Wrapper of execution-core error type. + Execution(ExecutionError), + /// A contract balance is not sufficient for the requested withdrawal NotEnoughBalance, } -impl From for Error { - fn from(e: PhoenixError) -> Self { - Self::Phoenix(e) +impl From for Error { + fn from(e: ExecutionError) -> Self { + Self::Execution(e) } } diff --git a/contracts/transfer/tests/common/utils.rs b/contracts/transfer/tests/common/utils.rs index 39f0e98d0b..79393f502f 100644 --- a/contracts/transfer/tests/common/utils.rs +++ b/contracts/transfer/tests/common/utils.rs @@ -228,4 +228,5 @@ pub fn create_phoenix_transaction( gas_price, exec.map(Into::into), ) + .expect("creating the creation shouldn't fail") } From 20fc16767a4eb43a995218a43a812f141a67128b Mon Sep 17 00:00:00 2001 From: moana Date: Mon, 26 Aug 2024 13:18:37 +0200 Subject: [PATCH 12/15] stake-contract: Integrate `execution-core::Error` --- contracts/stake/tests/common/utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/stake/tests/common/utils.rs b/contracts/stake/tests/common/utils.rs index 63db9ced58..d77de40c84 100644 --- a/contracts/stake/tests/common/utils.rs +++ b/contracts/stake/tests/common/utils.rs @@ -193,5 +193,6 @@ pub fn create_transaction( gas_price, exec.map(Into::into), ) + .expect("creating the creation shouldn't fail") .into() } From 5231ea5b5feefc1c6716c8a685640dccd15e737f Mon Sep 17 00:00:00 2001 From: moana Date: Mon, 26 Aug 2024 13:27:07 +0200 Subject: [PATCH 13/15] wallet-core: Integrate `execution-core::Error` --- wallet-core/src/transaction.rs | 179 +++++++++++++++++---------------- 1 file changed, 94 insertions(+), 85 deletions(-) diff --git a/wallet-core/src/transaction.rs b/wallet-core/src/transaction.rs index f55f6f7288..bc21c64aae 100644 --- a/wallet-core/src/transaction.rs +++ b/wallet-core/src/transaction.rs @@ -7,7 +7,6 @@ //! Implementations of basic wallet functionalities to create transactions. use alloc::vec::Vec; -use core::fmt::Debug; use rand::{CryptoRng, RngCore}; @@ -28,46 +27,9 @@ use execution_core::{ withdraw::{Withdraw, WithdrawReceiver, WithdrawReplayToken}, Transaction, }, - BlsScalar, ContractId, JubJubScalar, + BlsScalar, ContractId, Error, JubJubScalar, }; -/// Create a [`Transaction`] that is paid in phoenix-notes. -#[allow(clippy::too_many_arguments)] -#[allow(clippy::module_name_repetitions)] -pub fn proven_phoenix_transaction( - rng: &mut R, - sender_sk: &PhoenixSecretKey, - change_pk: &PhoenixPublicKey, - receiver_pk: &PhoenixPublicKey, - inputs: Vec<(Note, Opening<(), NOTES_TREE_DEPTH>)>, - root: BlsScalar, - transfer_value: u64, - obfuscated_transaction: bool, - deposit: u64, - gas_limit: u64, - gas_price: u64, - exec: Option>, -) -> Transaction -where -

::Error: Debug, -{ - PhoenixTransaction::new::( - rng, - sender_sk, - change_pk, - receiver_pk, - inputs, - root, - transfer_value, - obfuscated_transaction, - deposit, - gas_limit, - gas_price, - exec, - ) - .into() -} - /// An unproven-transaction is nearly identical to a [`PhoenixTransaction`] with /// the only difference being that it carries a serialized [`TxCircuitVec`] /// instead of the proof bytes. @@ -78,9 +40,16 @@ where /// Once the proof is generated from the [`TxCircuitVec`] bytes, it can /// replace the serialized circuit in the transaction by calling /// [`Transaction::replace_proof`]. +/// +/// # Errors +/// The creation of a transaction is not possible and will error if: +/// - one of the input-notes doesn't belong to the `phoenix_sender_sk` +/// - the transaction input doesn't cover the transaction costs +/// - the `inputs` vector is either empty or larger than 4 elements +/// - the `inputs` vector contains duplicate `Note`s +/// - the `Prove` trait is implemented incorrectly #[allow(clippy::too_many_arguments)] -#[allow(clippy::module_name_repetitions)] -pub fn phoenix_transaction( +pub fn phoenix( rng: &mut R, sender_sk: &PhoenixSecretKey, change_pk: &PhoenixPublicKey, @@ -93,8 +62,8 @@ pub fn phoenix_transaction( gas_limit: u64, gas_price: u64, exec: Option>, -) -> PhoenixTransaction { - PhoenixTransaction::new::( +) -> Result { + Ok(PhoenixTransaction::new::( rng, sender_sk, change_pk, @@ -107,26 +76,22 @@ pub fn phoenix_transaction( gas_limit, gas_price, exec, - ) -} - -/// Implementation of the Prove trait that adds the serialized circuit instead -/// of a proof. This way the proof creation can be delegated to a 3rd party. -struct UnprovenProver(); - -impl Prove for UnprovenProver { - // this implementation of the trait will never error. - type Error = (); - - fn prove(circuit: &[u8]) -> Result, Self::Error> { - Ok(circuit.to_vec()) - } + )? + .into()) } /// Create a [`Transaction`] to stake from phoenix-notes. +/// +/// # Errors +/// The creation of a transaction is not possible and will error if: +/// - one of the input-notes doesn't belong to the `phoenix_sender_sk` +/// - the transaction input doesn't cover the transaction costs +/// - the `inputs` vector is either empty or larger than 4 elements +/// - the `inputs` vector contains duplicate `Note`s +/// - the `Prove` trait is implemented incorrectly #[allow(clippy::too_many_arguments)] #[allow(clippy::missing_panics_doc)] -pub fn phoenix_stake( +pub fn phoenix_stake( rng: &mut R, phoenix_sender_sk: &PhoenixSecretKey, stake_sk: &BlsSecretKey, @@ -136,7 +101,7 @@ pub fn phoenix_stake( gas_price: u64, stake_value: u64, current_nonce: u64, -) -> PhoenixTransaction { +) -> Result { let receiver_pk = PhoenixPublicKey::from(phoenix_sender_sk); let change_pk = receiver_pk; @@ -146,10 +111,9 @@ pub fn phoenix_stake( let stake = Stake::new(stake_sk, stake_value, current_nonce + 1); - let contract_call = ContractCall::new(STAKE_CONTRACT, "stake", &stake) - .expect("rkyv serialization of the stake struct should work."); + let contract_call = ContractCall::new(STAKE_CONTRACT, "stake", &stake)?; - phoenix_transaction( + phoenix::( rng, phoenix_sender_sk, &change_pk, @@ -167,9 +131,17 @@ pub fn phoenix_stake( /// Create an unproven [`Transaction`] to withdraw stake rewards into a /// phoenix-note. +/// +/// # Errors +/// The creation of a transaction is not possible and will error if: +/// - one of the input-notes doesn't belong to the `phoenix_sender_sk` +/// - the transaction input doesn't cover the transaction costs +/// - the `inputs` vector is either empty or larger than 4 elements +/// - the `inputs` vector contains duplicate `Note`s +/// - the `Prove` trait is implemented incorrectly #[allow(clippy::too_many_arguments)] #[allow(clippy::missing_panics_doc)] -pub fn phoenix_withdraw_stake_reward( +pub fn phoenix_stake_reward( rng: &mut R, phoenix_sender_sk: &PhoenixSecretKey, stake_sk: &BlsSecretKey, @@ -178,7 +150,7 @@ pub fn phoenix_withdraw_stake_reward( reward_amount: u64, gas_limit: u64, gas_price: u64, -) -> PhoenixTransaction { +) -> Result { let receiver_pk = PhoenixPublicKey::from(phoenix_sender_sk); let change_pk = receiver_pk; @@ -198,21 +170,15 @@ pub fn phoenix_withdraw_stake_reward( let gas_payment_token = WithdrawReplayToken::Phoenix(nullifiers); - let withdraw = withdraw_to_phoenix( + let contract_call = stake_reward_to_phoenix( rng, phoenix_sender_sk, - STAKE_CONTRACT, + stake_sk, gas_payment_token, reward_amount, - ); - - let reward_withdraw = StakeWithdraw::new(stake_sk, withdraw); - - let contract_call = - ContractCall::new(STAKE_CONTRACT, "withdraw", &reward_withdraw) - .expect("rkyv should serialize the reward_withdraw correctly"); + )?; - phoenix_transaction( + phoenix::( rng, phoenix_sender_sk, &change_pk, @@ -229,9 +195,17 @@ pub fn phoenix_withdraw_stake_reward( } /// Create an unproven [`Transaction`] to unstake into a phoenix-note. +/// +/// # Errors +/// The creation of a transaction is not possible and will error if: +/// - one of the input-notes doesn't belong to the `sender_sk` +/// - the transaction input doesn't cover the transaction costs +/// - the `inputs` vector is either empty or larger than 4 elements +/// - the `inputs` vector contains duplicate `Note`s +/// - the `Prove` trait is implemented incorrectly #[allow(clippy::too_many_arguments)] #[allow(clippy::missing_panics_doc)] -pub fn phoenix_unstake( +pub fn phoenix_unstake( rng: &mut R, phoenix_sender_sk: &PhoenixSecretKey, stake_sk: &BlsSecretKey, @@ -240,7 +214,7 @@ pub fn phoenix_unstake( unstake_value: u64, gas_limit: u64, gas_price: u64, -) -> PhoenixTransaction { +) -> Result { let receiver_pk = PhoenixPublicKey::from(phoenix_sender_sk); let change_pk = receiver_pk; @@ -260,20 +234,15 @@ pub fn phoenix_unstake( let gas_payment_token = WithdrawReplayToken::Phoenix(nullifiers); - let withdraw = withdraw_to_phoenix( + let contract_call = unstake_to_phoenix( rng, phoenix_sender_sk, - STAKE_CONTRACT, + stake_sk, gas_payment_token, unstake_value, - ); - - let unstake = StakeWithdraw::new(stake_sk, withdraw); + )?; - let contract_call = ContractCall::new(STAKE_CONTRACT, "unstake", &unstake) - .expect("unstake should serialize correctly"); - - phoenix_transaction( + phoenix::( rng, phoenix_sender_sk, &change_pk, @@ -289,6 +258,46 @@ pub fn phoenix_unstake( ) } +fn stake_reward_to_phoenix( + rng: &mut R, + phoenix_sender_sk: &PhoenixSecretKey, + stake_sk: &BlsSecretKey, + gas_payment_token: WithdrawReplayToken, + reward_amount: u64, +) -> Result { + let withdraw = withdraw_to_phoenix( + rng, + phoenix_sender_sk, + STAKE_CONTRACT, + gas_payment_token, + reward_amount, + ); + + let reward_withdraw = StakeWithdraw::new(stake_sk, withdraw); + + ContractCall::new(STAKE_CONTRACT, "withdraw", &reward_withdraw) +} + +fn unstake_to_phoenix( + rng: &mut R, + phoenix_sender_sk: &PhoenixSecretKey, + stake_sk: &BlsSecretKey, + gas_payment_token: WithdrawReplayToken, + unstake_value: u64, +) -> Result { + let withdraw = withdraw_to_phoenix( + rng, + phoenix_sender_sk, + STAKE_CONTRACT, + gas_payment_token, + unstake_value, + ); + + let unstake = StakeWithdraw::new(stake_sk, withdraw); + + ContractCall::new(STAKE_CONTRACT, "unstake", &unstake) +} + /// Create a [`Withdraw`] struct to be used to withdraw funds from a contract /// into a phoenix-note. /// From e4ab41da14d28ec9ec8899e331931e271eea12a7 Mon Sep 17 00:00:00 2001 From: moana Date: Mon, 26 Aug 2024 15:01:48 +0200 Subject: [PATCH 14/15] test-wallet: Integrate `execution-core::Error` --- test-wallet/Cargo.toml | 3 +- test-wallet/src/imp.rs | 162 ++++++++++++++++------------------------- test-wallet/src/lib.rs | 20 +---- 3 files changed, 65 insertions(+), 120 deletions(-) diff --git a/test-wallet/Cargo.toml b/test-wallet/Cargo.toml index f9b8b25eaf..f45395fd24 100644 --- a/test-wallet/Cargo.toml +++ b/test-wallet/Cargo.toml @@ -7,8 +7,6 @@ license = "MPL-2.0" [dependencies] rand_core = "^0.6" -rand_chacha = { version = "^0.3", default-features = false } -sha2 = { version = "^0.10", default-features = false } dusk-bytes = "^0.1" poseidon-merkle = { version = "0.7", features = ["rkyv-impl"] } rkyv = { version = "0.7", default-features = false } @@ -18,6 +16,7 @@ zeroize = { version = "1", default-features = false, features = ["derive"] } # rusk dependencies execution-core = { version = "0.1.0", path = "../execution-core" } wallet-core = { version = "0.1", path = "../wallet-core/" } +rusk-prover = { version = "0.3", path = "../rusk-prover/" } [dev-dependencies] rand = "^0.8" diff --git a/test-wallet/src/imp.rs b/test-wallet/src/imp.rs index 2ed5c01e64..6297bce503 100644 --- a/test-wallet/src/imp.rs +++ b/test-wallet/src/imp.rs @@ -4,7 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use crate::{ProverClient, StateClient, Store}; +use crate::{StateClient, Store}; use core::convert::Infallible; @@ -27,20 +27,20 @@ use execution_core::{ contract_exec::ContractExec, moonlight::{AccountData, Transaction as MoonlightTransaction}, phoenix::{ - Error as PhoenixError, Note, PublicKey as PhoenixPublicKey, - SecretKey as PhoenixSecretKey, ViewKey as PhoenixViewKey, - NOTES_TREE_DEPTH, + Note, PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey, + ViewKey as PhoenixViewKey, NOTES_TREE_DEPTH, }, Transaction, }, - BlsScalar, + BlsScalar, Error as ExecutionError, }; +use rusk_prover::LocalProver; use wallet_core::{ keys::{derive_bls_sk, derive_phoenix_sk}, phoenix_balance, transaction::{ - phoenix_stake, phoenix_transaction, phoenix_unstake, - phoenix_withdraw_stake_reward, + phoenix as phoenix_transaction, phoenix_stake, phoenix_stake_reward, + phoenix_unstake, }, BalanceInfo, }; @@ -56,13 +56,11 @@ type SerializerError = CompositeSerializerError< /// The error type returned by this crate. #[derive(Debug)] #[allow(clippy::large_enum_variant)] -pub enum Error { +pub enum Error { /// Underlying store error. Store(S::Error), /// Error originating from the state client. State(SC::Error), - /// Error originating from the prover client. - Prover(PC::Error), /// Rkyv serialization. Rkyv, /// Random number generator error. @@ -71,10 +69,8 @@ pub enum Error { Bytes(BytesError), /// Bytes were meant to be utf8 but aren't. Utf8(FromUtf8Error), - /// Originating from the phoenix transaction model. - Phoenix(PhoenixError), - /// Not enough balance to perform phoenix transaction. - NotEnoughBalance, + /// Originating from the execution-core error. + Execution(ExecutionError), /// Note combination for the given value is impossible given the maximum /// amount if inputs in a phoenix transaction. NoteCombinationProblem, @@ -104,7 +100,7 @@ pub enum Error { }, } -impl Error { +impl Error { /// Returns an error from the underlying store error. pub fn from_store_err(se: S::Error) -> Self { Self::Store(se) @@ -113,57 +109,43 @@ impl Error { pub fn from_state_err(se: SC::Error) -> Self { Self::State(se) } - /// Returns an error from the underlying prover client. - pub fn from_prover_err(pe: PC::Error) -> Self { - Self::Prover(pe) - } } -impl From - for Error -{ +impl From for Error { fn from(_: SerializerError) -> Self { Self::Rkyv } } -impl - From> for Error +impl From> + for Error { fn from(_: CheckDeserializeError) -> Self { Self::Rkyv } } -impl From - for Error -{ +impl From for Error { fn from(re: RngError) -> Self { Self::Rng(re) } } -impl From - for Error -{ +impl From for Error { fn from(be: BytesError) -> Self { Self::Bytes(be) } } -impl From - for Error -{ +impl From for Error { fn from(err: FromUtf8Error) -> Self { Self::Utf8(err) } } -impl From - for Error -{ - fn from(pe: PhoenixError) -> Self { - Self::Phoenix(pe) +impl From for Error { + fn from(ee: ExecutionError) -> Self { + Self::Execution(ee) } } @@ -171,20 +153,15 @@ impl From /// /// This is responsible for holding the keys, and performing operations like /// creating transactions. -pub struct Wallet { +pub struct Wallet { store: S, state: SC, - prover: PC, } -impl Wallet { +impl Wallet { /// Create a new wallet given the underlying store and node client. - pub const fn new(store: S, state: SC, prover: PC) -> Self { - Self { - store, - state, - prover, - } + pub const fn new(store: S, state: SC) -> Self { + Self { store, state } } /// Return the inner Store reference @@ -196,24 +173,18 @@ impl Wallet { pub const fn state(&self) -> &SC { &self.state } - - /// Return the inner Prover reference - pub const fn prover(&self) -> &PC { - &self.prover - } } -impl Wallet +impl Wallet where S: Store, SC: StateClient, - PC: ProverClient, { /// Retrieve the secret key with the given index. pub fn phoenix_secret_key( &self, index: u8, - ) -> Result> { + ) -> Result> { self.store .phoenix_secret_key(index) .map_err(Error::from_store_err) @@ -223,7 +194,7 @@ where pub fn phoenix_public_key( &self, index: u8, - ) -> Result> { + ) -> Result> { self.store .phoenix_public_key(index) .map_err(Error::from_store_err) @@ -233,7 +204,7 @@ where pub fn account_secret_key( &self, index: u8, - ) -> Result> { + ) -> Result> { self.store .account_secret_key(index) .map_err(Error::from_store_err) @@ -243,7 +214,7 @@ where pub fn account_public_key( &self, index: u8, - ) -> Result> { + ) -> Result> { self.store .account_public_key(index) .map_err(Error::from_store_err) @@ -254,7 +225,7 @@ where fn unspent_notes_and_nullifiers( &self, sk: &PhoenixSecretKey, - ) -> Result, Error> { + ) -> Result, Error> { let vk = PhoenixViewKey::from(sk); let notes: Vec = self @@ -291,7 +262,7 @@ where &self, sender_sk: &PhoenixSecretKey, transaction_cost: u64, - ) -> Result, Error> { + ) -> Result, Error> { let sender_vk = PhoenixViewKey::from(sender_sk); // decrypt the value of all unspent note @@ -302,13 +273,15 @@ where let mut accumulated_value = 0; for (note, nullifier) in unspent_notes_nullifiers { - let val = note.value(Some(&sender_vk))?; + let val = note + .value(Some(&sender_vk)) + .map_err(|_| ExecutionError::PhoenixOwnership)?; accumulated_value += val; notes_values_nullifiers.push((note, val, nullifier)); } if accumulated_value < transaction_cost { - return Err(Error::NotEnoughBalance); + return Err(ExecutionError::InsufficientBalance.into()); } // pick the four smallest notes that cover the costs @@ -330,7 +303,7 @@ where transaction_cost: u64, ) -> Result< Vec<(Note, Opening<(), NOTES_TREE_DEPTH>, BlsScalar)>, - Error, + Error, > { let notes_and_nullifiers = self.input_notes_nullifiers(sender_sk, transaction_cost)?; @@ -355,8 +328,7 @@ where &self, sender_sk: &PhoenixSecretKey, transaction_cost: u64, - ) -> Result)>, Error> - { + ) -> Result)>, Error> { let notes_and_nullifiers = self.input_notes_nullifiers(sender_sk, transaction_cost)?; @@ -383,7 +355,7 @@ where gas_price: u64, deposit: u64, exec: impl Into, - ) -> Result> { + ) -> Result> { let mut sender_sk = self.phoenix_secret_key(sender_index)?; let receiver_pk = self.phoenix_public_key(sender_index)?; let change_pk = receiver_pk; @@ -398,7 +370,7 @@ where let transfer_value = 0; let obfuscated_transaction = false; - let utx = phoenix_transaction( + let tx = phoenix_transaction::( rng, &sender_sk, &change_pk, @@ -411,12 +383,11 @@ where gas_limit, gas_price, Some(exec), - ); + )?; sender_sk.zeroize(); - PC::compute_proof_and_propagate(&utx) - .map_err(|e| Error::from_prover_err(e)) + Ok(tx) } /// Transfer Dusk in the form of Phoenix notes from one key to another. @@ -429,7 +400,7 @@ where transfer_value: u64, gas_limit: u64, gas_price: u64, - ) -> Result> { + ) -> Result> { let mut sender_sk = self.phoenix_secret_key(sender_index)?; let change_pk = self.phoenix_public_key(sender_index)?; @@ -445,7 +416,7 @@ where let exec: Option = None; - let utx = phoenix_transaction( + let tx = phoenix_transaction::( rng, &sender_sk, &change_pk, @@ -458,12 +429,11 @@ where gas_limit, gas_price, exec, - ); + )?; sender_sk.zeroize(); - PC::compute_proof_and_propagate(&utx) - .map_err(|e| Error::from_prover_err(e)) + Ok(tx) } /// Stakes an amount of Dusk using Phoenix notes. @@ -476,7 +446,7 @@ where stake_value: u64, gas_limit: u64, gas_price: u64, - ) -> Result> { + ) -> Result> { let mut phoenix_sender_sk = self.phoenix_secret_key(sender_index)?; let mut stake_sk = self.account_secret_key(staker_index)?; @@ -495,7 +465,7 @@ where .map_err(Error::from_state_err)? .nonce; - let utx = phoenix_stake( + let tx = phoenix_stake::( rng, &phoenix_sender_sk, &stake_sk, @@ -505,13 +475,12 @@ where gas_price, stake_value, current_nonce, - ); + )?; stake_sk.zeroize(); phoenix_sender_sk.zeroize(); - PC::compute_proof_and_propagate(&utx) - .map_err(|e| Error::from_prover_err(e)) + Ok(tx) } /// Unstakes a key from the stake contract, using Phoenix notes. @@ -522,7 +491,7 @@ where staker_index: u8, gas_limit: u64, gas_price: u64, - ) -> Result> { + ) -> Result> { let mut phoenix_sender_sk = self.phoenix_secret_key(sender_index)?; let mut stake_sk = self.account_secret_key(staker_index)?; @@ -548,7 +517,7 @@ where })? .value; - let utx = phoenix_unstake( + let tx = phoenix_unstake::( rng, &phoenix_sender_sk, &stake_sk, @@ -557,13 +526,12 @@ where staked_amount, gas_limit, gas_price, - ); + )?; stake_sk.zeroize(); phoenix_sender_sk.zeroize(); - PC::compute_proof_and_propagate(&utx) - .map_err(|e| Error::from_prover_err(e)) + Ok(tx) } /// Withdraw the accumulated staking reward for a key, into Phoenix notes. @@ -575,7 +543,7 @@ where staker_index: u8, gas_limit: u64, gas_price: u64, - ) -> Result> { + ) -> Result> { let mut phoenix_sender_sk = self.phoenix_secret_key(sender_index)?; let mut stake_sk = self.account_secret_key(staker_index)?; @@ -594,7 +562,7 @@ where .map_err(Error::from_state_err)? .reward; - let utx = phoenix_withdraw_stake_reward( + let tx = phoenix_stake_reward::( rng, &phoenix_sender_sk, &stake_sk, @@ -603,13 +571,12 @@ where stake_reward, gas_limit, gas_price, - ); + )?; stake_sk.zeroize(); phoenix_sender_sk.zeroize(); - PC::compute_proof_and_propagate(&utx) - .map_err(|e| Error::from_prover_err(e)) + Ok(tx) } /// Transfer Dusk from one account to another using moonlight. @@ -620,7 +587,7 @@ where value: u64, gas_limit: u64, gas_price: u64, - ) -> Result> { + ) -> Result> { let deposit = 0; let exec: Option = None; @@ -646,7 +613,7 @@ where gas_limit: u64, gas_price: u64, exec: Option>, - ) -> Result> { + ) -> Result> { let mut seed = self.store.get_seed().map_err(Error::from_store_err)?; let mut from_sk = derive_bls_sk(&seed, from_index); let from_account = BlsPublicKey::from(&from_sk); @@ -660,7 +627,7 @@ where // the network with transactions that are unspendable. let max_value = value + deposit + gas_limit * gas_price; if max_value > account.balance { - return Err(Error::NotEnoughBalance); + return Err(ExecutionError::InsufficientBalance.into()); } let nonce = account.nonce + 1; @@ -679,7 +646,7 @@ where pub fn get_balance( &self, sk_index: u8, - ) -> Result> { + ) -> Result> { let mut seed = self.store.get_seed().map_err(Error::from_store_err)?; let mut phoenix_sk = derive_phoenix_sk(&seed, sk_index); let phoenix_vk = PhoenixViewKey::from(&phoenix_sk); @@ -698,10 +665,7 @@ where } /// Gets the stake and the expiration of said stake for a key. - pub fn get_stake( - &self, - sk_index: u8, - ) -> Result> { + pub fn get_stake(&self, sk_index: u8) -> Result> { let mut seed = self.store.get_seed().map_err(Error::from_store_err)?; let mut account_sk = derive_bls_sk(&seed, sk_index); @@ -722,7 +686,7 @@ where pub fn get_account( &self, sk_index: u8, - ) -> Result> { + ) -> Result> { let mut seed = self.store.get_seed().map_err(Error::from_store_err)?; let mut account_sk = derive_bls_sk(&seed, sk_index); diff --git a/test-wallet/src/lib.rs b/test-wallet/src/lib.rs index 6a25b4c903..32d4fbe7bd 100644 --- a/test-wallet/src/lib.rs +++ b/test-wallet/src/lib.rs @@ -25,10 +25,8 @@ use execution_core::{ moonlight::AccountData, phoenix::{ Note, PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey, - Transaction as PhoenixTransaction, ViewKey as PhoenixViewKey, - NOTES_TREE_DEPTH, + ViewKey as PhoenixViewKey, NOTES_TREE_DEPTH, }, - Transaction, }, BlsScalar, }; @@ -39,22 +37,6 @@ pub use wallet_core::keys::{ pub use imp::*; -/// Types that are client of the prover. -pub trait ProverClient { - /// Error returned by the node client. - type Error; - - /// Requests that a node prove the given unproven [`PhoenixTransaction`] and - /// later propagates it. - /// - /// # Errors - /// This function will error if the proof can not be generated from the - /// unproven transaction. - fn compute_proof_and_propagate( - utx: &PhoenixTransaction, - ) -> Result; -} - /// Stores the cryptographic material necessary to derive cryptographic keys. pub trait Store { /// The error type returned from the store. From 24da333547c0c470c895dd4c66b959fff46dec5d Mon Sep 17 00:00:00 2001 From: moana Date: Mon, 26 Aug 2024 15:02:09 +0200 Subject: [PATCH 15/15] rusk: Integrate `execution-core::Error` --- rusk/src/lib/error.rs | 57 ++++++++++++++++------ rusk/src/lib/http/prover.rs | 5 +- rusk/tests/common/wallet.rs | 34 +------------ rusk/tests/rusk-state.rs | 18 +++---- rusk/tests/services/contract_deployment.rs | 7 ++- rusk/tests/services/gas_behavior.rs | 5 +- rusk/tests/services/multi_transfer.rs | 5 +- rusk/tests/services/stake.rs | 9 ++-- rusk/tests/services/transfer.rs | 5 +- rusk/tests/services/unspendable.rs | 5 +- 10 files changed, 66 insertions(+), 84 deletions(-) diff --git a/rusk/src/lib/error.rs b/rusk/src/lib/error.rs index 57777deda0..bc93586601 100644 --- a/rusk/src/lib/error.rs +++ b/rusk/src/lib/error.rs @@ -8,8 +8,8 @@ use std::{fmt, io}; use dusk_bytes::Serializable; use execution_core::{ - signatures::bls::PublicKey as BlsPublicKey, - transfer::phoenix::Error as PhoenixError, BlsScalar, Dusk, + signatures::bls::PublicKey as BlsPublicKey, transfer::phoenix::CoreError, + BlsScalar, Dusk, Error as ExecErr, }; use rusk_abi::PiecrustError; @@ -37,8 +37,10 @@ pub enum Error { OpeningNoteUndefined(u64), /// Bytes Serialization Errors Serialization(dusk_bytes::Error), + /// Originating from transaction-creation + Transaction(ExecErr), /// Originating from Phoenix. - Phoenix(PhoenixError), + Phoenix(CoreError), /// Piecrust VM internal Errors Vm(PiecrustError), /// IO Errors @@ -47,9 +49,6 @@ pub enum Error { CoinbaseBlockHeight(u64, u64), /// Bad dusk spent in coinbase (got, expected). CoinbaseDuskSpent(Dusk, Dusk), - /// Proof creation error - #[cfg(feature = "prover")] - ProofCreation(rusk_prover::ProverError), /// Failed to produce proper state #[cfg(feature = "node")] InconsistentState(dusk_consensus::operations::VerificationOutput), @@ -75,10 +74,39 @@ impl From for Error { } } -#[cfg(feature = "prover")] -impl From for Error { - fn from(err: rusk_prover::ProverError) -> Self { - Error::ProofCreation(err) +impl From for Error { + fn from(err: ExecErr) -> Self { + match err { + ExecErr::InsufficientBalance => { + Self::Transaction(ExecErr::InsufficientBalance) + } + ExecErr::Replay => Self::Transaction(ExecErr::Replay), + ExecErr::PhoenixOwnership => { + Self::Transaction(ExecErr::PhoenixOwnership) + } + ExecErr::PhoenixCircuit(e) => { + Self::Transaction(ExecErr::PhoenixCircuit(e)) + } + ExecErr::PhoenixProver(e) => { + Self::Transaction(ExecErr::PhoenixProver(e)) + } + ExecErr::InvalidData => { + Self::Serialization(dusk_bytes::Error::InvalidData) + } + ExecErr::BadLength(found, expected) => { + Self::Serialization(dusk_bytes::Error::BadLength { + found, + expected, + }) + } + ExecErr::InvalidChar(ch, index) => { + Self::Serialization(dusk_bytes::Error::InvalidChar { + ch, + index, + }) + } + ExecErr::Rkyv(e) => Self::Transaction(ExecErr::Rkyv(e)), + } } } @@ -88,8 +116,8 @@ impl From for Error { } } -impl From for Error { - fn from(pe: PhoenixError) -> Self { +impl From for Error { + fn from(pe: CoreError) -> Self { Self::Phoenix(pe) } } @@ -123,6 +151,7 @@ impl fmt::Display for Error { } Error::Vm(err) => write!(f, "VM Error: {err}"), Error::Io(err) => write!(f, "IO Error: {err}"), + Error::Transaction(err) => write!(f, "Transaction Error: {err}"), Error::Phoenix(err) => write!(f, "Phoenix error: {err}"), Error::Other(err) => write!(f, "Other error: {err}"), Error::CoinbaseBlockHeight(got, expected) => write!( @@ -145,10 +174,6 @@ impl fmt::Display for Error { Error::InvalidCircuitArguments(inputs_len, outputs_len) => { write!(f,"Expected: 0 < (inputs: {inputs_len}) < 5, 0 ≤ (outputs: {outputs_len}) < 3") } - #[cfg(feature = "prover")] - Error::ProofCreation(e) => { - write!(f, "Proof creation error: {e}") - } #[cfg(feature = "node")] Error::InconsistentState(verification_output) => { write!( diff --git a/rusk/src/lib/http/prover.rs b/rusk/src/lib/http/prover.rs index b24852cf00..936b624d74 100644 --- a/rusk/src/lib/http/prover.rs +++ b/rusk/src/lib/http/prover.rs @@ -4,6 +4,8 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +use anyhow::anyhow; + use execution_core::transfer::phoenix::Prove; use rusk_prover::LocalProver; @@ -21,7 +23,8 @@ impl HandleRequest for LocalProver { ) -> anyhow::Result { let topic = request.event.topic.as_str(); let response = match topic { - "prove_tx_circuit" => LocalProver::prove(request.event_data())?, + "prove_execute" => LocalProver::prove(request.event_data()) + .map_err(|e| anyhow!(e))?, _ => anyhow::bail!("Unsupported"), }; Ok(ResponseData::new(response)) diff --git a/rusk/tests/common/wallet.rs b/rusk/tests/common/wallet.rs index 4712d047aa..7b164edf88 100644 --- a/rusk/tests/common/wallet.rs +++ b/rusk/tests/common/wallet.rs @@ -16,18 +16,13 @@ use execution_core::{ stake::StakeData, transfer::{ moonlight::AccountData, - phoenix::{ - Note, Prove, Transaction as PhoenixTransaction, ViewKey, - NOTES_TREE_DEPTH, - }, - Transaction, + phoenix::{Note, ViewKey, NOTES_TREE_DEPTH}, }, BlsScalar, }; use futures::StreamExt; use poseidon_merkle::Opening as PoseidonOpening; use rusk::{Error, Result, Rusk}; -use rusk_prover::LocalProver; use test_wallet::{self as wallet, Store}; use tracing::info; @@ -128,33 +123,6 @@ impl wallet::StateClient for TestStateClient { } } -#[derive(Default)] -pub struct TestProverClient(); - -impl Debug for TestProverClient { - fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Ok(()) - } -} - -impl wallet::ProverClient for TestProverClient { - type Error = Error; - /// Requests that a node prove the given transaction and later propagates it - fn compute_proof_and_propagate( - utx: &PhoenixTransaction, - ) -> Result { - let circuit_bytes = &utx.proof()[..]; - let proof_bytes = LocalProver::prove(circuit_bytes)?; - info!("circuit: {}", hex::encode(circuit_bytes)); - let mut tx = utx.clone(); - tx.replace_proof(proof_bytes); - - //Propagate is not required yet - - Ok(tx.into()) - } -} - #[derive(Default, Debug, Clone)] pub struct DummyCacheItem { notes: Vec<(Note, u64)>, diff --git a/rusk/tests/rusk-state.rs b/rusk/tests/rusk-state.rs index c34b87238a..813ca14eb4 100644 --- a/rusk/tests/rusk-state.rs +++ b/rusk/tests/rusk-state.rs @@ -175,7 +175,7 @@ pub fn rusk_state_finalized() -> Result<()> { #[allow(dead_code)] // #[tokio::test(flavor = "multi_thread")] async fn generate_phoenix_txs() -> Result<(), Box> { - use common::wallet::{TestProverClient, TestStateClient, TestStore}; + use common::wallet::{TestStateClient, TestStore}; use std::io::Write; common::logger(); @@ -189,11 +189,8 @@ async fn generate_phoenix_txs() -> Result<(), Box> { let cache = Arc::new(std::sync::RwLock::new(std::collections::HashMap::new())); - let wallet = test_wallet::Wallet::new( - TestStore, - TestStateClient { rusk, cache }, - TestProverClient::default(), - ); + let wallet = + test_wallet::Wallet::new(TestStore, TestStateClient { rusk, cache }); const N_ADDRESSES: usize = 100; @@ -240,7 +237,7 @@ async fn generate_phoenix_txs() -> Result<(), Box> { #[allow(dead_code)] // #[tokio::test(flavor = "multi_thread")] async fn generate_moonlight_txs() -> Result<(), Box> { - use common::wallet::{TestProverClient, TestStateClient, TestStore}; + use common::wallet::{TestStateClient, TestStore}; use std::io::Write; common::logger(); @@ -254,11 +251,8 @@ async fn generate_moonlight_txs() -> Result<(), Box> { let cache = Arc::new(std::sync::RwLock::new(std::collections::HashMap::new())); - let wallet = test_wallet::Wallet::new( - TestStore, - TestStateClient { rusk, cache }, - TestProverClient::default(), - ); + let wallet = + test_wallet::Wallet::new(TestStore, TestStateClient { rusk, cache }); const N_ADDRESSES: usize = 100; diff --git a/rusk/tests/services/contract_deployment.rs b/rusk/tests/services/contract_deployment.rs index aa4269770d..1761dd6a54 100644 --- a/rusk/tests/services/contract_deployment.rs +++ b/rusk/tests/services/contract_deployment.rs @@ -25,7 +25,7 @@ use tracing::info; use crate::common::logger; use crate::common::state::{generator_procedure, ExecuteResult}; -use crate::common::wallet::{TestProverClient, TestStateClient, TestStore}; +use crate::common::wallet::{TestStateClient, TestStore}; const BLOCK_HEIGHT: u64 = 1; const BLOCK_GAS_LIMIT: u64 = 1_000_000_000_000; @@ -107,7 +107,7 @@ fn bytecode_hash(bytecode: impl AsRef<[u8]>) -> ContractId { fn make_and_execute_transaction_deploy( rusk: &Rusk, - wallet: &wallet::Wallet, + wallet: &wallet::Wallet, bytecode: impl AsRef<[u8]>, gas_limit: u64, init_value: u8, @@ -168,7 +168,7 @@ fn make_and_execute_transaction_deploy( struct Fixture { pub rusk: Rusk, - pub wallet: Wallet, + pub wallet: Wallet, pub bob_bytecode: Vec, pub contract_id: ContractId, pub path: PathBuf, @@ -189,7 +189,6 @@ impl Fixture { rusk: rusk.clone(), cache, }, - TestProverClient::default(), ); let original_root = rusk.state_root(); diff --git a/rusk/tests/services/gas_behavior.rs b/rusk/tests/services/gas_behavior.rs index c8fa0e7ad7..17d148c4c9 100644 --- a/rusk/tests/services/gas_behavior.rs +++ b/rusk/tests/services/gas_behavior.rs @@ -21,7 +21,7 @@ use tracing::info; use crate::common::logger; use crate::common::state::{generator_procedure, new_state}; -use crate::common::wallet::{TestProverClient, TestStateClient, TestStore}; +use crate::common::wallet::{TestStateClient, TestStore}; const BLOCK_HEIGHT: u64 = 1; const BLOCK_GAS_LIMIT: u64 = 1_000_000_000_000; @@ -45,7 +45,7 @@ const SENDER_INDEX_1: u8 = 1; fn make_transactions( rusk: &Rusk, - wallet: &wallet::Wallet, + wallet: &wallet::Wallet, ) { let initial_balance_0 = wallet .get_balance(SENDER_INDEX_0) @@ -149,7 +149,6 @@ pub async fn erroring_tx_charged_full() -> Result<()> { rusk: rusk.clone(), cache, }, - TestProverClient::default(), ); let original_root = rusk.state_root(); diff --git a/rusk/tests/services/multi_transfer.rs b/rusk/tests/services/multi_transfer.rs index 92d0aa5fe5..f3cf71cc56 100644 --- a/rusk/tests/services/multi_transfer.rs +++ b/rusk/tests/services/multi_transfer.rs @@ -17,7 +17,7 @@ use tracing::info; use crate::common::logger; use crate::common::state::{generator_procedure, new_state, ExecuteResult}; -use crate::common::wallet::{TestProverClient, TestStateClient, TestStore}; +use crate::common::wallet::{TestStateClient, TestStore}; const BLOCK_HEIGHT: u64 = 1; // This is purposefully chosen to be low to trigger the discarding of a @@ -39,7 +39,7 @@ fn initial_state>(dir: P) -> Result { /// to be included due to exceeding the block gas limit fn wallet_transfer( rusk: &Rusk, - wallet: &wallet::Wallet, + wallet: &wallet::Wallet, amount: u64, ) { // Generate a receiver pk @@ -192,7 +192,6 @@ pub async fn multi_transfer() -> Result<()> { rusk: rusk.clone(), cache, }, - TestProverClient::default(), ); let original_root = rusk.state_root(); diff --git a/rusk/tests/services/stake.rs b/rusk/tests/services/stake.rs index 9e6124575e..5033db1407 100644 --- a/rusk/tests/services/stake.rs +++ b/rusk/tests/services/stake.rs @@ -24,7 +24,7 @@ use test_wallet::{self as wallet}; use tracing::info; use crate::common::state::{generator_procedure, new_state}; -use crate::common::wallet::{TestProverClient, TestStateClient, TestStore}; +use crate::common::wallet::{TestStateClient, TestStore}; use crate::common::*; const BLOCK_HEIGHT: u64 = 1; @@ -54,7 +54,7 @@ fn slash_state>(dir: P) -> Result { /// stake and checking it is correctly withdrawn. fn wallet_stake( rusk: &Rusk, - wallet: &wallet::Wallet, + wallet: &wallet::Wallet, value: u64, ) { let mut rng = StdRng::seed_from_u64(0xdead); @@ -157,7 +157,6 @@ pub async fn stake() -> Result<()> { rusk: rusk.clone(), cache, }, - TestProverClient::default(), ); let original_root = rusk.state_root(); @@ -188,7 +187,7 @@ pub async fn stake() -> Result<()> { /// fails fn wallet_reward( rusk: &Rusk, - wallet: &wallet::Wallet, + wallet: &wallet::Wallet, ) { let mut rng = StdRng::seed_from_u64(0xdead); @@ -251,7 +250,6 @@ pub async fn reward() -> Result<()> { rusk: rusk.clone(), cache, }, - TestProverClient::default(), ); let original_root = rusk.state_root(); @@ -289,7 +287,6 @@ pub async fn slash() -> Result<()> { rusk: rusk.clone(), cache, }, - TestProverClient::default(), ); let original_root = rusk.state_root(); diff --git a/rusk/tests/services/transfer.rs b/rusk/tests/services/transfer.rs index f4978efcff..0b6ec460d0 100644 --- a/rusk/tests/services/transfer.rs +++ b/rusk/tests/services/transfer.rs @@ -18,7 +18,7 @@ use tracing::info; use crate::common::logger; use crate::common::state::{generator_procedure, new_state}; -use crate::common::wallet::{TestProverClient, TestStateClient, TestStore}; +use crate::common::wallet::{TestStateClient, TestStore}; const BLOCK_GAS_LIMIT: u64 = 100_000_000_000; const INITIAL_BALANCE: u64 = 10_000_000_000; @@ -37,7 +37,7 @@ fn initial_state>(dir: P) -> Result { /// successfully. fn wallet_transfer( rusk: &Rusk, - wallet: &wallet::Wallet, + wallet: &wallet::Wallet, amount: u64, block_height: u64, ) { @@ -145,7 +145,6 @@ pub async fn wallet() -> Result<()> { rusk: rusk.clone(), cache: cache.clone(), }, - TestProverClient::default(), ); let original_root = rusk.state_root(); diff --git a/rusk/tests/services/unspendable.rs b/rusk/tests/services/unspendable.rs index 67a565ed23..490d64e528 100644 --- a/rusk/tests/services/unspendable.rs +++ b/rusk/tests/services/unspendable.rs @@ -21,7 +21,7 @@ use tracing::info; use crate::common::logger; use crate::common::state::{generator_procedure, new_state, ExecuteResult}; -use crate::common::wallet::{TestProverClient, TestStateClient, TestStore}; +use crate::common::wallet::{TestStateClient, TestStore}; const BLOCK_HEIGHT: u64 = 1; const BLOCK_GAS_LIMIT: u64 = 1_000_000_000_000; @@ -47,7 +47,7 @@ const SENDER_INDEX_2: u8 = 2; fn make_transactions( rusk: &Rusk, - wallet: &wallet::Wallet, + wallet: &wallet::Wallet, ) { let initial_balance_0 = wallet .get_balance(SENDER_INDEX_0) @@ -179,7 +179,6 @@ pub async fn unspendable() -> Result<()> { rusk: rusk.clone(), cache, }, - TestProverClient::default(), ); let original_root = rusk.state_root();