diff --git a/src/consensus/state.rs b/src/consensus/state.rs index c8b6a990f..a371d687f 100644 --- a/src/consensus/state.rs +++ b/src/consensus/state.rs @@ -23,7 +23,10 @@ use crate::{ schnorr::{SchnorrPublic, SchnorrSecret}, }, net, - node::{Client, State}, + node::{ + state::{state_transition, StateUpdate}, + Client, MemoryState, State, + }, util::serial::{serialize, Encodable, SerialDecodable, SerialEncodable}, Result, }; @@ -807,4 +810,52 @@ impl ValidatorState { self.consensus = consensus; Ok(()) } + + // ========================== + // State transition functions + // ========================== + + /// Validate state transitions for given transactions and state and + /// return a vector of [`StateUpdate`] + pub fn validate_state_transitions(state: MemoryState, txs: &[Tx]) -> Result> { + let mut ret = vec![]; + let mut st = state.clone(); + + for (i, tx) in txs.iter().enumerate() { + let update = match state_transition(&st, tx.0.clone()) { + Ok(v) => v, + Err(e) => { + warn!("validate_state_transition(): Failed for tx {}: {}", i, e); + return Err(e.into()) + } + }; + st.apply(update.clone()); + ret.push(update); + } + + Ok(ret) + } + + /// Apply a vector of [`StateUpdate`] to the canonical state. + pub async fn update_canon_state( + &self, + updates: Vec, + notify: Option>, + ) -> Result<()> { + let secret_keys: Vec = + self.client.get_keypairs().await?.iter().map(|x| x.secret).collect(); + + debug!("update_canon_state(): Acquiring state machine lock"); + let mut state = self.state_machine.lock().await; + for update in updates { + state + .apply(update, secret_keys.clone(), notify.clone(), self.client.wallet.clone()) + .await?; + } + drop(state); + debug!("update_canon_state(): Dropped state machine lock"); + + debug!("update_canon_state(): Successfully applied state updates"); + Ok(()) + } } diff --git a/src/consensus/task/block_sync.rs b/src/consensus/task/block_sync.rs index b78cdf221..de1203972 100644 --- a/src/consensus/task/block_sync.rs +++ b/src/consensus/task/block_sync.rs @@ -1,11 +1,13 @@ use crate::{ consensus::{ block::{BlockOrder, BlockResponse}, - ValidatorStatePtr, + ValidatorState, ValidatorStatePtr, }, - net, Result, + net, + node::MemoryState, + Result, }; -use log::{info, warn}; +use log::{debug, info, warn}; /// async task used for block syncing. pub async fn block_sync_task(p2p: net::P2pPtr, state: ValidatorStatePtr) -> Result<()> { @@ -35,8 +37,30 @@ pub async fn block_sync_task(p2p: net::P2pPtr, state: ValidatorStatePtr) -> Resu let order = BlockOrder { sl: last.0, block: last.1 }; channel.send(order).await?; - // Node stores response data. Extra validations can be added here. + // Node stores response data. let resp = response_sub.receive().await?; + + // Verify state transitions for all blocks and their respective transactions. + debug!("block_sync_task(): Starting state transition validations"); + let mut canon_updates = vec![]; + let canon_state_clone = state.read().await.state_machine.lock().await.clone(); + let mut mem_state = MemoryState::new(canon_state_clone); + for block in &resp.blocks { + let mut state_updates = + ValidatorState::validate_state_transitions(mem_state.clone(), &block.txs)?; + + for update in &state_updates { + mem_state.apply(update.clone()); + } + + canon_updates.append(&mut state_updates); + } + debug!("block_sync_task(): All state transitions passed"); + + debug!("block_sync_task(): Updating canon state"); + state.write().await.update_canon_state(canon_updates, None).await?; + + debug!("block_sync_task(): Appending blocks to ledger"); state.write().await.blockchain.add(&resp.blocks)?; let last_received = state.read().await.blockchain.last()?.unwrap(); diff --git a/src/crypto/proof.rs b/src/crypto/proof.rs index 7c8488498..96e51c783 100644 --- a/src/crypto/proof.rs +++ b/src/crypto/proof.rs @@ -15,7 +15,7 @@ use crate::{ Result, }; -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct VerifyingKey { pub params: Params, pub vk: plonk::VerifyingKey, @@ -29,7 +29,7 @@ impl VerifyingKey { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ProvingKey { pub params: Params, pub pk: plonk::ProvingKey, diff --git a/src/node/client.rs b/src/node/client.rs index 7fbdadd59..56089dbf5 100644 --- a/src/node/client.rs +++ b/src/node/client.rs @@ -8,7 +8,7 @@ use crate::{ crypto::{ address::Address, coin::Coin, - keypair::{Keypair, PublicKey, SecretKey}, + keypair::{Keypair, PublicKey}, merkle_node::MerkleNode, proof::ProvingKey, types::DrkTokenId, @@ -196,31 +196,6 @@ impl Client { Err(ClientFailed::NotEnoughValue(amount)) } - // TODO: Should this function run on finalized blocks and iterate over its transactions? - async fn update_state( - secret_keys: Vec, - tx: Transaction, - state: Arc>, - wallet: WalletPtr, - notify: Option>, - ) -> Result<()> { - debug!("update_state(): Begin state update"); - debug!("update_state(): Acquiring state lock"); - let update; - { - let state = &*state.lock().await; - update = state_transition(state, tx)?; - } - - debug!("update_state(): Trying to apply the new state"); - let mut state = state.lock().await; - state.apply(update, secret_keys, notify, wallet).await?; - drop(state); - debug!("update_state(): Successfully updated state"); - - Ok(()) - } - pub async fn init_db(&self) -> Result<()> { self.wallet.init_db().await } diff --git a/src/node/memorystate.rs b/src/node/memorystate.rs index 52220c844..72427724c 100644 --- a/src/node/memorystate.rs +++ b/src/node/memorystate.rs @@ -7,6 +7,7 @@ use crate::crypto::{ }; /// In-memory state extension for state transition validations +#[derive(Clone)] pub struct MemoryState { /// Canonical state pub canon: State, @@ -45,7 +46,16 @@ impl ProgramState for MemoryState { } impl MemoryState { - pub async fn apply(&mut self, update: StateUpdate) { + pub fn new(canon_state: State) -> Self { + Self { + canon: canon_state.clone(), + tree: canon_state.tree, + merkle_roots: vec![], + nullifiers: vec![], + } + } + + pub fn apply(&mut self, update: StateUpdate) { debug!(target: "state_apply", "(in-memory) Extend nullifier set"); let mut nfs = update.nullifiers.clone(); self.nullifiers.append(&mut nfs); diff --git a/src/node/state.rs b/src/node/state.rs index be5f3e3dc..8c7c07482 100644 --- a/src/node/state.rs +++ b/src/node/state.rs @@ -37,6 +37,7 @@ pub trait ProgramState { /// A struct representing a state update. /// This gets applied on top of an existing state. +#[derive(Clone)] pub struct StateUpdate { /// All nullifiers in a transaction pub nullifiers: Vec, @@ -109,6 +110,7 @@ pub fn state_transition(state: &S, tx: Transaction) -> VerifyRe } /// Struct holding the state which we can apply a [`StateUpdate`] onto. +#[derive(Clone)] pub struct State { /// The entire Merkle tree state pub tree: BridgeTree,