diff --git a/applications/tari_app_grpc/proto/base_node.proto b/applications/tari_app_grpc/proto/base_node.proto index d5cb26a707..a9bbcaf999 100644 --- a/applications/tari_app_grpc/proto/base_node.proto +++ b/applications/tari_app_grpc/proto/base_node.proto @@ -91,7 +91,7 @@ service BaseNode { // Get VNs rpc GetActiveValidatorNodes(GetActiveValidatorNodesRequest) returns (stream ActiveValidatorNode); rpc GetCommittee(GetCommitteeRequest) returns (GetCommitteeResponse); - + rpc GetShardKey(GetShardKeyRequest) returns (GetShardKeyResponse); } message GetAssetMetadataRequest { @@ -455,4 +455,13 @@ message GetCommitteeRequest { message GetCommitteeResponse { repeated bytes public_key = 1; +} + +message GetShardKeyRequest { + uint64 height = 1; + bytes public_key = 2; +} + +message GetShardKeyResponse { + bytes shard_key = 1; } \ No newline at end of file diff --git a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs index a7b70eb5bc..d10aea2bd2 100644 --- a/applications/tari_base_node/src/grpc/base_node_grpc_server.rs +++ b/applications/tari_base_node/src/grpc/base_node_grpc_server.rs @@ -33,7 +33,7 @@ use tari_app_grpc::{ tari_rpc::{CalcType, Sorting}, }; use tari_app_utilities::consts; -use tari_common_types::types::{Commitment, Signature}; +use tari_common_types::types::{Commitment, PublicKey, Signature}; use tari_comms::{Bytes, CommsNode}; use tari_core::{ base_node::{ @@ -1599,6 +1599,24 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { Ok(Response::new(tari_rpc::GetCommitteeResponse { public_key: response })) } + async fn get_shard_key( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + let report_error_flag = self.report_error_flag(); + let mut handler = self.node_service.clone(); + let public_key = PublicKey::from_bytes(&request.public_key) + .map_err(|e| report_error(report_error_flag, Status::invalid_argument(e.to_string())))?; + let shard_key = handler.get_shard_key(request.height, public_key).await.map_err(|e| { + error!(target: LOG_TARGET, "Error {}", e); + report_error(report_error_flag, Status::internal(e.to_string())) + })?; + Ok(Response::new(tari_rpc::GetShardKeyResponse { + shard_key: shard_key.to_vec(), + })) + } + async fn get_active_validator_nodes( &self, request: Request, diff --git a/base_layer/core/src/base_node/comms_interface/comms_request.rs b/base_layer/core/src/base_node/comms_interface/comms_request.rs index 9c77b2bf1f..817438bc05 100644 --- a/base_layer/core/src/base_node/comms_interface/comms_request.rs +++ b/base_layer/core/src/base_node/comms_interface/comms_request.rs @@ -26,7 +26,7 @@ use std::{ }; use serde::{Deserialize, Serialize}; -use tari_common_types::types::{Commitment, HashOutput, PrivateKey, Signature}; +use tari_common_types::types::{Commitment, HashOutput, PrivateKey, PublicKey, Signature}; use tari_utilities::hex::Hex; use crate::{blocks::NewBlockTemplate, chain_storage::MmrTree, proof_of_work::PowAlgorithm}; @@ -58,6 +58,7 @@ pub enum NodeCommsRequest { FetchMempoolTransactionsByExcessSigs { excess_sigs: Vec }, FetchValidatorNodesKeys { height: u64 }, FetchCommittee { height: u64, shard: [u8; 32] }, + GetShardKey { height: u64, public_key: PublicKey }, } #[derive(Debug, Serialize, Deserialize)] @@ -102,6 +103,9 @@ impl Display for NodeCommsRequest { FetchCommittee { height, shard } => { write!(f, "FetchCommittee height ({}), shard({:?})", height, shard) }, + GetShardKey { height, public_key } => { + write!(f, "GetShardKey height ({}), public key ({:?})", height, public_key) + }, } } } diff --git a/base_layer/core/src/base_node/comms_interface/comms_response.rs b/base_layer/core/src/base_node/comms_interface/comms_response.rs index 9dde44f55c..6298ba8cc7 100644 --- a/base_layer/core/src/base_node/comms_interface/comms_response.rs +++ b/base_layer/core/src/base_node/comms_interface/comms_response.rs @@ -73,6 +73,7 @@ pub enum NodeCommsResponse { }, FetchValidatorNodesKeysResponse(Vec), FetchCommitteeResponse(Vec), + GetShardKeyResponse([u8; 32]), } impl Display for NodeCommsResponse { @@ -113,6 +114,7 @@ impl Display for NodeCommsResponse { FetchOutputsByContractIdResponse { .. } => write!(f, "FetchOutputsByContractIdResponse"), FetchValidatorNodesKeysResponse(_) => write!(f, "FetchValidatorNodesKeysResponse"), FetchCommitteeResponse(_) => write!(f, "FetchCommitteeResponse"), + GetShardKeyResponse(_) => write!(f, "GetShardKeyResponse"), } } } diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs index c5419cc62e..ce6e4f221c 100644 --- a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -373,6 +373,10 @@ where B: BlockchainBackend + 'static let validator_nodes = self.blockchain_db.fetch_committee(height, shard).await?; Ok(NodeCommsResponse::FetchCommitteeResponse(validator_nodes)) }, + NodeCommsRequest::GetShardKey { height, public_key } => { + let shard_key = self.blockchain_db.get_shard_key(height, public_key).await?; + Ok(NodeCommsResponse::GetShardKeyResponse(shard_key)) + }, } } diff --git a/base_layer/core/src/base_node/comms_interface/local_interface.rs b/base_layer/core/src/base_node/comms_interface/local_interface.rs index b51373f218..be699e725d 100644 --- a/base_layer/core/src/base_node/comms_interface/local_interface.rs +++ b/base_layer/core/src/base_node/comms_interface/local_interface.rs @@ -24,7 +24,7 @@ use std::{ops::RangeInclusive, sync::Arc}; use tari_common_types::{ chain_metadata::ChainMetadata, - types::{BlockHash, Commitment, HashOutput, Signature}, + types::{BlockHash, Commitment, HashOutput, PublicKey, Signature}, }; use tari_service_framework::{reply_channel::SenderService, Service}; use tokio::sync::broadcast; @@ -301,4 +301,15 @@ impl LocalNodeCommsInterface { _ => Err(CommsInterfaceError::UnexpectedApiResponse), } } + + pub async fn get_shard_key(&mut self, height: u64, public_key: PublicKey) -> Result<[u8; 32], CommsInterfaceError> { + match self + .request_sender + .call(NodeCommsRequest::GetShardKey { height, public_key }) + .await?? + { + NodeCommsResponse::GetShardKeyResponse(shard_key) => Ok(shard_key), + _ => Err(CommsInterfaceError::UnexpectedApiResponse), + } + } } diff --git a/base_layer/core/src/chain_storage/async_db.rs b/base_layer/core/src/chain_storage/async_db.rs index 42940705f5..7e03494073 100644 --- a/base_layer/core/src/chain_storage/async_db.rs +++ b/base_layer/core/src/chain_storage/async_db.rs @@ -26,7 +26,7 @@ use log::*; use rand::{rngs::OsRng, RngCore}; use tari_common_types::{ chain_metadata::ChainMetadata, - types::{BlockHash, Commitment, HashOutput, Signature}, + types::{BlockHash, Commitment, HashOutput, PublicKey, Signature}, }; use tari_utilities::epoch_time::EpochTime; @@ -271,6 +271,8 @@ impl AsyncBlockchainDb { make_async_fn!(fetch_committee(height: u64, shard: [u8;32]) -> Vec, "fetch_committee"); make_async_fn!(get_validator_nodes_mr() -> Vec, "get_validator_nodes_mr"); + + make_async_fn!(get_shard_key(height:u64, public_key:PublicKey) -> [u8;32], "get_shard_key"); } impl From> for AsyncBlockchainDb { diff --git a/base_layer/core/src/chain_storage/blockchain_backend.rs b/base_layer/core/src/chain_storage/blockchain_backend.rs index 08478a13a3..c7f0d72626 100644 --- a/base_layer/core/src/chain_storage/blockchain_backend.rs +++ b/base_layer/core/src/chain_storage/blockchain_backend.rs @@ -4,7 +4,7 @@ use croaring::Bitmap; use tari_common_types::{ chain_metadata::ChainMetadata, - types::{Commitment, HashOutput, Signature}, + types::{Commitment, HashOutput, PublicKey, Signature}, }; use super::ActiveValidatorNode; @@ -195,4 +195,5 @@ pub trait BlockchainBackend: Send + Sync { fn fetch_active_validator_nodes(&self, height: u64) -> Result, ChainStorageError>; fn fetch_committee(&self, height: u64, shard: [u8; 32]) -> Result, ChainStorageError>; + fn get_shard_key(&self, height: u64, public_key: PublicKey) -> Result<[u8; 32], ChainStorageError>; } diff --git a/base_layer/core/src/chain_storage/blockchain_database.rs b/base_layer/core/src/chain_storage/blockchain_database.rs index b32a21976b..4dc5195e36 100644 --- a/base_layer/core/src/chain_storage/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/blockchain_database.rs @@ -36,7 +36,7 @@ use log::*; use serde::{Deserialize, Serialize}; use tari_common_types::{ chain_metadata::ChainMetadata, - types::{BlockHash, Commitment, FixedHash, HashOutput, Signature}, + types::{BlockHash, Commitment, FixedHash, HashOutput, PublicKey, Signature}, }; use tari_mmr::pruned_hashset::PrunedHashSet; use tari_utilities::{epoch_time::EpochTime, hex::Hex, ByteArray}; @@ -848,6 +848,11 @@ where B: BlockchainBackend Ok(mmr.get_merkle_root().unwrap()) } + pub fn get_shard_key(&self, height: u64, public_key: PublicKey) -> Result<[u8; 32], ChainStorageError> { + let db = self.db_read_access()?; + db.get_shard_key(height, public_key) + } + /// Tries to add a block to the longest chain. /// /// The block is added to the longest chain if and only if diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs index 6cf12d69bd..d3a448a6f7 100644 --- a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs @@ -182,7 +182,7 @@ pub fn create_lmdb_database>( .add_database(LMDB_DB_ORPHAN_PARENT_MAP_INDEX, flags | db::DUPSORT) .add_database(LMDB_DB_BAD_BLOCK_LIST, flags) .add_database(LMDB_DB_REORGS, flags | db::INTEGERKEY) - .add_database(LMDB_DB_VALIDATOR_NODES, flags) + .add_database(LMDB_DB_VALIDATOR_NODES, flags | db::DUPSORT) .add_database(LMDB_DB_VALIDATOR_NODES_MAPPING, flags | db::DUPSORT) .build() .map_err(|err| ChainStorageError::CriticalError(format!("Could not create LMDB store:{}", err)))?; @@ -2471,6 +2471,21 @@ impl BlockchainBackend for LMDBDatabase { } Ok(result) } + + fn get_shard_key(&self, height: u64, public_key: PublicKey) -> Result<[u8; 32], ChainStorageError> { + let txn = self.read_transaction()?; + let validator_nodes: Vec = + lmdb_get_multiple(&txn, &self.validator_nodes, public_key.as_bytes())?; + validator_nodes + .iter() + .find(|a| a.from_height <= height && height <= a.to_height) + .map(|a| a.shard_key) + .ok_or(ChainStorageError::ValueNotFound { + entity: "ShardKey", + field: "public_key", + value: public_key.to_hex(), + }) + } } // Fetch the chain metadata diff --git a/base_layer/core/src/lib.rs b/base_layer/core/src/lib.rs index 0deca2dcef..a00af389af 100644 --- a/base_layer/core/src/lib.rs +++ b/base_layer/core/src/lib.rs @@ -108,6 +108,14 @@ mod domain_hashing { ); pub type InputMmrHasherBlake256 = DomainSeparatedHasher; pub type PrunedInputMmr = MerkleMountainRange; +} + +#[cfg(feature = "base_node")] +pub use domain_hashing::*; + +mod validator_domain_hashing { + use tari_crypto::{hash::blake2::Blake256, hash_domain, hashing::DomainSeparatedHasher}; + use tari_mmr::{Hash, MerkleMountainRange}; hash_domain!( ValidatorNodeMmrHashDomain, @@ -117,5 +125,5 @@ mod domain_hashing { pub type ValidatorNodeMmrHasherBlake256 = DomainSeparatedHasher; pub type ValidatorNodeMmr = MerkleMountainRange>; } -#[cfg(feature = "base_node")] -pub use domain_hashing::*; + +pub use validator_domain_hashing::*; diff --git a/base_layer/core/src/test_helpers/blockchain.rs b/base_layer/core/src/test_helpers/blockchain.rs index 92579237d4..c37c03cd05 100644 --- a/base_layer/core/src/test_helpers/blockchain.rs +++ b/base_layer/core/src/test_helpers/blockchain.rs @@ -32,7 +32,7 @@ use croaring::Bitmap; use tari_common::configuration::Network; use tari_common_types::{ chain_metadata::ChainMetadata, - types::{Commitment, HashOutput, Signature}, + types::{Commitment, HashOutput, PublicKey, Signature}, }; use tari_storage::lmdb_store::LMDBConfig; use tari_test_utils::paths::create_temporary_data_path; @@ -424,6 +424,10 @@ impl BlockchainBackend for TempDatabase { fn fetch_committee(&self, height: u64, shard: [u8; 32]) -> Result, ChainStorageError> { self.db.as_ref().unwrap().fetch_committee(height, shard) } + + fn get_shard_key(&self, height: u64, public_key: PublicKey) -> Result<[u8; 32], ChainStorageError> { + self.db.as_ref().unwrap().get_shard_key(height, public_key) + } } pub fn create_chained_blocks>(