From bcaabf04f5cef05d7707293236fb29b1020fa3de Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Thu, 7 Jul 2022 15:48:57 +0200 Subject: [PATCH] fix(dan_layer/core): include state root in checkpoint signature (#4285) Description --- - includes the "real" state root in the checkpoint signature, replacing the zero state root. - makes fields in `SignerSignature` private - adds `sign_checkpoint` method to `SigningService` Motivation and Context --- Generates the state root for the pre-committed state and includes that in the checkpoint signatures on the commit step instead of a dummy value for the state root. BUG: the state root MMR only includes state updates for existing keys and does not include _new_ keys. This was a pre-existing bug TODO: Share a blinding factor seed between validators and derive checkpoint blinding factors. Currently, a zero commitment is included in the checkpoint signature and a random blinding factor generated by the VN wallet is used in the checkpoint output. How Has This Been Tested? --- Manually, signatures are included in the checkpoint --- .../src/conversions/sidechain_features.rs | 12 ++--- .../src/grpc/services/wallet_client.rs | 4 +- .../src/p2p/proto/conversions.rs | 8 +-- base_layer/core/src/proto/types_impls.rs | 12 ++--- .../side_chain}/checkpoint_challenge.rs | 18 +++---- .../transaction_components/side_chain/mod.rs | 3 ++ .../side_chain/signer_signature.rs | 4 +- .../validation/dan_validators/test_helpers.rs | 3 +- dan_layer/core/src/models/mod.rs | 2 - .../core/src/services/acceptance_manager.rs | 4 +- .../core/src/services/checkpoint_manager.rs | 4 +- dan_layer/core/src/services/mocks/mod.rs | 23 ++++----- .../src/services/service_specification.rs | 2 +- .../core/src/services/signing_service.rs | 27 +++++----- dan_layer/core/src/services/wallet_client.rs | 2 +- .../src/storage/metadata_backend_adapter.rs | 1 - .../core/src/workers/consensus_worker.rs | 17 ++++++- .../core/src/workers/states/commit_state.rs | 49 ++++++++++--------- .../src/workers/states/pre_commit_state.rs | 2 +- dan_layer/core/src/workers/states/prepare.rs | 2 +- .../engine/src/state/models/state_root.rs | 8 ++- .../engine/src/state/state_db_unit_of_work.rs | 14 +++++- 22 files changed, 127 insertions(+), 94 deletions(-) rename {dan_layer/core/src/models => base_layer/core/src/transactions/transaction_components/side_chain}/checkpoint_challenge.rs (84%) diff --git a/applications/tari_app_grpc/src/conversions/sidechain_features.rs b/applications/tari_app_grpc/src/conversions/sidechain_features.rs index e7a301c5e4..19a2da4c7d 100644 --- a/applications/tari_app_grpc/src/conversions/sidechain_features.rs +++ b/applications/tari_app_grpc/src/conversions/sidechain_features.rs @@ -529,8 +529,8 @@ impl TryFrom for CommitteeSignatures { impl> From for grpc::SignerSignature { fn from(value: B) -> Self { Self { - signer: value.borrow().signer.to_vec(), - signature: Some(grpc::Signature::from(&value.borrow().signature)), + signer: value.borrow().signer().to_vec(), + signature: Some(grpc::Signature::from(value.borrow().signature())), } } } @@ -539,13 +539,13 @@ impl TryFrom for SignerSignature { type Error = String; fn try_from(value: grpc::SignerSignature) -> Result { - Ok(Self { - signer: PublicKey::from_bytes(&value.signer).map_err(|err| err.to_string())?, - signature: value + Ok(Self::new( + PublicKey::from_bytes(&value.signer).map_err(|err| err.to_string())?, + value .signature .map(TryInto::try_into) .ok_or("signature not provided")??, - }) + )) } } //---------------------------------- ContractAcceptance --------------------------------------------// diff --git a/applications/tari_validator_node/src/grpc/services/wallet_client.rs b/applications/tari_validator_node/src/grpc/services/wallet_client.rs index 8c8d6feb68..7fac77d445 100644 --- a/applications/tari_validator_node/src/grpc/services/wallet_client.rs +++ b/applications/tari_validator_node/src/grpc/services/wallet_client.rs @@ -72,11 +72,11 @@ impl WalletClient for GrpcWalletClient { contract_id: &FixedHash, state_root: &StateRoot, checkpoint_number: u64, - checkpoint_signatures: Vec, + checkpoint_signatures: &[SignerSignature], ) -> Result<(), DigitalAssetError> { let inner = self.connection().await?; let committee_signatures = grpc::CommitteeSignatures { - signatures: checkpoint_signatures.into_iter().map(Into::into).collect(), + signatures: checkpoint_signatures.iter().map(Into::into).collect(), }; if checkpoint_number == 0 { diff --git a/applications/tari_validator_node/src/p2p/proto/conversions.rs b/applications/tari_validator_node/src/p2p/proto/conversions.rs index eba5926576..b966cbf80b 100644 --- a/applications/tari_validator_node/src/p2p/proto/conversions.rs +++ b/applications/tari_validator_node/src/p2p/proto/conversions.rs @@ -387,13 +387,13 @@ impl TryFrom for SignerSignature { type Error = String; fn try_from(value: proto::common::SignerSignature) -> Result { - Ok(Self { - signer: PublicKey::from_bytes(&value.signer).map_err(|err| err.to_string())?, - signature: value + Ok(Self::new( + PublicKey::from_bytes(&value.signer).map_err(|err| err.to_string())?, + value .signature .map(TryInto::try_into) .ok_or("signature not provided")??, - }) + )) } } diff --git a/base_layer/core/src/proto/types_impls.rs b/base_layer/core/src/proto/types_impls.rs index 376305f17c..a72abe599e 100644 --- a/base_layer/core/src/proto/types_impls.rs +++ b/base_layer/core/src/proto/types_impls.rs @@ -82,8 +82,8 @@ impl> From for proto::Signature { impl> From for proto::SignerSignature { fn from(value: B) -> Self { Self { - signer: value.borrow().signer.to_vec(), - signature: Some(proto::Signature::from(&value.borrow().signature)), + signer: value.borrow().signer().to_vec(), + signature: Some(proto::Signature::from(value.borrow().signature())), } } } @@ -92,13 +92,13 @@ impl TryFrom for SignerSignature { type Error = String; fn try_from(value: proto::SignerSignature) -> Result { - Ok(Self { - signer: PublicKey::from_bytes(&value.signer).map_err(|err| err.to_string())?, - signature: value + Ok(Self::new( + PublicKey::from_bytes(&value.signer).map_err(|err| err.to_string())?, + value .signature .map(TryInto::try_into) .ok_or("signature not provided")??, - }) + )) } } diff --git a/dan_layer/core/src/models/checkpoint_challenge.rs b/base_layer/core/src/transactions/transaction_components/side_chain/checkpoint_challenge.rs similarity index 84% rename from dan_layer/core/src/models/checkpoint_challenge.rs rename to base_layer/core/src/transactions/transaction_components/side_chain/checkpoint_challenge.rs index e5ef3c8450..29b0415b80 100644 --- a/dan_layer/core/src/models/checkpoint_challenge.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/checkpoint_challenge.rs @@ -20,9 +20,9 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use digest::Digest; -use tari_common_types::types::{Commitment, FixedHash, HashDigest}; -use tari_utilities::ByteArray; +use tari_common_types::types::{Commitment, FixedHash}; + +use crate::consensus::ConsensusHashWriter; #[derive(Debug, Clone, Copy)] pub struct CheckpointChallenge(FixedHash); @@ -31,15 +31,15 @@ impl CheckpointChallenge { pub fn new( contract_id: &FixedHash, checkpoint_commitment: &Commitment, - merkle_root: FixedHash, + merkle_root: &FixedHash, checkpoint_number: u64, ) -> Self { // TODO: Use new tari_crypto domain-separated hashing - let hash = HashDigest::new() - .chain(contract_id.as_slice()) - .chain(checkpoint_commitment.as_bytes()) - .chain(merkle_root.as_slice()) - .chain(&checkpoint_number.to_le_bytes()) + let hash = ConsensusHashWriter::default() + .chain(contract_id) + .chain(checkpoint_commitment) + .chain(merkle_root) + .chain(&checkpoint_number) .finalize() .into(); Self(hash) diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/mod.rs b/base_layer/core/src/transactions/transaction_components/side_chain/mod.rs index 4774ad2cab..61f425824b 100644 --- a/base_layer/core/src/transactions/transaction_components/side_chain/mod.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/mod.rs @@ -64,6 +64,9 @@ pub use sidechain_features::{SideChainFeatures, SideChainFeaturesBuilder}; mod contract_checkpoint; pub use contract_checkpoint::ContractCheckpoint; +mod checkpoint_challenge; +pub use checkpoint_challenge::CheckpointChallenge; + // Length of FixedString pub const FIXED_STR_LEN: usize = 32; pub type FixedString = [u8; FIXED_STR_LEN]; diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/signer_signature.rs b/base_layer/core/src/transactions/transaction_components/side_chain/signer_signature.rs index 8c510f73e7..4e7b4e028e 100644 --- a/base_layer/core/src/transactions/transaction_components/side_chain/signer_signature.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/signer_signature.rs @@ -33,8 +33,8 @@ use crate::consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSi #[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq, Default)] pub struct SignerSignature { - pub signer: PublicKey, - pub signature: Signature, + signer: PublicKey, + signature: Signature, } impl SignerSignature { diff --git a/base_layer/core/src/validation/dan_validators/test_helpers.rs b/base_layer/core/src/validation/dan_validators/test_helpers.rs index 2fd6592494..5f7b10fe82 100644 --- a/base_layer/core/src/validation/dan_validators/test_helpers.rs +++ b/base_layer/core/src/validation/dan_validators/test_helpers.rs @@ -249,8 +249,7 @@ pub fn create_acceptance_signature( private_key: RistrettoSecretKey, ) -> Signature { let challenge = ContractAcceptanceChallenge::new(&commitment, &contract_id); - - SignerSignature::sign(&private_key, &challenge).signature + SignerSignature::sign(&private_key, &challenge).signature().clone() } pub fn create_random_key_pair() -> (RistrettoSecretKey, RistrettoPublicKey) { diff --git a/dan_layer/core/src/models/mod.rs b/dan_layer/core/src/models/mod.rs index c202a79ea6..70d346b560 100644 --- a/dan_layer/core/src/models/mod.rs +++ b/dan_layer/core/src/models/mod.rs @@ -25,7 +25,6 @@ use std::{convert::TryFrom, fmt::Debug, hash::Hash}; mod asset_definition; mod base_layer_metadata; mod base_layer_output; -mod checkpoint_challenge; mod committee; pub mod domain_events; mod error; @@ -45,7 +44,6 @@ mod view_id; pub use asset_definition::{AssetDefinition, InitialState}; pub use base_layer_metadata::BaseLayerMetadata; pub use base_layer_output::{BaseLayerOutput, CheckpointOutput, CommitteeOutput}; -pub use checkpoint_challenge::CheckpointChallenge; pub use committee::Committee; pub use error::ModelError; pub use hot_stuff_message::HotStuffMessage; diff --git a/dan_layer/core/src/services/acceptance_manager.rs b/dan_layer/core/src/services/acceptance_manager.rs index e7a4e03ece..7d44794ff6 100644 --- a/dan_layer/core/src/services/acceptance_manager.rs +++ b/dan_layer/core/src/services/acceptance_manager.rs @@ -73,11 +73,11 @@ impl, + signature: &[SignerSignature], ) -> Result<(), DigitalAssetError>; } @@ -60,7 +60,7 @@ impl CheckpointManager for ConcreteCheckpoi &mut self, checkpoint_number: u64, state_root: StateRoot, - signatures: Vec, + signatures: &[SignerSignature], ) -> Result<(), DigitalAssetError> { info!( target: LOG_TARGET, diff --git a/dan_layer/core/src/services/mocks/mod.rs b/dan_layer/core/src/services/mocks/mod.rs index 2a7966aa1b..0ec28387e1 100644 --- a/dan_layer/core/src/services/mocks/mod.rs +++ b/dan_layer/core/src/services/mocks/mod.rs @@ -22,7 +22,6 @@ use std::{ collections::VecDeque, - marker::PhantomData, sync::{Arc, Mutex}, }; @@ -30,7 +29,7 @@ use async_trait::async_trait; use tari_common_types::types::{FixedHash, PublicKey}; use tari_core::{ chain_storage::UtxoMinedInfo, - transactions::transaction_components::{OutputType, SignerSignature}, + transactions::transaction_components::{CheckpointChallenge, OutputType, SignerSignature}, }; use tari_crypto::ristretto::RistrettoPublicKey; use tari_dan_common_types::TemplateId; @@ -201,18 +200,20 @@ impl EventsPublisher for MockEventsPublisher { } } -pub fn mock_signing_service() -> MockSigningService { - MockSigningService:: { p: PhantomData } +pub fn mock_signing_service() -> MockSigningService { + MockSigningService } -pub struct MockSigningService { - p: PhantomData, -} +pub struct MockSigningService; -impl SigningService for MockSigningService { - fn sign(&self, _identity: &TAddr, _challenge: &[u8]) -> Result { +impl SigningService for MockSigningService { + fn sign(&self, _challenge: &[u8]) -> Result { Ok(ValidatorSignature {}) } + + fn sign_checkpoint(&self, _challenge: &CheckpointChallenge) -> Result { + todo!() + } } #[derive(Clone)] @@ -345,7 +346,7 @@ impl WalletClient for MockWalletClient { _contract_id: &FixedHash, _state_root: &StateRoot, _checkpoint_number: u64, - _checkpoint_signatures: Vec, + _checkpoint_signatures: &[SignerSignature], ) -> Result<(), DigitalAssetError> { Ok(()) } @@ -493,7 +494,7 @@ impl ServiceSpecification for MockServiceSpecification { type Payload = TariDanPayload; type PayloadProcessor = MockPayloadProcessor; type PayloadProvider = MockStaticPayloadProvider; - type SigningService = MockSigningService; + type SigningService = MockSigningService; type StateDbBackendAdapter = MockStateDbBackupAdapter; type ValidatorNodeClientFactory = MockValidatorNodeClientFactory; type WalletClient = MockWalletClient; diff --git a/dan_layer/core/src/services/service_specification.rs b/dan_layer/core/src/services/service_specification.rs index 2bb0fdb5a1..5322c19f83 100644 --- a/dan_layer/core/src/services/service_specification.rs +++ b/dan_layer/core/src/services/service_specification.rs @@ -75,7 +75,7 @@ pub trait ServiceSpecification: Default + Clone { type Payload: Payload; type PayloadProcessor: PayloadProcessor; type PayloadProvider: PayloadProvider; - type SigningService: SigningService; + type SigningService: SigningService; type StateDbBackendAdapter: StateDbBackendAdapter; type ValidatorNodeClientFactory: ValidatorNodeClientFactory + Clone; type WalletClient: WalletClient + Clone; diff --git a/dan_layer/core/src/services/signing_service.rs b/dan_layer/core/src/services/signing_service.rs index 873267e09a..244cbc8a2c 100644 --- a/dan_layer/core/src/services/signing_service.rs +++ b/dan_layer/core/src/services/signing_service.rs @@ -22,16 +22,15 @@ use std::sync::Arc; -use tari_comms::{types::CommsPublicKey, NodeIdentity}; +use tari_comms::NodeIdentity; +use tari_core::transactions::transaction_components::{CheckpointChallenge, SignerSignature}; -use crate::{ - digital_assets_error::DigitalAssetError, - models::ValidatorSignature, - services::infrastructure_services::NodeAddressable, -}; +use crate::{digital_assets_error::DigitalAssetError, models::ValidatorSignature}; -pub trait SigningService { - fn sign(&self, identity: &TAddr, challenge: &[u8]) -> Result; +pub trait SigningService { + fn sign(&self, challenge: &[u8]) -> Result; + + fn sign_checkpoint(&self, challenge: &CheckpointChallenge) -> Result; } pub struct NodeIdentitySigningService { @@ -44,13 +43,13 @@ impl NodeIdentitySigningService { } } -impl SigningService for NodeIdentitySigningService { - fn sign(&self, identity: &CommsPublicKey, _challenge: &[u8]) -> Result { - if identity != self.node_identity.public_key() { - return Err(DigitalAssetError::InvalidSignature); - } - +impl SigningService for NodeIdentitySigningService { + fn sign(&self, _challenge: &[u8]) -> Result { // TODO better sig Ok(ValidatorSignature {}) } + + fn sign_checkpoint(&self, challenge: &CheckpointChallenge) -> Result { + Ok(SignerSignature::sign(self.node_identity.secret_key(), challenge)) + } } diff --git a/dan_layer/core/src/services/wallet_client.rs b/dan_layer/core/src/services/wallet_client.rs index 6906107e67..6ebc5a949b 100644 --- a/dan_layer/core/src/services/wallet_client.rs +++ b/dan_layer/core/src/services/wallet_client.rs @@ -34,7 +34,7 @@ pub trait WalletClient: Send + Sync { contract_id: &FixedHash, state_root: &StateRoot, checkpoint_number: u64, - checkpoint_signatures: Vec, + checkpoint_signatures: &[SignerSignature], ) -> Result<(), DigitalAssetError>; async fn submit_contract_acceptance( diff --git a/dan_layer/core/src/storage/metadata_backend_adapter.rs b/dan_layer/core/src/storage/metadata_backend_adapter.rs index 6d3999c93c..8b883d1810 100644 --- a/dan_layer/core/src/storage/metadata_backend_adapter.rs +++ b/dan_layer/core/src/storage/metadata_backend_adapter.rs @@ -20,7 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// TODO: probably want to use something like bors or consensus encoding use tari_utilities::message_format::MessageFormat; use crate::storage::AtomicDb; diff --git a/dan_layer/core/src/workers/consensus_worker.rs b/dan_layer/core/src/workers/consensus_worker.rs index da23388a3c..7835c3ca5e 100644 --- a/dan_layer/core/src/workers/consensus_worker.rs +++ b/dan_layer/core/src/workers/consensus_worker.rs @@ -27,7 +27,7 @@ use std::sync::{ use log::*; use tari_common_types::types::PublicKey; -use tari_dan_engine::state::{StateDbUnitOfWork, StateDbUnitOfWorkImpl, StateDbUnitOfWorkReader}; +use tari_dan_engine::state::{models::StateRoot, StateDbUnitOfWork, StateDbUnitOfWorkImpl, StateDbUnitOfWorkReader}; use tari_shutdown::ShutdownSignal; use tokio::time::Duration; @@ -61,6 +61,7 @@ pub struct ConsensusWorker { db_factory: TSpecification::DbFactory, chain_storage_service: TSpecification::ChainStorageService, state_db_unit_of_work: Option>, + state_db_state_root: Option, checkpoint_manager: TSpecification::CheckpointManager, validator_node_client_factory: TSpecification::ValidatorNodeClientFactory, } @@ -99,6 +100,7 @@ impl> ConsensusWorker> ConsensusWorkerProcessor<'a, ) .await?; // Will only be committed in DECIDE + self.worker.state_db_state_root = Some(state_tx.calculate_root()?); self.worker.state_db_unit_of_work = Some(state_tx); unit_of_work.commit()?; Ok(res) @@ -278,6 +281,14 @@ impl<'a, T: ServiceSpecification> ConsensusWorkerProcessor<'a, self.worker.asset_definition.contract_id, self.worker.committee_manager.current_committee()?.clone(), ); + let proposed_state_root = + self.worker + .state_db_state_root + .as_ref() + .copied() + .ok_or_else(|| DigitalAssetError::InvalidLogicPath { + reason: "state_db_state_root is None in commit phase".to_string(), + })?; let res = state .next_event( self.worker.timeout, @@ -286,6 +297,7 @@ impl<'a, T: ServiceSpecification> ConsensusWorkerProcessor<'a, &mut self.worker.outbound_service, &self.worker.signing_service, unit_of_work.clone(), + proposed_state_root, current_checkpoint_num, ) .await?; @@ -320,7 +332,7 @@ impl<'a, T: ServiceSpecification> ConsensusWorkerProcessor<'a, let checkpoint_number = self.chain_db.get_current_checkpoint_number()?; self.worker .checkpoint_manager - .create_checkpoint(checkpoint_number, state_tx.calculate_root()?, signatures) + .create_checkpoint(checkpoint_number, state_tx.calculate_root()?, &signatures) .await?; self.chain_db.increment_checkpoint_number()?; } @@ -340,6 +352,7 @@ impl<'a, T: ServiceSpecification> ConsensusWorkerProcessor<'a, "Status: {} in mempool ", self.worker.payload_provider.get_payload_queue().await, ); + self.worker.state_db_state_root = None; self.worker.state_db_unit_of_work = None; let mut state = states::NextViewState::::new(); state diff --git a/dan_layer/core/src/workers/states/commit_state.rs b/dan_layer/core/src/workers/states/commit_state.rs index e6a80b1f90..e57931a863 100644 --- a/dan_layer/core/src/workers/states/commit_state.rs +++ b/dan_layer/core/src/workers/states/commit_state.rs @@ -23,24 +23,14 @@ use std::collections::HashMap; use log::*; -use rand::rngs::OsRng; -use tari_common_types::types::{Commitment, FixedHash, PrivateKey}; -use tari_core::transactions::transaction_components::SignerSignature; -use tari_crypto::keys::SecretKey; +use tari_common_types::types::{Commitment, FixedHash}; +use tari_core::transactions::transaction_components::CheckpointChallenge; +use tari_dan_engine::state::models::StateRoot; use tokio::time::{sleep, Duration}; use crate::{ digital_assets_error::DigitalAssetError, - models::{ - CheckpointChallenge, - Committee, - HotStuffMessage, - HotStuffMessageType, - QuorumCertificate, - TreeNodeHash, - View, - ViewId, - }, + models::{Committee, HotStuffMessage, HotStuffMessageType, QuorumCertificate, TreeNodeHash, View, ViewId}, services::{ infrastructure_services::{InboundConnectionService, OutboundService}, ServiceSpecification, @@ -82,6 +72,7 @@ impl CommitState { outbound_service: &mut TSpecification::OutboundService, signing_service: &TSpecification::SigningService, mut unit_of_work: TUnitOfWork, + proposed_state_root: StateRoot, checkpoint_number: u64, ) -> Result { self.received_new_view_messages.clear(); @@ -100,7 +91,17 @@ impl CommitState { r = inbound_services.wait_for_qc(HotStuffMessageType::PreCommit, current_view.view_id()) => { let (from, message) = r?; let leader = self.committee.leader_for_view(current_view.view_id).clone(); - if let Some(result) = self.process_replica_message(&message, current_view, &from, &leader, outbound_service, signing_service, &mut unit_of_work, checkpoint_number).await? { + if let Some(result) = self.process_replica_message( + &message, + current_view, + &from, + &leader, + outbound_service, + signing_service, + &mut unit_of_work, + proposed_state_root, + checkpoint_number, + ).await? { break Ok(result); } } @@ -203,6 +204,7 @@ impl CommitState { outbound: &mut TSpecification::OutboundService, signing_service: &TSpecification::SigningService, unit_of_work: &mut TUnitOfWork, + proposed_state_root: StateRoot, checkpoint_number: u64, ) -> Result, DigitalAssetError> { if let Some(justify) = message.justify() { @@ -232,6 +234,7 @@ impl CommitState { view_leader, current_view.view_id, signing_service, + proposed_state_root, checkpoint_number, ) .await?; @@ -249,20 +252,22 @@ impl CommitState { view_leader: &TSpecification::Addr, view_number: ViewId, signing_service: &TSpecification::SigningService, + proposed_state_root: StateRoot, checkpoint_number: u64, ) -> Result<(), DigitalAssetError> { - // TODO: wire in the signer secret (probably node identity) - let signer_secret = PrivateKey::random(&mut OsRng); // TODO: Validators should have agreed on a checkpoint commitment and included this in the signature for base // layer validation let commitment = Commitment::default(); - // TODO: We need the finalized state root to be able to produce a signature - let state_root = FixedHash::zero(); - let challenge = CheckpointChallenge::new(&self.contract_id, &commitment, state_root, checkpoint_number); - let checkpoint_signature = SignerSignature::sign(&signer_secret, challenge); + let challenge = CheckpointChallenge::new( + &self.contract_id, + &commitment, + &proposed_state_root.into(), + checkpoint_number, + ); + let checkpoint_signature = signing_service.sign_checkpoint(&challenge)?; let mut message = HotStuffMessage::vote_commit(node, view_number, self.contract_id, checkpoint_signature); - message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); + message.add_partial_sig(signing_service.sign(&message.create_signature_challenge())?); outbound.send(self.node_id.clone(), view_leader.clone(), message).await } } diff --git a/dan_layer/core/src/workers/states/pre_commit_state.rs b/dan_layer/core/src/workers/states/pre_commit_state.rs index 77a2f9f1a4..efde5447cf 100644 --- a/dan_layer/core/src/workers/states/pre_commit_state.rs +++ b/dan_layer/core/src/workers/states/pre_commit_state.rs @@ -246,7 +246,7 @@ impl PreCommitState { signing_service: &TSpecification::SigningService, ) -> Result<(), DigitalAssetError> { let mut message = HotStuffMessage::vote_pre_commit(node, view_number, self.contract_id); - message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); + message.add_partial_sig(signing_service.sign(&message.create_signature_challenge())?); outbound.send(self.node_id.clone(), view_leader.clone(), message).await } } diff --git a/dan_layer/core/src/workers/states/prepare.rs b/dan_layer/core/src/workers/states/prepare.rs index 24aba6c7e1..c33699d884 100644 --- a/dan_layer/core/src/workers/states/prepare.rs +++ b/dan_layer/core/src/workers/states/prepare.rs @@ -388,7 +388,7 @@ impl Prepare { ) -> Result<(), DigitalAssetError> { // TODO: Only send node hash, not the full node let mut message = HotStuffMessage::vote_prepare(node, view_number, self.contract_id); - message.add_partial_sig(signing_service.sign(&self.node_id, &message.create_signature_challenge())?); + message.add_partial_sig(signing_service.sign(&message.create_signature_challenge())?); outbound.send(self.node_id.clone(), view_leader.clone(), message).await } } diff --git a/dan_layer/engine/src/state/models/state_root.rs b/dan_layer/engine/src/state/models/state_root.rs index fb1d1efff7..f5102e5fca 100644 --- a/dan_layer/engine/src/state/models/state_root.rs +++ b/dan_layer/engine/src/state/models/state_root.rs @@ -3,7 +3,7 @@ use tari_common_types::types::FixedHash; -#[derive(PartialEq, Eq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct StateRoot { root: FixedHash, } @@ -23,3 +23,9 @@ impl StateRoot { } } } + +impl From for FixedHash { + fn from(state_root: StateRoot) -> Self { + state_root.root + } +} diff --git a/dan_layer/engine/src/state/state_db_unit_of_work.rs b/dan_layer/engine/src/state/state_db_unit_of_work.rs index fac7adc7d7..6652c0b998 100644 --- a/dan_layer/engine/src/state/state_db_unit_of_work.rs +++ b/dan_layer/engine/src/state/state_db_unit_of_work.rs @@ -62,7 +62,7 @@ impl UnitOfWorkContext { } } -pub struct StateDbUnitOfWorkImpl { +pub struct StateDbUnitOfWorkImpl { inner: Arc>>, context: UnitOfWorkContext, } @@ -101,6 +101,9 @@ impl StateDbUnitOfWork for StateDbUnitOf fn commit(&mut self) -> Result<(), StateStorageError> { let mut inner = self.inner.write()?; + if !inner.is_dirty() { + return Ok(()); + } let tx = inner .backend_adapter .create_transaction() @@ -191,6 +194,8 @@ impl StateDbUnitOfWorkReader for StateDb .map_err(TBackendAdapter::Error::into) } + // TODO: Needs to keep a merkle proof of the latest state and append all updates onto that to get the merkle root + // TODO: This does not include _new_ keys that are to be added in the updates fn calculate_root(&self) -> Result { let inner = self.inner.read()?; let tx = inner @@ -236,6 +241,7 @@ impl StateDbUnitOfWorkReader for StateDb let hasher = HashDigest::new(); top_level_mmr.push(hasher.chain(schema).chain(mmr.get_merkle_root()?).finalize().to_vec())?; } + Ok(StateRoot::new( top_level_mmr .get_merkle_root()? @@ -306,7 +312,7 @@ fn find_update( None } -pub struct StateDbUnitOfWorkInner { +pub struct StateDbUnitOfWorkInner { backend_adapter: TBackendAdapter, updates: Vec>, } @@ -318,4 +324,8 @@ impl StateDbUnitOfWorkInner bool { + !self.updates.is_empty() + } }