diff --git a/packages/dapi-grpc/build.rs b/packages/dapi-grpc/build.rs index 4da78da1b4c..8ae4b360eea 100644 --- a/packages/dapi-grpc/build.rs +++ b/packages/dapi-grpc/build.rs @@ -53,6 +53,7 @@ fn generate1( let mut pb = prost_build::Config::new(); pb.out_dir(out_dir); pb.format(true); + pb.protoc_arg("--experimental_allow_proto3_optional"); pb.compile_protos(files, proto_includes) } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index 1dcafffed74..4bf8aaa5ead 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -51,7 +51,8 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features = "tracing-log", ], optional = true } atty = { version = "0.2.14", optional = true } -tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", branch = "master", optional = true } +tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", branch = "master" } +# tenderdash-abci = { path = "../../../rs-tenderdash-abci/abci" } anyhow = { version = "1.0.70" } lazy_static = "1.4.0" itertools = { version = "0.10.5" } @@ -68,14 +69,7 @@ base64 = "0.20.0" [features] default = ["server"] -server = [ - "tenderdash-abci", - "clap", - "dotenvy", - "tracing-subscriber", - "atty", - "mockall", -] +server = ["clap", "dotenvy", "tracing-subscriber", "atty", "mockall"] [[bin]] name = "drive-abci" diff --git a/packages/rs-drive-abci/src/abci/commit.rs b/packages/rs-drive-abci/src/abci/commit.rs index 3cf3fbd9d7c..55e5c5ffea4 100644 --- a/packages/rs-drive-abci/src/abci/commit.rs +++ b/packages/rs-drive-abci/src/abci/commit.rs @@ -12,10 +12,14 @@ use tenderdash_abci::signatures::SignDigest; use super::AbciError; /// Represents block commit +#[derive(Clone, Debug)] pub struct Commit { - inner: proto::types::Commit, - chain_id: String, - quorum_type: QuorumType, + /// Commit in Tenderdash format + pub inner: proto::types::Commit, + /// ID of chain used to sign this commit + pub chain_id: String, + /// Type of quorum used to sign this commit + pub quorum_type: QuorumType, } impl Commit { diff --git a/packages/rs-drive-abci/src/abci/mimic.rs b/packages/rs-drive-abci/src/abci/mimic.rs index 2ea8ff7558d..e3609a2c2e8 100644 --- a/packages/rs-drive-abci/src/abci/mimic.rs +++ b/packages/rs-drive-abci/src/abci/mimic.rs @@ -22,14 +22,18 @@ use tenderdash_abci::proto::abci::response_verify_vote_extension::VerifyStatus; use tenderdash_abci::proto::abci::{CommitInfo, RequestExtendVote, RequestFinalizeBlock, RequestPrepareProposal, RequestProcessProposal, RequestVerifyVoteExtension, ResponsePrepareProposal, ValidatorSetUpdate}; use tenderdash_abci::proto::google::protobuf::Timestamp; use tenderdash_abci::proto::types::{ - Block, BlockId, Data, EvidenceList, Header, PartSetHeader, VoteExtension, VoteExtensionType, + Block, BlockId, Data, EvidenceList, Header, PartSetHeader, VoteExtension, VoteExtensionType, StateId, CanonicalVote, SignedMsgType, }; +use tenderdash_abci::signatures::SignBytes; use tenderdash_abci::{ signatures::SignDigest, - proto::{self, version::Consensus}, + proto::version::Consensus, Application, }; +/// Chain ID used in tests +pub const CHAIN_ID: &str = "strategy_tests"; + /// The outcome struct when mimicking block execution pub struct MimicExecuteBlockOutcome { /// withdrawal transactions @@ -40,6 +44,14 @@ pub struct MimicExecuteBlockOutcome { pub next_validator_set_hash: Vec, /// Root App hash pub root_app_hash: [u8; 32], + /// State ID needed to verify the block, for example height, app version, etc. + pub state_id: StateId, + /// Hash of CanonicalBlockId + pub block_id_hash: [u8; 32], + /// Block signature + pub signature: [u8; 96], + /// Version of Drive app used to generate this block + pub app_version: u64, } /// Options for execution @@ -61,8 +73,11 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { state_transitions: Vec, options: MimicExecuteBlockOptions, ) -> Result { + const APP_VERSION: u64 = 0; + const ROUND: i32 = 0; + let mut rng = StdRng::seed_from_u64(block_info.height); - let block_hash: [u8; 32] = rng.gen(); // We fake a block hash for the test + let next_validators_hash: [u8; 32] = rng.gen(); // We fake a block hash for the test let serialized_state_transitions = state_transitions .into_iter() @@ -75,7 +90,10 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { mut core_height, epoch: _, } = block_info; - + let time = Timestamp { + seconds: (time_ms / 1000) as i64, + nanos: ((time_ms % 1000) * 1000) as i32, + }; // PREPARE (also processes internally) let request_prepare_proposal = RequestPrepareProposal { @@ -84,16 +102,16 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { local_last_commit: None, misbehavior: vec![], height: height as i64, - time: Some(Timestamp { - seconds: (time_ms / 1000) as i64, - nanos: ((time_ms % 1000) * 1000) as i32, - }), + time: Some(time.clone()), next_validators_hash: next_validators_hash.to_vec(), - round: 0, + round: ROUND, core_chain_locked_height: core_height, proposer_pro_tx_hash: proposer_pro_tx_hash.to_vec(), proposed_app_version: proposed_version as u64, - version: Some(Consensus { block: 0, app: 0 }), + version: Some(Consensus { + block: 0, + app: APP_VERSION, + }), quorum_hash: current_quorum.quorum_hash.to_vec(), }; @@ -133,23 +151,50 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { // PROCESS + let state_id = StateId { + app_hash: app_hash.clone(), + app_version: APP_VERSION, + core_chain_locked_height: core_height, + height, + time: Some(time.clone()), + }; + let state_id_hash = state_id + .sha256(CHAIN_ID, height as i64, ROUND) + .expect("cannot hash state id"); + + let block_header_hash: [u8; 32] = rng.gen(); + let block_id = BlockId { + hash: block_header_hash.to_vec(), + part_set_header: Some(PartSetHeader { + total: 0, + hash: vec![0u8; 32], + }), + state_id: state_id_hash, + }; + let block_id_hash = block_id + .sha256(CHAIN_ID, height as i64, ROUND) + .expect("cannot hash block id"); + let request_process_proposal = RequestProcessProposal { txs: serialized_state_transitions, proposed_last_commit: None, misbehavior: vec![], - hash: block_hash.to_vec(), + hash: block_header_hash.to_vec(), height: height as i64, time: Some(Timestamp { seconds: (time_ms / 1000) as i64, nanos: ((time_ms % 1000) * 1000) as i32, }), next_validators_hash: next_validators_hash.to_vec(), - round: 0, + round: ROUND, core_chain_locked_height: core_height, core_chain_lock_update, proposer_pro_tx_hash: proposer_pro_tx_hash.to_vec(), proposed_app_version: proposed_version as u64, - version: Some(Consensus { block: 0, app: 0 }), + version: Some(Consensus { + block: 0, + app: APP_VERSION, + }), quorum_hash: current_quorum.quorum_hash.to_vec(), }; @@ -165,9 +210,9 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { let tx_order_for_finalize_block = tx_records.into_iter().map(|record| record.tx).collect(); let request_extend_vote = RequestExtendVote { - hash: block_hash.to_vec(), + hash: block_header_hash.to_vec(), height: height as i64, - round: 0, + round: ROUND, }; let response_extend_vote = self.extend_vote(request_extend_vote).unwrap_or_else(|e| { @@ -183,10 +228,10 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { for validator in current_quorum.validator_set.iter() { let request_verify_vote_extension = RequestVerifyVoteExtension { - hash: block_hash.to_vec(), + hash: block_header_hash.to_vec(), validator_pro_tx_hash: validator.pro_tx_hash.to_vec(), height: height as i64, - round: 0, + round: ROUND, vote_extensions: vote_extensions.clone(), }; let response_validate_vote_extension = self @@ -258,42 +303,55 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { drop(guarded_block_execution_context); - // We need to sign the block hash - let chain_id = "strategy_tests".to_string(); - let quorum_type = self.platform.config.quorum_type(); + // We need to sign the block - let block_id = BlockId { - hash: block_hash.to_vec(), //todo - part_set_header: Some(PartSetHeader::default()), // todo - state_id: [0; 32].to_vec(), //todo + let quorum_type = self.platform.config.quorum_type(); + let state_id_hash = state_id + .sha256(CHAIN_ID, height as i64, ROUND) + .expect("cannot calculate state id hash"); + + let commit = CanonicalVote { + block_id: block_id_hash.clone(), + state_id: state_id_hash, + chain_id: CHAIN_ID.to_string(), + height: height as i64, + round: ROUND as i64, + r#type: SignedMsgType::Precommit.into(), }; let quorum_hash = current_quorum.quorum_hash.to_vec(); - // quorum_hash.reverse(); let mut commit_info = CommitInfo { - round: 0, + round: ROUND, quorum_hash: quorum_hash.clone(), block_signature: Default::default(), threshold_vote_extensions: extensions, }; - - let commit = proto::types::Commit { - block_id: Some(block_id.clone()), - height: height as i64, - round: 0, - quorum_hash: quorum_hash.clone(), - threshold_block_signature: Default::default(), - threshold_vote_extensions: Default::default(), - }; - //if not in testing this will default to true if self.platform.config.testing_configs.block_signing { let quorum_hash: [u8; 32] = quorum_hash.try_into().expect("wrong quorum hash len"); let digest = commit - .sign_digest(&chain_id, quorum_type as u8, &quorum_hash, height as i64, 0) + .sign_digest( + CHAIN_ID, + quorum_type as u8, + &quorum_hash, + height as i64, + ROUND, + ) .expect("expected to sign digest"); + tracing::trace!( + digest=hex::encode(&digest), + ?state_id, + ?commit, + ?quorum_type, + ?quorum_hash, + CHAIN_ID, + height, + ROUND, + public_key = ?current_quorum.public_key, + "Signing block" + ); let block_signature = current_quorum.private_key.sign(digest.as_slice()); commit_info.block_signature = block_signature.to_bytes().to_vec(); @@ -306,45 +364,44 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { .map(|update| update.quorum_hash.clone()) .unwrap_or(current_quorum.quorum_hash.to_vec()); + let block = Block { + header: Some(Header { + version: Some(Consensus { + block: 0, //todo + app: APP_VERSION, + }), + chain_id: CHAIN_ID.to_string(), + height: height as i64, + time: Some(time), + last_block_id: None, + last_commit_hash: [0; 32].to_vec(), + data_hash: [0; 32].to_vec(), + validators_hash: current_quorum.quorum_hash.to_vec(), + next_validators_hash: next_validator_set_hash.clone(), + consensus_hash: [0; 32].to_vec(), + next_consensus_hash: [0; 32].to_vec(), + app_hash: app_hash.clone(), + results_hash: [0; 32].to_vec(), + evidence_hash: vec![], + proposed_app_version: proposed_version as u64, + proposer_pro_tx_hash: proposer_pro_tx_hash.to_vec(), + core_chain_locked_height: core_height, + }), + data: Some(Data { + txs: tx_order_for_finalize_block, + }), + evidence: Some(EvidenceList { evidence: vec![] }), + last_commit: None, + core_chain_lock: None, + }; + let request_finalize_block = RequestFinalizeBlock { - commit: Some(commit_info), + commit: Some(commit_info.clone()), misbehavior: vec![], - hash: block_hash.to_vec(), + hash: block_header_hash.to_vec(), height: height as i64, - round: 0, - block: Some(Block { - header: Some(Header { - version: Some(Consensus { - block: 0, //todo - app: 0, //todo - }), - chain_id, - height: height as i64, - time: Some(Timestamp { - seconds: (time_ms / 1000) as i64, - nanos: ((time_ms % 1000) * 1000) as i32, - }), - last_block_id: None, - last_commit_hash: [0; 32].to_vec(), - data_hash: [0; 32].to_vec(), - validators_hash: current_quorum.quorum_hash.to_vec(), - next_validators_hash: next_validator_set_hash.clone(), - consensus_hash: [0; 32].to_vec(), - next_consensus_hash: [0; 32].to_vec(), - app_hash: app_hash.clone(), - results_hash: [0; 32].to_vec(), - evidence_hash: vec![], - proposed_app_version: 0, - proposer_pro_tx_hash: proposer_pro_tx_hash.to_vec(), - core_chain_locked_height: core_height, - }), - data: Some(Data { - txs: tx_order_for_finalize_block, - }), - evidence: Some(EvidenceList { evidence: vec![] }), - last_commit: None, - core_chain_lock: None, - }), + round: ROUND, + block: Some(block), block_id: Some(block_id), }; @@ -380,12 +437,19 @@ impl<'a, C: CoreRPCLike> AbciApplication<'a, C> { } Ok(MimicExecuteBlockOutcome { + app_version: APP_VERSION, withdrawal_transactions: withdrawals, validator_set_update, next_validator_set_hash, root_app_hash: app_hash .try_into() .expect("expected 32 bytes for the root hash"), + state_id, + block_id_hash: block_id_hash.try_into().expect("invalid block id hash len"), + signature: commit_info + .block_signature + .try_into() + .expect("signature mut be 96 bytes long"), }) } } diff --git a/packages/rs-drive-abci/src/logging.rs b/packages/rs-drive-abci/src/logging.rs index e5f8c052368..97337cebf70 100644 --- a/packages/rs-drive-abci/src/logging.rs +++ b/packages/rs-drive-abci/src/logging.rs @@ -22,6 +22,7 @@ use tracing_subscriber::fmt; use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt; use tracing_subscriber::registry; use tracing_subscriber::util::SubscriberInitExt; +use tracing_subscriber::util::TryInitError; use tracing_subscriber::EnvFilter; use tracing_subscriber::Layer; use tracing_subscriber::Registry; @@ -288,7 +289,7 @@ impl Loggers { /// /// # Examples /// - /// ``` + /// ```no_run /// use drive_abci::logging::{LogBuilder, Loggers}; /// /// // Create logger(s) using LogBuilder @@ -306,10 +307,28 @@ impl Loggers { /// /// This method panics if the logging subsystem is already initialized. pub fn install(&self) { + if let Err(e) = self.try_install() { + panic!("Logging subsystem is already initialized: {}", e) + } + } + + /// Installs loggers prepared in the [LogBuilder] as a global tracing handler. + /// + /// Same as [Loggers::install()], but returns error if the logging subsystem is already initialized. + /// + /// # Example + /// + /// The following code can be used in tests. It ignores errors, as tests might actually call it more than once, + /// and we don't want to panic in this case. + /// + /// ``` + /// drive_abci::logging::Loggers::default().try_install().ok(); + /// ``` + pub fn try_install(&self) -> Result<(), TryInitError> { // Based on examples from https://docs.rs/tracing-subscriber/0.3.17/tracing_subscriber/layer/index.html let loggers = self.0.values().map(|l| Box::new(l.layer())).collect_vec(); - registry().with(loggers).init(); + registry().with(loggers).try_init() } /// Flushes all loggers. @@ -362,6 +381,30 @@ impl Loggers { } } +impl Default for Loggers { + /// Default loggers that are just printing human-readable logs, based on `RUST_LOG` env variable. + /// + /// Useful for tests. + /// + /// # Panics + /// + /// Panics in (a very unlikely) event when logger builder fails to add new logger. + /// + /// # Example + /// + /// ``` + /// use drive_abci::logging::Loggers; + /// + /// Loggers::default().try_install().ok(); + /// ``` + fn default() -> Self { + let mut logger_builder = LogBuilder::new(); + logger_builder + .add("default", &LogConfig::default()) + .expect("cannot configure default logger"); + logger_builder.build() + } +} // // NON-PUBLIC TYPES // diff --git a/packages/rs-drive-abci/src/validation/state_transition/identity_create.rs b/packages/rs-drive-abci/src/validation/state_transition/identity_create.rs index ea58789115a..6723924cd17 100644 --- a/packages/rs-drive-abci/src/validation/state_transition/identity_create.rs +++ b/packages/rs-drive-abci/src/validation/state_transition/identity_create.rs @@ -6,9 +6,7 @@ use dpp::consensus::basic::invalid_identifier_error::InvalidIdentifierError; use dpp::consensus::basic::BasicError; use dpp::consensus::state::identity::IdentityAlreadyExistsError; use dpp::consensus::ConsensusError; - use dpp::dashcore::OutPoint; - use dpp::identity::state_transition::identity_create_transition::validation::basic::IDENTITY_CREATE_TRANSITION_SCHEMA_VALIDATOR; use dpp::identity::state_transition::identity_create_transition::IdentityCreateTransitionAction; use dpp::identity::PartialIdentity; diff --git a/packages/rs-drive-abci/tests/strategy_tests/execution.rs b/packages/rs-drive-abci/tests/strategy_tests/execution.rs index ff3ee4b4102..13f24fd05bd 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/execution.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/execution.rs @@ -17,7 +17,7 @@ use dashcore_rpc::dashcore_rpc_json::{ }; use dpp::block::block_info::BlockInfo; use dpp::block::epoch::Epoch; -use drive_abci::abci::mimic::{MimicExecuteBlockOptions, MimicExecuteBlockOutcome}; +use drive_abci::abci::mimic::{self, MimicExecuteBlockOptions, MimicExecuteBlockOutcome}; use drive_abci::abci::AbciApplication; use drive_abci::config::PlatformConfig; use drive_abci::execution::fee_pools::epoch::{EpochInfo, EPOCH_CHANGE_TIME_MS}; @@ -690,6 +690,10 @@ pub(crate) fn continue_chain_for_strategy( validator_set_update, next_validator_set_hash, root_app_hash, + state_id, + block_id_hash: block_hash, + signature, + app_version, } = abci_app .mimic_execute_block( proposer.pro_tx_hash.into_inner(), @@ -739,9 +743,17 @@ pub(crate) fn continue_chain_for_strategy( if let Some(query_strategy) = &strategy.query_testing { query_strategy.query_chain_for_strategy( &ProofVerification { - root_app_hash: &root_app_hash, - block_signature: &vec![], //todo - quorum_hash: ¤t_quorum_with_test_info.quorum_hash.into_inner(), + quorum_hash: current_quorum_with_test_info.quorum_hash.as_inner(), + quorum_type: config.quorum_type(), + app_version, + chain_id: mimic::CHAIN_ID.to_string(), + core_chain_locked_height: state_id.core_chain_locked_height, + height: state_id.height as i64, + block_hash: &block_hash, + app_hash: &root_app_hash, + time: state_id.time.expect("time is required in StateId"), + signature: &signature, + public_key: ¤t_quorum_with_test_info.public_key, }, ¤t_identities, &abci_app, @@ -757,7 +769,7 @@ pub(crate) fn continue_chain_for_strategy( i += 1; i %= quorum_size; //todo: this could be variable } - } + } // for block_height let masternode_identity_balances = if strategy.dont_finalize_block() && i == 0 { BTreeMap::new() diff --git a/packages/rs-drive-abci/tests/strategy_tests/main.rs b/packages/rs-drive-abci/tests/strategy_tests/main.rs index 0bdc69e1edd..2bc1469a600 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/main.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/main.rs @@ -780,7 +780,8 @@ mod tests { } #[test] - fn run_chain_insert_one_new_identity_per_block() { + fn run_chain_insert_one_new_identity_per_block_with_block_signing() { + // drive_abci::logging::Loggers::default().try_install().ok(); let strategy = Strategy { contracts_with_updates: vec![], operations: vec![], @@ -812,7 +813,7 @@ mod tests { quorum_size: 100, validator_set_quorum_rotation_block_count: 25, block_spacing_ms: 3000, - testing_configs: PlatformTestConfig::default_with_no_block_signing(), + testing_configs: PlatformTestConfig::default(), ..Default::default() }; let mut platform = TestPlatformBuilder::new() diff --git a/packages/rs-drive-abci/tests/strategy_tests/query.rs b/packages/rs-drive-abci/tests/strategy_tests/query.rs index 19b326e923c..411c0d01b98 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/query.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/query.rs @@ -2,31 +2,158 @@ use crate::frequency::Frequency; use crate::strategy::StrategyRandomness; use dapi_grpc::platform::v0::{ get_identities_by_public_key_hashes_response, GetIdentitiesByPublicKeyHashesRequest, - GetIdentitiesByPublicKeyHashesResponse, + GetIdentitiesByPublicKeyHashesResponse, Proof, }; +use dashcore_rpc::dashcore_rpc_json::QuorumType; use dpp::identity::{Identity, PartialIdentity}; use dpp::serialization_traits::PlatformDeserializable; +use dpp::validation::SimpleValidationResult; use drive::drive::verify::RootHash; use drive::drive::Drive; -use drive_abci::abci::AbciApplication; - +use drive_abci::abci::{AbciApplication, AbciError}; use drive_abci::rpc::core::MockCoreRPCLike; use prost::Message; use rand::prelude::SliceRandom; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use std::collections::{HashMap, HashSet}; +use tenderdash_abci::proto::google::protobuf::Timestamp; +use tenderdash_abci::proto::types::{CanonicalVote, SignedMsgType, StateId}; +use tenderdash_abci::signatures::{SignBytes, SignDigest}; #[derive(Clone, Debug, Default)] pub struct QueryStrategy { pub query_identities_by_public_key_hashes: Frequency, } -#[derive(Debug)] +/// ProofVerification contains trusted data from Platform chain (Tenderdash) needed to verify proofs at given `height`. +/// +/// See https://github.com/dashpay/tenderdash/blob/v0.12-dev/spec/consensus/signing.md#block-signature-verification-on-light-client +#[derive(Debug, Clone)] pub struct ProofVerification<'a> { - pub root_app_hash: &'a [u8; 32], - pub block_signature: &'a Vec, + /// Chain ID + pub chain_id: String, + + /// Type of quorum + pub quorum_type: QuorumType, + + /// Quorum hash pub quorum_hash: &'a [u8; 32], + + /// Commit height + pub height: i64, + + /// Hash of CanonicalBlockID + pub block_hash: &'a [u8; 32], + + /// Version of ABCI app used to generate this commit + pub app_version: u64, + + /// App hash for the `height` + pub app_hash: &'a [u8; 32], + + /// Core chain locked height in use when generating block + pub core_chain_locked_height: u32, + + /// Block generation time + pub time: Timestamp, + + /// Block signature + pub signature: &'a [u8; 96], + + /// Threshold key used to verify the signature + pub public_key: &'a dpp::bls_signatures::PublicKey, +} + +impl<'a> ProofVerification<'a> { + /// Verify proof signature + /// + /// Constructs new signature for provided state ID and checks if signature is still valid. + /// + /// Implements algorithm described at: + /// https://github.com/dashpay/tenderdash/blob/v0.12-dev/spec/consensus/signing.md#block-signature-verification-on-light-client + fn verify_signature(&self, state_id: StateId, round: u32) -> SimpleValidationResult { + let state_id_hash = match state_id.sha256(&self.chain_id, self.height, round as i32) { + Ok(s) => s, + Err(e) => return SimpleValidationResult::new_with_error(AbciError::from(e)), + }; + + let v = CanonicalVote { + block_id: self.block_hash.to_vec(), + state_id: state_id_hash, + chain_id: self.chain_id.clone(), + height: self.height, + round: round as i64, + r#type: SignedMsgType::Precommit.into(), + }; + + let digest = match v.sign_digest( + &self.chain_id, + self.quorum_type as u8, + self.quorum_hash, + self.height, + round as i32, + ) { + Ok(h) => h, + Err(e) => return SimpleValidationResult::new_with_error(e.into()), + }; + // We could have received a fake commit, so signature validation needs to be returned if error as a simple validation result + let signature = match dpp::bls_signatures::Signature::from_bytes(self.signature) { + Ok(signature) => signature, + Err(e) => { + return SimpleValidationResult::new_with_error( + AbciError::BlsErrorOfTenderdashThresholdMechanism( + e, + format!("Malformed signature data: {}", hex::encode(&self.signature)), + ), + ) + } + }; + tracing::trace!( + digest=hex::encode(&digest), + ?state_id, + commit = ?v, + verification_context = ?self, + "Proof verification" + ); + match self.public_key.verify(&signature, &digest) { + true => SimpleValidationResult::default(), + false => { + SimpleValidationResult::new_with_error(AbciError::BadCommitSignature(format!( + "commit signature {} is wrong", + hex::encode(signature.to_bytes().as_slice()) + ))) + } + } + } + + /// Verify proof returned by the Platform. + pub fn verify_proof(&self, app_hash: &[u8], proof: Proof) -> SimpleValidationResult { + tracing::debug!(?proof, app_hash = hex::encode(&app_hash), "verifying proof"); + + if self.app_hash != app_hash { + return SimpleValidationResult::new_with_error(AbciError::InvalidState( + "Invalid root app hash".to_string(), + )); + }; + + if proof.signature != self.signature { + tracing::error!(?proof.signature,?self.signature, "proof signature mismatch"); + return SimpleValidationResult::new_with_error(AbciError::BadCommitSignature( + "Proof signature mismatch".to_string(), + )); + }; + + let state_id = StateId { + app_hash: app_hash.to_vec(), + app_version: self.app_version, + core_chain_locked_height: self.core_chain_locked_height, + height: self.height as u64, + time: Some(self.time.clone()), + }; + + self.verify_signature(state_id, proof.round) + } } impl QueryStrategy { @@ -138,7 +265,10 @@ impl QueryStrategy { ) }) .collect(); - assert_eq!(proof_verification.root_app_hash, &proof_root_hash); + assert_eq!(proof_verification.app_hash, &proof_root_hash); + assert!(proof_verification + .verify_proof(&proof_root_hash, proof) + .is_valid()); assert_eq!(identities, public_key_hashes); } get_identities_by_public_key_hashes_response::Result::Identities(data) => { diff --git a/packages/rs-drive/src/drive/asset_lock/mod.rs b/packages/rs-drive/src/drive/asset_lock/mod.rs index e7be0417d02..e54556be344 100644 --- a/packages/rs-drive/src/drive/asset_lock/mod.rs +++ b/packages/rs-drive/src/drive/asset_lock/mod.rs @@ -6,7 +6,6 @@ use crate::drive::object_size_info::PathKeyElementInfo::PathFixedSizeKeyRefEleme use crate::drive::{Drive, RootTree}; use crate::error::Error; use crate::fee::op::LowLevelDriveOperation; - use dpp::platform_value::Bytes36; use grovedb::batch::KeyInfoPath; use grovedb::Element::Item; diff --git a/packages/rs-drive/src/drive/batch/drive_op_batch/system.rs b/packages/rs-drive/src/drive/batch/drive_op_batch/system.rs index 15c9d94be17..7999636d6ad 100644 --- a/packages/rs-drive/src/drive/batch/drive_op_batch/system.rs +++ b/packages/rs-drive/src/drive/batch/drive_op_batch/system.rs @@ -4,7 +4,6 @@ use crate::error::Error; use crate::fee::credits::Credits; use crate::fee::op::LowLevelDriveOperation; use dpp::block::block_info::BlockInfo; - use dpp::platform_value::Bytes36; use grovedb::batch::KeyInfoPath; use grovedb::{EstimatedLayerInformation, TransactionArg}; diff --git a/packages/rs-drive/src/drive/grove_operations.rs b/packages/rs-drive/src/drive/grove_operations.rs index 40b16eacd21..503acd227f4 100644 --- a/packages/rs-drive/src/drive/grove_operations.rs +++ b/packages/rs-drive/src/drive/grove_operations.rs @@ -32,8 +32,6 @@ //! Defines and implements in Drive functions pertinent to groveDB operations. //! -use path::SubtreePath; - use crate::drive::batch::GroveDbOpBatch; use costs::storage_cost::removal::StorageRemovedBytes::BasicStorageRemoval; use costs::storage_cost::transition::OperationStorageTransitionType; @@ -43,12 +41,10 @@ use grovedb::batch::{ key_info::KeyInfo, BatchApplyOptions, GroveDbOp, KeyInfoPath, Op, OpsByLevelPath, }; use grovedb::{Element, EstimatedLayerInformation, GroveDb, PathQuery, TransactionArg}; - -use std::collections::HashMap; +use path::SubtreePath; use crate::drive::flags::StorageFlags; use crate::drive::object_size_info::DriveKeyInfo::{Key, KeyRef, KeySize}; - use crate::drive::object_size_info::PathKeyElementInfo::{ PathFixedSizeKeyRefElement, PathKeyElement, PathKeyElementSize, PathKeyRefElement, PathKeyUnknownElementSize, @@ -67,11 +63,10 @@ use grovedb::operations::insert::InsertOptions; use grovedb::query_result_type::{ PathKeyOptionalElementTrio, QueryResultElements, QueryResultType, }; - use grovedb::Error as GroveError; use integer_encoding::VarInt; - use intmap::IntMap; +use std::collections::HashMap; use storage::rocksdb_storage::RocksDbStorage; /// Pushes an operation's `OperationCost` to `drive_operations` given its `CostContext`