From 50c58ab711f8b5b88f8378fc85a4d55f62b1684c Mon Sep 17 00:00:00 2001 From: Mathieu Baudet Date: Wed, 29 Sep 2021 17:20:49 -0700 Subject: [PATCH 1/7] [fastpay_core] Add support atomic-swaps on the server side --- fastpay/Cargo.toml | 1 + fastpay/src/network.rs | 18 +++ fastpay_core/Cargo.toml | 1 + fastpay_core/src/account.rs | 36 ++++- fastpay_core/src/authority.rs | 206 +++++++++++++++++++++++-- fastpay_core/src/client.rs | 5 +- fastpay_core/src/consensus.rs | 74 +++++++++ fastpay_core/src/error.rs | 17 ++ fastpay_core/src/generate_format.rs | 2 + fastpay_core/src/lib.rs | 1 + fastpay_core/src/messages.rs | 87 ++++++++++- fastpay_core/src/serialize.rs | 6 + fastpay_core/tests/staged/fastpay.yaml | 126 ++++++++++++++- 13 files changed, 549 insertions(+), 31 deletions(-) create mode 100644 fastpay_core/src/consensus.rs diff --git a/fastpay/Cargo.toml b/fastpay/Cargo.toml index f4e58a0..c94f82d 100644 --- a/fastpay/Cargo.toml +++ b/fastpay/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" bytes = "1.1.0" clap = "2.33.3" env_logger = "0.9.0" +either = "1.6.1" failure = "0.1.8" futures = "0.3.17" log = "0.4.14" diff --git a/fastpay/src/network.rs b/fastpay/src/network.rs index 89272e9..53be37a 100644 --- a/fastpay/src/network.rs +++ b/fastpay/src/network.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::transport::*; +use either::Either; use fastpay_core::{authority::*, base_types::*, client::*, error::*, messages::*, serialize::*}; #[cfg(feature = "benchmark")] @@ -216,6 +217,23 @@ impl MessageHandler for RunningServerState { Err(error) => Err(error), } } + SerializedMessage::ConsensusOrder(message) => { + match self.server.state.handle_consensus_order(*message) { + Ok(Either::Left(vote)) => { + // Response + Ok(Some(serialize_vote(&vote))) + } + Ok(Either::Right(continuations)) => { + // Cross-shard requests + for continuation in continuations { + self.handle_continuation(continuation).await; + } + // No response. (TODO: this is a bit rough) + Ok(None) + } + Err(error) => Err(error), + } + } SerializedMessage::InfoQuery(message) => self .server .state diff --git a/fastpay_core/Cargo.toml b/fastpay_core/Cargo.toml index afdcf37..8036e35 100644 --- a/fastpay_core/Cargo.toml +++ b/fastpay_core/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" bcs = "0.1.3" bincode = "1.3.3" coconut = { version = "0.1.0", path = "../coconut", features = ["with_serde"] } +either = "1.6.1" failure = "0.1.8" ff = "0.11.0" futures = "0.3.17" diff --git a/fastpay_core/src/account.rs b/fastpay_core/src/account.rs index 8ac1448..d473a02 100644 --- a/fastpay_core/src/account.rs +++ b/fastpay_core/src/account.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{base_types::*, error::FastPayError, messages::*}; -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; /// State of a FastPay account. #[derive(Debug, Default)] @@ -152,10 +152,34 @@ impl AccountState { ); Value::Confirm(request) } - Operation::CloseAccount | Operation::ChangeOwner { .. } => { + Operation::StartConsensusInstance { + new_id, accounts, .. + } => { + // Verify the new UID. + let expected_id = request.account_id.make_child(request.sequence_number); + fp_ensure!( + new_id == &expected_id, + FastPayError::InvalidNewAccountId(new_id.clone()) + ); + // Make sure accounts are unique. + let numbers = accounts + .clone() + .into_iter() + .collect::>(); + fp_ensure!( + numbers.len() == accounts.len(), + FastPayError::InvalidRequestOrder + ); + Value::Confirm(request) + } + Operation::Skip | Operation::CloseAccount | Operation::ChangeOwner { .. } => { // Nothing to check. Value::Confirm(request) } + Operation::LockInto { .. } => { + // Nothing to check. + Value::Lock(request) + } }; Ok(value) } @@ -171,7 +195,9 @@ impl AccountState { operation ); match operation { - Operation::OpenAccount { .. } => (), + Operation::OpenAccount { .. } + | Operation::StartConsensusInstance { .. } + | Operation::Skip => (), Operation::ChangeOwner { new_owner } => { self.owner = Some(*new_owner); } @@ -181,9 +207,9 @@ impl AccountState { Operation::Transfer { amount, .. } => { self.balance.try_sub_assign((*amount).into())?; } - Operation::Spend { .. } => { + Operation::Spend { .. } | Operation::LockInto { .. } => { // impossible under BFT assumptions. - unreachable!("Spend operation are never confirmed"); + unreachable!("Spend and lock operation are never confirmed"); } }; self.confirmed_log.push(certificate); diff --git a/fastpay_core/src/authority.rs b/fastpay_core/src/authority.rs index 3857927..4eff801 100644 --- a/fastpay_core/src/authority.rs +++ b/fastpay_core/src/authority.rs @@ -2,10 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - account::AccountState, base_types::*, committee::Committee, error::FastPayError, messages::*, + account::AccountState, base_types::*, committee::Committee, consensus::ConsensusState, + error::FastPayError, messages::*, }; use bls12_381::Scalar; use std::collections::{BTreeMap, BTreeSet, HashSet}; +use either::Either; #[cfg(test)] #[path = "unit_tests/authority_tests.rs"] @@ -23,6 +25,8 @@ pub struct AuthorityState { pub coconut_key_pair: Option, /// States of FastPay accounts. pub accounts: BTreeMap, + /// States of consensus instances. + pub instances: BTreeMap, /// The latest transaction index of the blockchain that the authority has seen. pub last_transaction_index: SequenceNumber, /// The sharding ID of this authority shard. 0 if one shard. @@ -63,6 +67,12 @@ pub trait Authority { order: CoinCreationOrder, ) -> Result<(CoinCreationResponse, Vec), FastPayError>; + /// Process a message meant for a consensus instance. + fn handle_consensus_order( + &mut self, + order: ConsensusOrder, + ) -> Result>, FastPayError>; + /// Force synchronization to finalize requests from Primary to FastPay. fn handle_primary_synchronization_order( &mut self, @@ -152,14 +162,22 @@ impl AuthorityState { operation: Operation, certificate: Certificate, ) -> Result<(), FastPayError> { - let recipient = operation - .recipient() - .ok_or(FastPayError::InvalidCrossShardRequest)?; - // Verify sharding. - fp_ensure!(self.in_shard(recipient), FastPayError::WrongShard); - // Execute the recipient's side of the operation. - let account = self.accounts.entry(recipient.clone()).or_default(); - account.apply_operation_as_recipient(&operation, certificate)?; + if let Some(recipient) = operation.recipient() { + fp_ensure!(self.in_shard(recipient), FastPayError::WrongShard); + // Execute the recipient's side of the operation. + let account = self.accounts.entry(recipient.clone()).or_default(); + account.apply_operation_as_recipient(&operation, certificate)?; + } else if let Operation::StartConsensusInstance { + new_id, + accounts, + functionality, + } = &operation + { + fp_ensure!(self.in_shard(new_id), FastPayError::WrongShard); + assert!(!self.instances.contains_key(new_id)); // guaranteed under BFT assumptions. + let instance = ConsensusState::new(*functionality, accounts.clone()); + self.instances.insert(new_id.clone(), instance); + } // This concludes the confirmation of `certificate`. Ok(()) } @@ -428,6 +446,167 @@ impl Authority for AuthorityState { Ok((response, continuations)) } + /// Process a message meant for a consensus instance. + fn handle_consensus_order( + &mut self, + order: ConsensusOrder, + ) -> Result>, FastPayError> { + match order { + ConsensusOrder::Propose { + proposal, + owner, + signature, + locks, + } => { + let instance = self + .instances + .get_mut(&proposal.instance_id) + .ok_or_else(|| { + FastPayError::UnknownConsensusInstance(proposal.instance_id.clone()) + })?; + // Process lock certificates. + for lock in locks { + lock.check(&self.committee)?; + match lock.value { + Value::Lock(Request { + account_id, + operation: Operation::LockInto { instance_id, owner }, + sequence_number, + }) if instance_id == proposal.instance_id + && Some(&sequence_number) + == instance.sequence_numbers.get(&account_id) => + { + // Update locking status for `account_id`. + instance.locked_accounts.insert(account_id, owner); + instance.participants.insert(owner); + } + _ => fp_bail!(FastPayError::InvalidConsensusOrder), + } + } + // Verify the signature and that the author of the signature is authorized. + fp_ensure!( + instance.participants.contains(&owner), + FastPayError::InvalidConsensusOrder + ); + signature.check(&proposal, owner)?; + // Check validity of the proposal and obtain the corresponding requests. + let requests = instance.make_requests(proposal.decision)?; + // TODO: verify that `proposal.round` is "available". + // Verify safety. + if let Some(proposed) = &instance.proposed { + fp_ensure!( + (proposed.round == proposal.round + && proposed.decision == proposal.decision) + || proposed.round < proposal.round, + FastPayError::UnsafeConsensusProposal + ); + } + if let Some(locked) = &instance.locked { + fp_ensure!( + locked.round < proposal.round && locked.decision == proposal.decision, + FastPayError::UnsafeConsensusProposal + ); + } + // Update proposed decision. + instance.proposed = Some(proposal.clone()); + // Vote in favor of pre-commit (aka lock). + let value = Value::PreCommit { proposal, requests }; + let vote = Vote::new(value, &self.key_pair); + Ok(Either::Left(vote)) + } + ConsensusOrder::HandlePreCommit { certificate } => { + certificate.check(&self.committee)?; + let (proposal, requests) = match certificate.value { + Value::PreCommit { proposal, requests } => (proposal, requests), + _ => fp_bail!(FastPayError::InvalidConsensusOrder), + }; + let instance = self + .instances + .get_mut(&proposal.instance_id) + .ok_or_else(|| { + FastPayError::UnknownConsensusInstance(proposal.instance_id.clone()) + })?; + // Verify safety. + if let Some(proposed) = &instance.proposed { + fp_ensure!( + proposed.round <= proposal.round, + FastPayError::UnsafeConsensusPreCommit + ); + } + if let Some(locked) = &instance.locked { + fp_ensure!( + locked.round <= proposal.round, + FastPayError::UnsafeConsensusPreCommit + ); + } + // Update locked decision. + instance.locked = Some(proposal.clone()); + // Vote in favor of commit. + let value = Value::Commit { proposal, requests }; + let vote = Vote::new(value, &self.key_pair); + Ok(Either::Left(vote)) + } + ConsensusOrder::HandleCommit { certificate, locks } => { + certificate.check(&self.committee)?; + let (proposal, requests) = match &certificate.value { + Value::Commit { proposal, requests } => (proposal, requests), + _ => fp_bail!(FastPayError::InvalidConsensusOrder), + }; + // Success. + // Only execute the requests in the commit once. + let mut requests = { + if self.instances.contains_key(&proposal.instance_id) { + requests.clone() + } else { + Vec::new() + } + }; + // Process lock certificates to add skip requests if needed. + if let ConsensusDecision::Abort = &proposal.decision { + for lock in locks { + lock.check(&self.committee)?; + match lock.value { + Value::Lock(Request { + account_id, + operation: + Operation::LockInto { + instance_id, + owner: _, + }, + sequence_number, + }) if instance_id == proposal.instance_id => { + requests.push(Request { + account_id: account_id.clone(), + operation: Operation::Skip, + sequence_number, + }); + } + _ => fp_bail!(FastPayError::InvalidConsensusOrder), + } + } + } + // Remove the consensus instance if needed. + self.instances.remove(&proposal.instance_id); + // Prepate cross-requests. + let continuations = requests + .iter() + .map(|request| { + let shard_id = self.which_shard(&request.account_id); + CrossShardContinuation::Request { + shard_id, + request: Box::new(CrossShardRequest::ProcessConfirmedRequest { + request: request.clone(), + certificate: certificate.clone(), + }), + } + }) + .collect(); + Ok(Either::Right(continuations)) + } + } + } + + /// Finalize a request from Primary. fn handle_primary_synchronization_order( &mut self, order: PrimarySynchronizationOrder, @@ -470,6 +649,13 @@ impl Authority for AuthorityState { self.accounts.remove(&account_id); Ok(()) } + CrossShardRequest::ProcessConfirmedRequest { + request, + certificate, + } => { + self.process_confirmed_request(request, certificate)?; // TODO: process continuations + Ok(()) + } } } @@ -507,6 +693,7 @@ impl AuthorityState { key_pair, coconut_key_pair, accounts: BTreeMap::new(), + instances: BTreeMap::new(), last_transaction_index: SequenceNumber::new(), shard_id: 0, number_of_shards: 1, @@ -526,6 +713,7 @@ impl AuthorityState { key_pair, coconut_key_pair, accounts: BTreeMap::new(), + instances: BTreeMap::new(), last_transaction_index: SequenceNumber::new(), shard_id, number_of_shards, diff --git a/fastpay_core/src/client.rs b/fastpay_core/src/client.rs index 67e6528..e1879c0 100644 --- a/fastpay_core/src/client.rs +++ b/fastpay_core/src/client.rs @@ -723,7 +723,10 @@ where | Operation::SpendAndTransfer { .. } => { self.key_pair = None; } - Operation::OpenAccount { .. } => (), + Operation::OpenAccount { .. } + | Operation::Skip + | Operation::LockInto { .. } + | Operation::StartConsensusInstance { .. } => (), } // Record certificate. self.sent_certificates.push(certificate); diff --git a/fastpay_core/src/consensus.rs b/fastpay_core/src/consensus.rs new file mode 100644 index 0000000..c8ee4e9 --- /dev/null +++ b/fastpay_core/src/consensus.rs @@ -0,0 +1,74 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{base_types::*, error::FastPayError, messages::*}; +use std::collections::{BTreeMap, BTreeSet}; + +/// State of a one-shot consensus instance. +#[derive(Debug)] +#[cfg_attr(test, derive(Eq, PartialEq))] +pub struct ConsensusState { + /// Functionality realized by this instance. + pub functionality: Functionality, + /// Accounts expected to be locked and managed by the protocol. + pub accounts: Vec, + /// Expected sequence number for each locked account. + pub sequence_numbers: BTreeMap, + /// Accounts locked so far and for which controlling key. + pub locked_accounts: BTreeMap, + /// Authorized participants. + pub participants: BTreeSet, + /// Pending proposal. + pub proposed: Option, + /// Pending locked (i.e. pre-committed) proposal. + pub locked: Option, +} + +impl ConsensusState { + pub fn new(functionality: Functionality, expected: Vec<(AccountId, SequenceNumber)>) -> Self { + let accounts: Vec<_> = expected.iter().map(|(id, _)| id.clone()).collect(); + let sequence_numbers: BTreeMap<_, _> = expected.into_iter().collect(); + assert_eq!(accounts.len(), sequence_numbers.len()); + Self { + functionality, + accounts, + sequence_numbers, + locked_accounts: BTreeMap::new(), + participants: BTreeSet::new(), + proposed: None, + locked: None, + } + } + + pub(crate) fn make_requests( + &self, + decision: ConsensusDecision, + ) -> Result, FastPayError> { + match (self.functionality, decision) { + (_, ConsensusDecision::Abort) => Ok(Vec::new()), + (Functionality::AtomicSwap, ConsensusDecision::Confirm) => { + let num_accounts = self.accounts.len(); + for id in &self.accounts { + fp_ensure!( + self.locked_accounts.contains_key(id), + FastPayError::MissingConsensusLock { + account_id: id.clone() + } + ); + } + let mut requests = Vec::new(); + for (i, id) in self.accounts.iter().enumerate() { + let sequence_number = *self.sequence_numbers.get(id).unwrap(); + let next_id = &self.accounts[(i + 1) % num_accounts]; + let new_owner = *self.locked_accounts.get(next_id).unwrap(); + requests.push(Request { + account_id: id.clone(), + operation: Operation::ChangeOwner { new_owner }, + sequence_number, + }); + } + Ok(requests) + } + } + } +} diff --git a/fastpay_core/src/error.rs b/fastpay_core/src/error.rs index e228255..077dd00 100644 --- a/fastpay_core/src/error.rs +++ b/fastpay_core/src/error.rs @@ -122,4 +122,21 @@ pub enum FastPayError { UnexpectedMessage, #[fail(display = "Network error while querying service: {:?}.", error)] ClientIoError { error: String }, + + // Consensus + #[fail(display = "Unknown consensus instance {:?}", 0)] + UnknownConsensusInstance(AccountId), + #[fail(display = "Invalid consensus order.")] + InvalidConsensusOrder, + #[fail(display = "Unsafe consensus proposal.")] + UnsafeConsensusProposal, + #[fail( + display = "The following account has not been locked yet: {}", + account_id + )] + MissingConsensusLock { account_id: AccountId }, + #[fail(display = "Invalid consensus proposal.")] + InvalidConsensusProposal, + #[fail(display = "Unsafe consensus pre-commit.")] + UnsafeConsensusPreCommit, } diff --git a/fastpay_core/src/generate_format.rs b/fastpay_core/src/generate_format.rs index 10f5892..3765961 100644 --- a/fastpay_core/src/generate_format.rs +++ b/fastpay_core/src/generate_format.rs @@ -50,6 +50,8 @@ fn get_registry() -> Result { tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; diff --git a/fastpay_core/src/lib.rs b/fastpay_core/src/lib.rs index 3de2f4f..40b34ba 100644 --- a/fastpay_core/src/lib.rs +++ b/fastpay_core/src/lib.rs @@ -9,6 +9,7 @@ pub mod authority; pub mod base_types; pub mod client; pub mod committee; +pub mod consensus; pub mod downloader; pub mod fastpay_smart_contract; pub mod messages; diff --git a/fastpay_core/src/messages.rs b/fastpay_core/src/messages.rs index 118c5e5..9c029f2 100644 --- a/fastpay_core/src/messages.rs +++ b/fastpay_core/src/messages.rs @@ -39,6 +39,8 @@ pub enum Operation { new_id: AccountId, new_owner: AccountOwner, }, + /// Do nothing. (This can be used for testing or to unlock accounts after a consensus decisions.) + Skip, /// Close the account. CloseAccount, /// Change the authentication key of the account. @@ -56,6 +58,23 @@ pub enum Operation { amount: Amount, user_data: UserData, }, + /// Start a consensus protocol `new_id` to manage the given `accounts`. + StartConsensusInstance { + new_id: AccountId, + functionality: Functionality, + accounts: Vec<(AccountId, SequenceNumber)>, + }, + /// Lock the account into the given consensus instance, managed by the given key. + LockInto { + instance_id: AccountId, + owner: AccountOwner, + }, +} + +/// A one-shot funtionality implemented in a consensus instance. +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] +pub enum Functionality { + AtomicSwap, } /// A request containing an account operation. @@ -118,13 +137,42 @@ pub struct TransparentCoin { pub seed: u128, } +/// Functionality-dependent consensus decision. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)] +pub enum ConsensusDecision { + Abort, + Confirm, +} + +/// The proposal of a particular decision during consensus. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct ConsensusProposal { + pub instance_id: AccountId, + pub round: SequenceNumber, + pub decision: ConsensusDecision, +} + /// A statement to be certified by the authorities. // TODO: decide if we split Vote & Certificate in one type per kind of value. #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] pub enum Value { + // -- Account management -- + /// The request was validated but confirmation will require additional steps. Lock(Request), + /// The request is ready to be confirmed (i.e. executed). Confirm(Request), + /// This coin is ready to be spent. Coin(TransparentCoin), + /// The proposal was validated but confirmation will require additional steps. + PreCommit { + proposal: ConsensusProposal, + requests: Vec, + }, + /// The proposal is ready to be committed, thus confirming a number of requests. + Commit { + proposal: ConsensusProposal, + requests: Vec, + }, } /// The balance of an account plus linked coins to be used in a coin creation description. @@ -176,6 +224,25 @@ pub struct CoinCreationResponse { pub tracking_id: AccountId, } +/// Same as RequestOrder but meant for a consensus instance. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(test, derive(Eq, PartialEq))] +pub enum ConsensusOrder { + Propose { + proposal: ConsensusProposal, + owner: AccountOwner, + signature: Signature, + locks: Vec, + }, + HandlePreCommit { + certificate: Certificate, + }, + HandleCommit { + certificate: Certificate, + locks: Vec, + }, +} + /// A vote on a statement from an authority. #[derive(Clone, Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(Eq, PartialEq))] @@ -228,8 +295,16 @@ pub struct AccountInfoResponse { #[derive(Clone, Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(Eq, PartialEq))] pub enum CrossShardRequest { - UpdateRecipient { certificate: Certificate }, - DestroyAccount { account_id: AccountId }, + UpdateRecipient { + certificate: Certificate, + }, + DestroyAccount { + account_id: AccountId, + }, + ProcessConfirmedRequest { + request: Request, + certificate: Certificate, + }, } #[cfg(test)] @@ -365,12 +440,7 @@ impl Operation { .. } | OpenAccount { new_id: id, .. } => Some(id), - - Operation::Spend { .. } - | Operation::SpendAndTransfer { .. } - | Operation::CloseAccount - | Transfer { .. } - | ChangeOwner { .. } => None, + _ => None, } } @@ -600,3 +670,4 @@ impl BcsSignable for RequestValue {} impl BcsSignable for Value {} impl BcsSignable for CoconutKey {} impl BcsSignable for CoinCreationDescription {} +impl BcsSignable for ConsensusProposal {} diff --git a/fastpay_core/src/serialize.rs b/fastpay_core/src/serialize.rs index c99fee9..0f31f28 100644 --- a/fastpay_core/src/serialize.rs +++ b/fastpay_core/src/serialize.rs @@ -17,6 +17,7 @@ pub enum SerializedMessage { RequestOrder(Box), ConfirmationOrder(Box), CoinCreationOrder(Box), + ConsensusOrder(Box), InfoQuery(Box), // Outbound Vote(Box), @@ -36,6 +37,7 @@ enum ShallowSerializedMessage<'a> { RequestOrder(&'a RequestOrder), ConfirmationOrder(&'a ConfirmationOrder), CoinCreationOrder(&'a CoinCreationOrder), + ConsensusOrder(&'a ConsensusOrder), InfoQuery(&'a AccountInfoQuery), // Outbound Vote(&'a Vote), @@ -120,6 +122,10 @@ pub fn serialize_coin_creation_response(value: &CoinCreationResponse) -> Vec serialize(&ShallowSerializedMessage::CoinCreationResponse(value)) } +pub fn serialize_consensus_order(value: &ConsensusOrder) -> Vec { + serialize(&ShallowSerializedMessage::ConsensusOrder(value)) +} + pub fn serialize_vote(value: &Vote) -> Vec { serialize(&ShallowSerializedMessage::Vote(value)) } diff --git a/fastpay_core/tests/staged/fastpay.yaml b/fastpay_core/tests/staged/fastpay.yaml index 8530c4d..c43cfed 100644 --- a/fastpay_core/tests/staged/fastpay.yaml +++ b/fastpay_core/tests/staged/fastpay.yaml @@ -152,6 +152,47 @@ ConfirmationOrder: STRUCT: - certificate: TYPENAME: Certificate +ConsensusDecision: + ENUM: + 0: + Abort: UNIT + 1: + Confirm: UNIT +ConsensusOrder: + ENUM: + 0: + Propose: + STRUCT: + - proposal: + TYPENAME: ConsensusProposal + - owner: + TYPENAME: PublicKeyBytes + - signature: + TYPENAME: Signature + - locks: + SEQ: + TYPENAME: Certificate + 1: + HandlePreCommit: + STRUCT: + - certificate: + TYPENAME: Certificate + 2: + HandleCommit: + STRUCT: + - certificate: + TYPENAME: Certificate + - locks: + SEQ: + TYPENAME: Certificate +ConsensusProposal: + STRUCT: + - instance_id: + TYPENAME: AccountId + - round: + TYPENAME: SequenceNumber + - decision: + TYPENAME: ConsensusDecision CrossShardRequest: ENUM: 0: @@ -164,6 +205,13 @@ CrossShardRequest: STRUCT: - account_id: TYPENAME: AccountId + 2: + ProcessConfirmedRequest: + STRUCT: + - request: + TYPENAME: Request + - certificate: + TYPENAME: Certificate FastPayError: ENUM: 0: @@ -252,6 +300,27 @@ FastPayError: ClientIoError: STRUCT: - error: STR + 34: + UnknownConsensusInstance: + NEWTYPE: + TYPENAME: AccountId + 35: + InvalidConsensusOrder: UNIT + 36: + UnsafeConsensusProposal: UNIT + 37: + MissingConsensusLock: + STRUCT: + - account_id: + TYPENAME: AccountId + 38: + InvalidConsensusProposal: UNIT + 39: + UnsafeConsensusPreCommit: UNIT +Functionality: + ENUM: + 0: + AtomicSwap: UNIT G1: TUPLESTRUCT: - U8 @@ -440,20 +509,22 @@ Operation: - new_owner: TYPENAME: PublicKeyBytes 2: - CloseAccount: UNIT + Skip: UNIT 3: + CloseAccount: UNIT + 4: ChangeOwner: STRUCT: - new_owner: TYPENAME: PublicKeyBytes - 4: + 5: Spend: STRUCT: - account_balance: TYPENAME: Amount - description_hash: TYPENAME: HashValue - 5: + 6: SpendAndTransfer: STRUCT: - recipient: @@ -462,6 +533,25 @@ Operation: TYPENAME: Amount - user_data: TYPENAME: UserData + 7: + StartConsensusInstance: + STRUCT: + - new_id: + TYPENAME: AccountId + - functionality: + TYPENAME: Functionality + - accounts: + SEQ: + TUPLE: + - TYPENAME: AccountId + - TYPENAME: SequenceNumber + 8: + LockInto: + STRUCT: + - instance_id: + TYPENAME: AccountId + - owner: + TYPENAME: PublicKeyBytes OutputAttribute: STRUCT: - seed: @@ -589,26 +679,30 @@ SerializedMessage: NEWTYPE: TYPENAME: CoinCreationOrder 3: + ConsensusOrder: + NEWTYPE: + TYPENAME: ConsensusOrder + 4: InfoQuery: NEWTYPE: TYPENAME: AccountInfoQuery - 4: + 5: Vote: NEWTYPE: TYPENAME: Vote - 5: + 6: InfoResponse: NEWTYPE: TYPENAME: AccountInfoResponse - 6: + 7: CoinCreationResponse: NEWTYPE: TYPENAME: CoinCreationResponse - 7: + 8: Error: NEWTYPE: TYPENAME: FastPayError - 8: + 9: CrossShardRequest: NEWTYPE: TYPENAME: CrossShardRequest @@ -644,6 +738,22 @@ Value: Coin: NEWTYPE: TYPENAME: TransparentCoin + 3: + PreCommit: + STRUCT: + - proposal: + TYPENAME: ConsensusProposal + - requests: + SEQ: + TYPENAME: Request + 4: + Commit: + STRUCT: + - proposal: + TYPENAME: ConsensusProposal + - requests: + SEQ: + TYPENAME: Request Vote: STRUCT: - value: From 5c5ffe2d6f3dd8807d180ae0f03e1f7b656aab24 Mon Sep 17 00:00:00 2001 From: Mathieu Baudet Date: Sun, 7 Nov 2021 21:26:41 -0800 Subject: [PATCH 2/7] [consensus][nit] fix typo --- fastpay_core/src/authority.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastpay_core/src/authority.rs b/fastpay_core/src/authority.rs index 4eff801..4448375 100644 --- a/fastpay_core/src/authority.rs +++ b/fastpay_core/src/authority.rs @@ -587,7 +587,7 @@ impl Authority for AuthorityState { } // Remove the consensus instance if needed. self.instances.remove(&proposal.instance_id); - // Prepate cross-requests. + // Prepare cross-shard requests. let continuations = requests .iter() .map(|request| { From a3f10ef9e9433c44f2e02ec7368ed7487e6980cb Mon Sep 17 00:00:00 2001 From: Mathieu Baudet Date: Mon, 15 Nov 2021 15:37:55 -0800 Subject: [PATCH 3/7] [consensus] keep necessary information for liveness --- fastpay_core/src/authority.rs | 18 +++++++++++++----- fastpay_core/src/consensus.rs | 13 ++++++++++--- fastpay_core/src/messages.rs | 7 +++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/fastpay_core/src/authority.rs b/fastpay_core/src/authority.rs index 4448375..46b8417 100644 --- a/fastpay_core/src/authority.rs +++ b/fastpay_core/src/authority.rs @@ -175,7 +175,7 @@ impl AuthorityState { { fp_ensure!(self.in_shard(new_id), FastPayError::WrongShard); assert!(!self.instances.contains_key(new_id)); // guaranteed under BFT assumptions. - let instance = ConsensusState::new(*functionality, accounts.clone()); + let instance = ConsensusState::new(*functionality, accounts.clone(), certificate); self.instances.insert(new_id.clone(), instance); } // This concludes the confirmation of `certificate`. @@ -501,7 +501,11 @@ impl Authority for AuthorityState { FastPayError::UnsafeConsensusProposal ); } - if let Some(locked) = &instance.locked { + if let Some(locked) = instance + .locked + .as_ref() + .and_then(|cert| cert.value.pre_commit_proposal()) + { fp_ensure!( locked.round < proposal.round && locked.decision == proposal.decision, FastPayError::UnsafeConsensusProposal @@ -516,7 +520,7 @@ impl Authority for AuthorityState { } ConsensusOrder::HandlePreCommit { certificate } => { certificate.check(&self.committee)?; - let (proposal, requests) = match certificate.value { + let (proposal, requests) = match certificate.value.clone() { Value::PreCommit { proposal, requests } => (proposal, requests), _ => fp_bail!(FastPayError::InvalidConsensusOrder), }; @@ -533,14 +537,18 @@ impl Authority for AuthorityState { FastPayError::UnsafeConsensusPreCommit ); } - if let Some(locked) = &instance.locked { + if let Some(locked) = instance + .locked + .as_ref() + .and_then(|cert| cert.value.pre_commit_proposal()) + { fp_ensure!( locked.round <= proposal.round, FastPayError::UnsafeConsensusPreCommit ); } // Update locked decision. - instance.locked = Some(proposal.clone()); + instance.locked = Some(certificate); // Vote in favor of commit. let value = Value::Commit { proposal, requests }; let vote = Vote::new(value, &self.key_pair); diff --git a/fastpay_core/src/consensus.rs b/fastpay_core/src/consensus.rs index c8ee4e9..215d1dc 100644 --- a/fastpay_core/src/consensus.rs +++ b/fastpay_core/src/consensus.rs @@ -20,12 +20,18 @@ pub struct ConsensusState { pub participants: BTreeSet, /// Pending proposal. pub proposed: Option, - /// Pending locked (i.e. pre-committed) proposal. - pub locked: Option, + /// Locked proposal and its "pre-commit" certificate. + pub locked: Option, + /// The certificate that created this instance. + pub received: Certificate, } impl ConsensusState { - pub fn new(functionality: Functionality, expected: Vec<(AccountId, SequenceNumber)>) -> Self { + pub fn new( + functionality: Functionality, + expected: Vec<(AccountId, SequenceNumber)>, + received: Certificate, + ) -> Self { let accounts: Vec<_> = expected.iter().map(|(id, _)| id.clone()).collect(); let sequence_numbers: BTreeMap<_, _> = expected.into_iter().collect(); assert_eq!(accounts.len(), sequence_numbers.len()); @@ -37,6 +43,7 @@ impl ConsensusState { participants: BTreeSet::new(), proposed: None, locked: None, + received, } } diff --git a/fastpay_core/src/messages.rs b/fastpay_core/src/messages.rs index 9c029f2..7f5a1c4 100644 --- a/fastpay_core/src/messages.rs +++ b/fastpay_core/src/messages.rs @@ -510,6 +510,13 @@ impl Value { _ => None, } } + + pub fn pre_commit_proposal(&self) -> Option<&ConsensusProposal> { + match self { + Value::PreCommit { proposal, .. } => Some(proposal), + _ => None, + } + } } /// Non-testing code should make the pattern matching explicit so that From 2e27ba36110da62d979f1419085a6aa68849fa51 Mon Sep 17 00:00:00 2001 From: Mathieu Baudet Date: Mon, 15 Nov 2021 16:39:46 -0800 Subject: [PATCH 4/7] [consensus] create handler to return consensus info --- benchmark_client/src/client.rs | 2 +- benchmark_client/src/coco_client.rs | 2 +- benchmark_server/src/core.rs | 6 ++-- fastpay/src/client.rs | 2 +- fastpay/src/network.rs | 24 ++++++++------- fastpay_core/src/authority.rs | 30 +++++++++++++++---- fastpay_core/src/messages.rs | 13 ++++++++ fastpay_core/src/serialize.rs | 22 +++++++++----- .../src/unit_tests/serialize_tests.rs | 12 ++++---- fastpay_core/tests/staged/fastpay.yaml | 25 ++++++++++++++-- 10 files changed, 99 insertions(+), 39 deletions(-) diff --git a/benchmark_client/src/client.rs b/benchmark_client/src/client.rs index d8c956d..7f14d55 100644 --- a/benchmark_client/src/client.rs +++ b/benchmark_client/src/client.rs @@ -159,7 +159,7 @@ impl BenchmarkClient { tokio::select! { Some(bytes) = rx_certificate.recv() => { match deserialize_message(&*bytes).unwrap() { - SerializedMessage::InfoResponse(response) => { + SerializedMessage::AccountInfoResponse(response) => { let id = response .account_id .sequence_number() diff --git a/benchmark_client/src/coco_client.rs b/benchmark_client/src/coco_client.rs index 7b4c66e..ef8a88a 100644 --- a/benchmark_client/src/coco_client.rs +++ b/benchmark_client/src/coco_client.rs @@ -186,7 +186,7 @@ impl CocoBenchmarkClient { tokio::select! { Some(bytes) = rx_certificate.recv() => { match deserialize_message(&*bytes).unwrap() { - SerializedMessage::InfoResponse(response) => { + SerializedMessage::AccountInfoResponse(response) => { let id = response .account_id .sequence_number() diff --git a/benchmark_server/src/core.rs b/benchmark_server/src/core.rs index 65839f0..4b15974 100644 --- a/benchmark_server/src/core.rs +++ b/benchmark_server/src/core.rs @@ -10,7 +10,7 @@ use fastpay_core::{ error::FastPayError, serialize::{ serialize_coin_creation_response, serialize_cross_shard_request, serialize_error, - serialize_info_response, SerializedMessage, + serialize_account_info_response, SerializedMessage, }, }; use log::{debug, warn}; @@ -58,14 +58,14 @@ impl MessageHandler for Core { SerializedMessage::RequestOrder(message) => self .state .handle_request_order(*message) - .map(|info| Some(serialize_info_response(&info))), + .map(|info| Some(serialize_account_info_response(&info))), SerializedMessage::ConfirmationOrder(message) => { match self.state.handle_confirmation_order(*message) { Ok((info, continuation)) => { // Cross-shard request self.handle_continuation(continuation).await; // Response - Ok(Some(serialize_info_response(&info))) + Ok(Some(serialize_account_info_response(&info))) } Err(error) => Err(error), } diff --git a/fastpay/src/client.rs b/fastpay/src/client.rs index fc66bff..c1c1723 100644 --- a/fastpay/src/client.rs +++ b/fastpay/src/client.rs @@ -341,7 +341,7 @@ impl ClientContext { fn deserialize_response(response: &[u8]) -> Option { match deserialize_message(response) { - Ok(SerializedMessage::InfoResponse(info)) => Some(*info), + Ok(SerializedMessage::AccountInfoResponse(info)) => Some(*info), Ok(SerializedMessage::Error(error)) => { error!("Received error value: {}", error); None diff --git a/fastpay/src/network.rs b/fastpay/src/network.rs index 53be37a..2b2786d 100644 --- a/fastpay/src/network.rs +++ b/fastpay/src/network.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::transport::*; -use either::Either; use fastpay_core::{authority::*, base_types::*, client::*, error::*, messages::*, serialize::*}; #[cfg(feature = "benchmark")] @@ -192,14 +191,14 @@ impl MessageHandler for RunningServerState { .server .state .handle_request_order(*message) - .map(|info| Some(serialize_info_response(&info))), + .map(|info| Some(serialize_account_info_response(&info))), SerializedMessage::ConfirmationOrder(message) => { match self.server.state.handle_confirmation_order(*message) { Ok((info, continuation)) => { // Cross-shard request self.handle_continuation(continuation).await; // Response - Ok(Some(serialize_info_response(&info))) + Ok(Some(serialize_account_info_response(&info))) } Err(error) => Err(error), } @@ -219,11 +218,15 @@ impl MessageHandler for RunningServerState { } SerializedMessage::ConsensusOrder(message) => { match self.server.state.handle_consensus_order(*message) { - Ok(Either::Left(vote)) => { + Ok(ConsensusResponse::Info(info)) => { + // Response + Ok(Some(serialize_consensus_info_response(&info))) + } + Ok(ConsensusResponse::Vote(vote)) => { // Response Ok(Some(serialize_vote(&vote))) } - Ok(Either::Right(continuations)) => { + Ok(ConsensusResponse::Continuations(continuations)) => { // Cross-shard requests for continuation in continuations { self.handle_continuation(continuation).await; @@ -234,11 +237,11 @@ impl MessageHandler for RunningServerState { Err(error) => Err(error), } } - SerializedMessage::InfoQuery(message) => self + SerializedMessage::AccountInfoQuery(message) => self .server .state .handle_account_info_query(*message) - .map(|info| Some(serialize_info_response(&info))), + .map(|info| Some(serialize_account_info_response(&info))), SerializedMessage::CrossShardRequest(request) => { match self.server.state.handle_cross_shard_request(*request) { Ok(()) => (), @@ -252,7 +255,8 @@ impl MessageHandler for RunningServerState { SerializedMessage::Vote(_) | SerializedMessage::CoinCreationResponse(_) | SerializedMessage::Error(_) - | SerializedMessage::InfoResponse(_) => { + | SerializedMessage::AccountInfoResponse(_) + | SerializedMessage::ConsensusInfoResponse(_) => { Err(FastPayError::UnexpectedMessage) } } @@ -367,7 +371,7 @@ impl Client { Ok(response) => { // Parse reply match deserialize_message(&response[..]) { - Ok(SerializedMessage::InfoResponse(resp)) => Ok(*resp), + Ok(SerializedMessage::AccountInfoResponse(resp)) => Ok(*resp), Ok(SerializedMessage::Error(error)) => Err(*error), Err(_) => Err(FastPayError::InvalidDecoding), _ => Err(FastPayError::UnexpectedMessage), @@ -450,7 +454,7 @@ impl AuthorityClient for Client { ) -> AsyncResult { Box::pin(async move { let shard = AuthorityState::get_shard(self.num_shards, &request.account_id); - self.send_recv_info_bytes(shard, serialize_info_query(&request)) + self.send_recv_info_bytes(shard, serialize_account_info_query(&request)) .await }) } diff --git a/fastpay_core/src/authority.rs b/fastpay_core/src/authority.rs index 46b8417..e1a4711 100644 --- a/fastpay_core/src/authority.rs +++ b/fastpay_core/src/authority.rs @@ -7,7 +7,6 @@ use crate::{ }; use bls12_381::Scalar; use std::collections::{BTreeMap, BTreeSet, HashSet}; -use either::Either; #[cfg(test)] #[path = "unit_tests/authority_tests.rs"] @@ -44,6 +43,13 @@ pub enum CrossShardContinuation { }, } +/// The response to a consensus order. +pub enum ConsensusResponse { + Info(ConsensusInfoResponse), + Vote(Vote), + Continuations(Vec), +} + /// Interface provided by each (shard of an) authority. /// All commands return either the current account info or an error. /// Repeating commands produces no changes and returns no error. @@ -71,7 +77,7 @@ pub trait Authority { fn handle_consensus_order( &mut self, order: ConsensusOrder, - ) -> Result>, FastPayError>; + ) -> Result; /// Force synchronization to finalize requests from Primary to FastPay. fn handle_primary_synchronization_order( @@ -450,8 +456,20 @@ impl Authority for AuthorityState { fn handle_consensus_order( &mut self, order: ConsensusOrder, - ) -> Result>, FastPayError> { + ) -> Result { match order { + ConsensusOrder::GetStatus { instance_id } => { + let instance = self + .instances + .get_mut(&instance_id) + .ok_or(FastPayError::UnknownConsensusInstance(instance_id))?; + let info = ConsensusInfoResponse { + proposed: instance.proposed.clone(), + locked: instance.locked.clone(), + received: instance.received.clone(), + }; + Ok(ConsensusResponse::Info(info)) + } ConsensusOrder::Propose { proposal, owner, @@ -516,7 +534,7 @@ impl Authority for AuthorityState { // Vote in favor of pre-commit (aka lock). let value = Value::PreCommit { proposal, requests }; let vote = Vote::new(value, &self.key_pair); - Ok(Either::Left(vote)) + Ok(ConsensusResponse::Vote(vote)) } ConsensusOrder::HandlePreCommit { certificate } => { certificate.check(&self.committee)?; @@ -552,7 +570,7 @@ impl Authority for AuthorityState { // Vote in favor of commit. let value = Value::Commit { proposal, requests }; let vote = Vote::new(value, &self.key_pair); - Ok(Either::Left(vote)) + Ok(ConsensusResponse::Vote(vote)) } ConsensusOrder::HandleCommit { certificate, locks } => { certificate.check(&self.committee)?; @@ -609,7 +627,7 @@ impl Authority for AuthorityState { } }) .collect(); - Ok(Either::Right(continuations)) + Ok(ConsensusResponse::Continuations(continuations)) } } } diff --git a/fastpay_core/src/messages.rs b/fastpay_core/src/messages.rs index 7f5a1c4..6917e2b 100644 --- a/fastpay_core/src/messages.rs +++ b/fastpay_core/src/messages.rs @@ -241,6 +241,19 @@ pub enum ConsensusOrder { certificate: Certificate, locks: Vec, }, + GetStatus { + instance_id: AccountId, + }, +} + +/// Current status of a consensus instance. +/// TODO: Information on available rounds. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(test, derive(Eq, PartialEq))] +pub struct ConsensusInfoResponse { + pub proposed: Option, + pub locked: Option, + pub received: Certificate, } /// A vote on a statement from an authority. diff --git a/fastpay_core/src/serialize.rs b/fastpay_core/src/serialize.rs index 0f31f28..c232e76 100644 --- a/fastpay_core/src/serialize.rs +++ b/fastpay_core/src/serialize.rs @@ -18,11 +18,12 @@ pub enum SerializedMessage { ConfirmationOrder(Box), CoinCreationOrder(Box), ConsensusOrder(Box), - InfoQuery(Box), + AccountInfoQuery(Box), // Outbound Vote(Box), - InfoResponse(Box), + AccountInfoResponse(Box), CoinCreationResponse(Box), + ConsensusInfoResponse(Box), Error(Box), // Internal to an authority CrossShardRequest(Box), @@ -38,11 +39,12 @@ enum ShallowSerializedMessage<'a> { ConfirmationOrder(&'a ConfirmationOrder), CoinCreationOrder(&'a CoinCreationOrder), ConsensusOrder(&'a ConsensusOrder), - InfoQuery(&'a AccountInfoQuery), + AccountInfoQuery(&'a AccountInfoQuery), // Outbound Vote(&'a Vote), - InfoResponse(&'a AccountInfoResponse), + AccountInfoResponse(&'a AccountInfoResponse), CoinCreationResponse(&'a CoinCreationResponse), + ConsensusInfoResponse(&'a ConsensusInfoResponse), Error(&'a FastPayError), // Internal to an authority CrossShardRequest(&'a CrossShardRequest), @@ -102,12 +104,16 @@ where serialize_into(writer, &ShallowSerializedMessage::ConfirmationOrder(value)) } -pub fn serialize_info_query(value: &AccountInfoQuery) -> Vec { - serialize(&ShallowSerializedMessage::InfoQuery(value)) +pub fn serialize_account_info_query(value: &AccountInfoQuery) -> Vec { + serialize(&ShallowSerializedMessage::AccountInfoQuery(value)) } -pub fn serialize_info_response(value: &AccountInfoResponse) -> Vec { - serialize(&ShallowSerializedMessage::InfoResponse(value)) +pub fn serialize_account_info_response(value: &AccountInfoResponse) -> Vec { + serialize(&ShallowSerializedMessage::AccountInfoResponse(value)) +} + +pub fn serialize_consensus_info_response(value: &ConsensusInfoResponse) -> Vec { + serialize(&ShallowSerializedMessage::ConsensusInfoResponse(value)) } pub fn serialize_cross_shard_request(value: &CrossShardRequest) -> Vec { diff --git a/fastpay_core/src/unit_tests/serialize_tests.rs b/fastpay_core/src/unit_tests/serialize_tests.rs index aab406d..0f423be 100644 --- a/fastpay_core/src/unit_tests/serialize_tests.rs +++ b/fastpay_core/src/unit_tests/serialize_tests.rs @@ -32,20 +32,20 @@ fn test_info_query() { query_received_certificates_excluding_first_nth: None, }; - let buf1 = serialize_info_query(&query1); - let buf2 = serialize_info_query(&query2); + let buf1 = serialize_account_info_query(&query1); + let buf2 = serialize_account_info_query(&query2); let result1 = deserialize_message(buf1.as_slice()); let result2 = deserialize_message(buf2.as_slice()); assert!(result1.is_ok()); assert!(result2.is_ok()); - if let SerializedMessage::InfoQuery(o) = result1.unwrap() { + if let SerializedMessage::AccountInfoQuery(o) = result1.unwrap() { assert!(*o == query1); } else { panic!() } - if let SerializedMessage::InfoQuery(o) = result2.unwrap() { + if let SerializedMessage::AccountInfoQuery(o) = result2.unwrap() { assert!(*o == query2); } else { panic!() @@ -226,10 +226,10 @@ fn test_info_response() { }; for resp in [resp1, resp2, resp3, resp4].iter() { - let buf = serialize_info_response(resp); + let buf = serialize_account_info_response(resp); let result = deserialize_message(buf.as_slice()); assert!(result.is_ok()); - if let SerializedMessage::InfoResponse(o) = result.unwrap() { + if let SerializedMessage::AccountInfoResponse(o) = result.unwrap() { assert!(*o == *resp); } else { panic!() diff --git a/fastpay_core/tests/staged/fastpay.yaml b/fastpay_core/tests/staged/fastpay.yaml index c43cfed..091f03c 100644 --- a/fastpay_core/tests/staged/fastpay.yaml +++ b/fastpay_core/tests/staged/fastpay.yaml @@ -158,6 +158,16 @@ ConsensusDecision: Abort: UNIT 1: Confirm: UNIT +ConsensusInfoResponse: + STRUCT: + - proposed: + OPTION: + TYPENAME: ConsensusProposal + - locked: + OPTION: + TYPENAME: Certificate + - received: + TYPENAME: Certificate ConsensusOrder: ENUM: 0: @@ -185,6 +195,11 @@ ConsensusOrder: - locks: SEQ: TYPENAME: Certificate + 3: + GetStatus: + STRUCT: + - instance_id: + TYPENAME: AccountId ConsensusProposal: STRUCT: - instance_id: @@ -683,7 +698,7 @@ SerializedMessage: NEWTYPE: TYPENAME: ConsensusOrder 4: - InfoQuery: + AccountInfoQuery: NEWTYPE: TYPENAME: AccountInfoQuery 5: @@ -691,7 +706,7 @@ SerializedMessage: NEWTYPE: TYPENAME: Vote 6: - InfoResponse: + AccountInfoResponse: NEWTYPE: TYPENAME: AccountInfoResponse 7: @@ -699,10 +714,14 @@ SerializedMessage: NEWTYPE: TYPENAME: CoinCreationResponse 8: + ConsensusInfoResponse: + NEWTYPE: + TYPENAME: ConsensusInfoResponse + 9: Error: NEWTYPE: TYPENAME: FastPayError - 9: + 10: CrossShardRequest: NEWTYPE: TYPENAME: CrossShardRequest From 6c661235610fd12586cfeeec13d26d929b37f886 Mon Sep 17 00:00:00 2001 From: Mathieu Baudet Date: Mon, 15 Nov 2021 17:35:08 -0800 Subject: [PATCH 5/7] [consensus] also expose which account ids have been locked so far --- fastpay_core/src/authority.rs | 1 + fastpay_core/src/messages.rs | 3 ++- fastpay_core/tests/staged/fastpay.yaml | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fastpay_core/src/authority.rs b/fastpay_core/src/authority.rs index e1a4711..679c55e 100644 --- a/fastpay_core/src/authority.rs +++ b/fastpay_core/src/authority.rs @@ -464,6 +464,7 @@ impl Authority for AuthorityState { .get_mut(&instance_id) .ok_or(FastPayError::UnknownConsensusInstance(instance_id))?; let info = ConsensusInfoResponse { + locked_accounts: instance.locked_accounts.clone(), proposed: instance.proposed.clone(), locked: instance.locked.clone(), received: instance.received.clone(), diff --git a/fastpay_core/src/messages.rs b/fastpay_core/src/messages.rs index 6917e2b..4efd364 100644 --- a/fastpay_core/src/messages.rs +++ b/fastpay_core/src/messages.rs @@ -4,7 +4,7 @@ use super::{base_types::*, committee::Committee, error::FastPayError}; use ff::Field; use serde::{Deserialize, Serialize}; -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; #[cfg(test)] #[path = "unit_tests/messages_tests.rs"] @@ -251,6 +251,7 @@ pub enum ConsensusOrder { #[derive(Clone, Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(Eq, PartialEq))] pub struct ConsensusInfoResponse { + pub locked_accounts: BTreeMap, pub proposed: Option, pub locked: Option, pub received: Certificate, diff --git a/fastpay_core/tests/staged/fastpay.yaml b/fastpay_core/tests/staged/fastpay.yaml index 091f03c..1a1a164 100644 --- a/fastpay_core/tests/staged/fastpay.yaml +++ b/fastpay_core/tests/staged/fastpay.yaml @@ -160,6 +160,12 @@ ConsensusDecision: Confirm: UNIT ConsensusInfoResponse: STRUCT: + - locked_accounts: + MAP: + KEY: + TYPENAME: AccountId + VALUE: + TYPENAME: PublicKeyBytes - proposed: OPTION: TYPENAME: ConsensusProposal From fd54b0037a5fe46013a00b2155a5a4d6e48f8dca Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Fri, 21 Jan 2022 13:27:34 -0800 Subject: [PATCH 6/7] fix fmt --- benchmark_server/src/core.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark_server/src/core.rs b/benchmark_server/src/core.rs index 4b15974..9b6bfb7 100644 --- a/benchmark_server/src/core.rs +++ b/benchmark_server/src/core.rs @@ -9,8 +9,8 @@ use fastpay_core::{ base_types::AuthorityName, error::FastPayError, serialize::{ - serialize_coin_creation_response, serialize_cross_shard_request, serialize_error, - serialize_account_info_response, SerializedMessage, + serialize_account_info_response, serialize_coin_creation_response, + serialize_cross_shard_request, serialize_error, SerializedMessage, }, }; use log::{debug, warn}; From a4e3d7c953c8c4e00c1155e40fedcd2790229dae Mon Sep 17 00:00:00 2001 From: Mathieu Baudet <1105398+ma2bd@users.noreply.github.com> Date: Fri, 21 Jan 2022 16:43:42 -0800 Subject: [PATCH 7/7] comments --- fastpay_core/src/messages.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fastpay_core/src/messages.rs b/fastpay_core/src/messages.rs index 4efd364..aa1c2f2 100644 --- a/fastpay_core/src/messages.rs +++ b/fastpay_core/src/messages.rs @@ -153,7 +153,6 @@ pub struct ConsensusProposal { } /// A statement to be certified by the authorities. -// TODO: decide if we split Vote & Certificate in one type per kind of value. #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] pub enum Value { // -- Account management -- @@ -161,8 +160,10 @@ pub enum Value { Lock(Request), /// The request is ready to be confirmed (i.e. executed). Confirm(Request), - /// This coin is ready to be spent. + // -- Assets -- + /// This transparent coin is ready to be spent. Coin(TransparentCoin), + // -- Consensus -- /// The proposal was validated but confirmation will require additional steps. PreCommit { proposal: ConsensusProposal,