diff --git a/applications/tari_app_grpc/proto/base_node.proto b/applications/tari_app_grpc/proto/base_node.proto index 957176e14b..4b61816a22 100644 --- a/applications/tari_app_grpc/proto/base_node.proto +++ b/applications/tari_app_grpc/proto/base_node.proto @@ -25,6 +25,7 @@ import "types.proto"; import "transaction.proto"; import "block.proto"; import "network.proto"; +import "sidechain_types.proto"; package tari.rpc; @@ -92,6 +93,8 @@ service BaseNode { rpc GetActiveValidatorNodes(GetActiveValidatorNodesRequest) returns (stream GetActiveValidatorNodesResponse); rpc GetCommittee(GetCommitteeRequest) returns (GetCommitteeResponse); rpc GetShardKey(GetShardKeyRequest) returns (GetShardKeyResponse); + // Get templates + rpc GetTemplateRegistrations(GetTemplateRegistrationsRequest) returns (stream TemplateRegistration); } message GetAssetMetadataRequest { @@ -465,3 +468,7 @@ message GetShardKeyRequest { message GetShardKeyResponse { bytes shard_key = 1; } + +message GetTemplateRegistrationsRequest { + uint64 from_height = 1; +} 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 61666eac5d..09bd6783b7 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 @@ -140,6 +140,7 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { type GetMempoolTransactionsStream = mpsc::Receiver>; type GetNetworkDifficultyStream = mpsc::Receiver>; type GetPeersStream = mpsc::Receiver>; + type GetTemplateRegistrationsStream = mpsc::Receiver>; type GetTokensInCirculationStream = mpsc::Receiver>; type ListHeadersStream = mpsc::Receiver>; type SearchKernelsStream = mpsc::Receiver>; @@ -1488,7 +1489,7 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { }, Ok(data) => data, }; - dbg!(&active_validator_nodes); + // dbg!(&active_validator_nodes); for (public_key, shard_key) in active_validator_nodes { let active_validator_node = tari_rpc::GetActiveValidatorNodesResponse { public_key: public_key.to_vec(), @@ -1525,6 +1526,81 @@ impl tari_rpc::base_node_server::BaseNode for BaseNodeGrpcServer { ); Ok(Response::new(rx)) } + + async fn get_template_registrations( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + let report_error_flag = self.report_error_flag(); + debug!(target: LOG_TARGET, "Incoming GRPC request for GetTemplateRegistrations"); + + let mut handler = self.node_service.clone(); + let (mut tx, rx) = mpsc::channel(1000); + + task::spawn(async move { + let template_registrations = match handler.get_template_registrations(request.from_height).await { + Err(err) => { + warn!(target: LOG_TARGET, "Error communicating with base node: {}", err,); + return; + }, + Ok(data) => data, + }; + + for template_registration in template_registrations { + let template_registration = match tari_rpc::TemplateRegistration::try_from(template_registration) { + Ok(t) => t, + Err(e) => { + warn!( + target: LOG_TARGET, + "Error sending converting template registration for GRPC: {}", e + ); + match tx + .send(Err(obscure_error_if_true( + report_error_flag, + Status::internal("Error converting template_registration"), + ))) + .await + { + Ok(_) => (), + Err(send_err) => { + warn!(target: LOG_TARGET, "Error sending error to GRPC client: {}", send_err) + }, + } + return; + }, + }; + + match tx.send(Ok(template_registration)).await { + Ok(_) => (), + Err(err) => { + warn!( + target: LOG_TARGET, + "Error sending template registration via GRPC: {}", err + ); + match tx + .send(Err(obscure_error_if_true( + report_error_flag, + Status::unknown("Error sending data"), + ))) + .await + { + Ok(_) => (), + Err(send_err) => { + warn!(target: LOG_TARGET, "Error sending error to GRPC client: {}", send_err) + }, + } + return; + }, + } + } + }); + debug!( + target: LOG_TARGET, + "Sending GetTemplateRegistrations response stream to client" + ); + Ok(Response::new(rx)) + } } enum BlockGroupType { 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 817438bc05..a46e08df52 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 @@ -59,6 +59,7 @@ pub enum NodeCommsRequest { FetchValidatorNodesKeys { height: u64 }, FetchCommittee { height: u64, shard: [u8; 32] }, GetShardKey { height: u64, public_key: PublicKey }, + FetchTemplateRegistrations { from_height: u64 }, } #[derive(Debug, Serialize, Deserialize)] @@ -106,6 +107,9 @@ impl Display for NodeCommsRequest { GetShardKey { height, public_key } => { write!(f, "GetShardKey height ({}), public key ({:?})", height, public_key) }, + FetchTemplateRegistrations { from_height } => { + write!(f, "FetchTemplateRegistrations ({})", from_height) + }, } } } 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 214b00216e..9dd0d6b360 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 @@ -34,7 +34,12 @@ use crate::{ blocks::{Block, ChainHeader, HistoricalBlock, NewBlockTemplate}, chain_storage::{ActiveValidatorNode, UtxoMinedInfo}, proof_of_work::Difficulty, - transactions::transaction_components::{Transaction, TransactionKernel, TransactionOutput}, + transactions::transaction_components::{ + CodeTemplateRegistration, + Transaction, + TransactionKernel, + TransactionOutput, + }, }; /// API Response enum @@ -74,6 +79,7 @@ pub enum NodeCommsResponse { FetchValidatorNodesKeysResponse(Vec<(PublicKey, [u8; 32])>), FetchCommitteeResponse(Vec), GetShardKeyResponse([u8; 32]), + FetchTemplateRegistrationsResponse(Vec), } impl Display for NodeCommsResponse { @@ -115,6 +121,7 @@ impl Display for NodeCommsResponse { FetchValidatorNodesKeysResponse(_) => write!(f, "FetchValidatorNodesKeysResponse"), FetchCommitteeResponse(_) => write!(f, "FetchCommitteeResponse"), GetShardKeyResponse(_) => write!(f, "GetShardKeyResponse"), + FetchTemplateRegistrationsResponse(_) => write!(f, "FetchTemplateRegistrationsResponse"), } } } 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 b56d1ee94d..b1a72dc3c1 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 @@ -376,6 +376,18 @@ where B: BlockchainBackend + 'static let shard_key = self.blockchain_db.get_shard_key(height, public_key).await?; Ok(NodeCommsResponse::GetShardKeyResponse(shard_key)) }, + NodeCommsRequest::FetchTemplateRegistrations { from_height } => { + let template_registrations = self + .blockchain_db + .fetch_template_registrations(from_height) + .await? + .into_iter() + .map(|tr| tr.registration_data) + .collect(); + Ok(NodeCommsResponse::FetchTemplateRegistrationsResponse( + template_registrations, + )) + }, } } 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 ada66a3644..6f283ba0bd 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 @@ -40,7 +40,7 @@ use crate::{ blocks::{Block, ChainHeader, HistoricalBlock, NewBlockTemplate}, chain_storage::ActiveValidatorNode, proof_of_work::PowAlgorithm, - transactions::transaction_components::{TransactionKernel, TransactionOutput}, + transactions::transaction_components::{CodeTemplateRegistration, TransactionKernel, TransactionOutput}, }; pub type BlockEventSender = broadcast::Sender>; @@ -312,4 +312,18 @@ impl LocalNodeCommsInterface { _ => Err(CommsInterfaceError::UnexpectedApiResponse), } } + + pub async fn get_template_registrations( + &mut self, + from_height: u64, + ) -> Result, CommsInterfaceError> { + match self + .request_sender + .call(NodeCommsRequest::FetchTemplateRegistrations { from_height }) + .await?? + { + NodeCommsResponse::FetchTemplateRegistrationsResponse(template_registrations) => Ok(template_registrations), + _ => 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 14ad5b9c35..b25228a46a 100644 --- a/base_layer/core/src/chain_storage/async_db.rs +++ b/base_layer/core/src/chain_storage/async_db.rs @@ -30,7 +30,7 @@ use tari_common_types::{ }; use tari_utilities::epoch_time::EpochTime; -use super::ActiveValidatorNode; +use super::{ActiveValidatorNode, TemplateRegistration}; use crate::{ blocks::{ Block, @@ -270,6 +270,8 @@ impl AsyncBlockchainDb { make_async_fn!(fetch_committee(height: u64, shard: [u8;32]) -> Vec, "fetch_committee"); make_async_fn!(get_shard_key(height:u64, public_key: PublicKey) -> [u8;32], "get_shard_key"); + + make_async_fn!(fetch_template_registrations(from_height: u64) -> Vec, "fetch_template_registrations"); } 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 fbc8dc1b4d..b30e280daa 100644 --- a/base_layer/core/src/chain_storage/blockchain_backend.rs +++ b/base_layer/core/src/chain_storage/blockchain_backend.rs @@ -7,7 +7,7 @@ use tari_common_types::{ types::{Commitment, HashOutput, PublicKey, Signature}, }; -use super::ActiveValidatorNode; +use super::{ActiveValidatorNode, TemplateRegistration}; use crate::{ blocks::{ Block, @@ -196,4 +196,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>; + fn fetch_template_registrations(&self, from_height: u64) -> Result, ChainStorageError>; } diff --git a/base_layer/core/src/chain_storage/blockchain_database.rs b/base_layer/core/src/chain_storage/blockchain_database.rs index 59d54b4c23..d52954c388 100644 --- a/base_layer/core/src/chain_storage/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/blockchain_database.rs @@ -41,7 +41,7 @@ use tari_common_types::{ use tari_mmr::pruned_hashset::PrunedHashSet; use tari_utilities::{epoch_time::EpochTime, hex::Hex, ByteArray}; -use super::ActiveValidatorNode; +use super::{ActiveValidatorNode, TemplateRegistration}; use crate::{ blocks::{ Block, @@ -1181,6 +1181,14 @@ where B: BlockchainBackend let db = self.db_read_access()?; db.fetch_committee(height, shard) } + + pub fn fetch_template_registrations( + &self, + from_height: u64, + ) -> Result, ChainStorageError> { + let db = self.db_read_access()?; + db.fetch_template_registrations(from_height) + } } fn unexpected_result(request: DbKey, response: DbValue) -> Result { diff --git a/base_layer/core/src/chain_storage/db_transaction.rs b/base_layer/core/src/chain_storage/db_transaction.rs index 6eb2ed3f4f..e0cf01008e 100644 --- a/base_layer/core/src/chain_storage/db_transaction.rs +++ b/base_layer/core/src/chain_storage/db_transaction.rs @@ -30,7 +30,7 @@ use croaring::Bitmap; use tari_common_types::types::{BlockHash, Commitment, HashOutput}; use tari_utilities::hex::Hex; -use super::ActiveValidatorNode; +use super::{ActiveValidatorNode, TemplateRegistration}; use crate::{ blocks::{Block, BlockHeader, BlockHeaderAccumulatedData, ChainBlock, ChainHeader, UpdateBlockAccumulatedData}, chain_storage::{error::ChainStorageError, HorizonData, Reorg}, @@ -362,6 +362,9 @@ pub enum WriteOperation { InsertValidatorNode { validator_node: ActiveValidatorNode, }, + InsertTemplateRegistration { + template_registration: TemplateRegistration, + }, } impl fmt::Display for WriteOperation { @@ -461,6 +464,9 @@ impl fmt::Display for WriteOperation { InsertValidatorNode { validator_node } => { write!(f, "Inserting VN {:?}", validator_node) }, + InsertTemplateRegistration { template_registration } => { + write!(f, "Inserting Template {:?}", template_registration) + }, } } } 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 977b381319..50238f03ce 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 @@ -103,6 +103,7 @@ use crate::{ MmrTree, PrunedOutput, Reorg, + TemplateRegistration, }, consensus::ConsensusManager, transactions::{ @@ -145,6 +146,7 @@ const LMDB_DB_REORGS: &str = "reorgs"; const LMDB_DB_VALIDATOR_NODES: &str = "validator_nodes"; const LMDB_DB_VALIDATOR_NODES_MAPPING: &str = "validator_nodes_mapping"; const LMDB_DB_VALIDATOR_NODE_ENDING: &str = "validator_node_ending"; +const LMDB_DB_TEMPLATE_REGISTRATIONS: &str = "template_registrations"; pub fn create_lmdb_database>( path: P, @@ -190,6 +192,7 @@ pub fn create_lmdb_database>( .add_database(LMDB_DB_VALIDATOR_NODES, flags | db::DUPSORT) .add_database(LMDB_DB_VALIDATOR_NODES_MAPPING, flags | db::DUPSORT) .add_database(LMDB_DB_VALIDATOR_NODE_ENDING, flags | db::INTEGERKEY | db::DUPSORT) + .add_database(LMDB_DB_TEMPLATE_REGISTRATIONS, flags | db::DUPSORT) .build() .map_err(|err| ChainStorageError::CriticalError(format!("Could not create LMDB store:{}", err)))?; debug!(target: LOG_TARGET, "LMDB database creation successful"); @@ -254,6 +257,8 @@ pub struct LMDBDatabase { validator_nodes_mapping: DatabaseRef, /// Maps the end block height of nodes validator_nodes_ending: DatabaseRef, + /// Maps CodeTemplateRegistration hash-> TemplateRegistration + template_registrations: DatabaseRef, _file_lock: Arc, consensus_manager: ConsensusManager, } @@ -297,6 +302,7 @@ impl LMDBDatabase { validator_nodes: get_database(store, LMDB_DB_VALIDATOR_NODES)?, validator_nodes_mapping: get_database(store, LMDB_DB_VALIDATOR_NODES_MAPPING)?, validator_nodes_ending: get_database(store, LMDB_DB_VALIDATOR_NODE_ENDING)?, + template_registrations: get_database(store, LMDB_DB_TEMPLATE_REGISTRATIONS)?, env, env_config: store.env_config(), _file_lock: Arc::new(file_lock), @@ -502,6 +508,9 @@ impl LMDBDatabase { InsertValidatorNode { validator_node } => { self.insert_validator_node(&write_txn, validator_node)?; }, + InsertTemplateRegistration { template_registration } => { + self.insert_template_registration(&write_txn, template_registration)?; + }, } } write_txn.commit()?; @@ -509,7 +518,7 @@ impl LMDBDatabase { Ok(()) } - fn all_dbs(&self) -> [(&'static str, &DatabaseRef); 26] { + fn all_dbs(&self) -> [(&'static str, &DatabaseRef); 27] { [ ("metadata_db", &self.metadata_db), ("headers_db", &self.headers_db), @@ -543,6 +552,7 @@ impl LMDBDatabase { ("reorgs", &self.reorgs), ("validator_nodes", &self.validator_nodes), ("validator_nodes_mapping", &self.validator_nodes_mapping), + ("template_registrations", &self.template_registrations), ] } @@ -1326,6 +1336,19 @@ impl LMDBDatabase { }; self.insert_validator_node(txn, &validator_node)?; } + if let Some(template_reg) = output + .features + .sidechain_feature + .as_ref() + .and_then(|f| f.template_registration()) + { + let record = TemplateRegistration { + registration_data: template_reg.clone(), + height: header.height, + }; + + self.insert_template_registration(txn, &record)?; + } self.insert_output( txn, &block_hash, @@ -1616,6 +1639,21 @@ impl LMDBDatabase { Ok(()) } + fn insert_template_registration( + &self, + txn: &WriteTransaction<'_>, + template_registration: &TemplateRegistration, + ) -> Result<(), ChainStorageError> { + let key = template_registration.registration_data.hash(); + lmdb_insert( + txn, + &self.template_registrations, + key.as_bytes(), + template_registration, + "template_registrations", + ) + } + fn fetch_output_in_txn( &self, txn: &ConstTransaction<'_>, @@ -2538,6 +2576,18 @@ impl BlockchainBackend for LMDBDatabase { value: public_key.to_hex(), }) } + + fn fetch_template_registrations(&self, from_height: u64) -> Result, ChainStorageError> { + // TODO: we can optimise this query by making using a compound key + let txn = self.read_transaction()?; + lmdb_filter_map_values(&txn, &self.template_registrations, |tr: TemplateRegistration| { + if tr.height >= from_height { + Some(tr) + } else { + None + } + }) + } } // Fetch the chain metadata diff --git a/base_layer/core/src/chain_storage/mod.rs b/base_layer/core/src/chain_storage/mod.rs index d374dccf1b..aa65f98a95 100644 --- a/base_layer/core/src/chain_storage/mod.rs +++ b/base_layer/core/src/chain_storage/mod.rs @@ -82,3 +82,6 @@ pub use utxo_mined_info::*; mod active_validator_node; pub use active_validator_node::ActiveValidatorNode; + +mod template_registation; +pub use template_registation::TemplateRegistration; diff --git a/base_layer/core/src/chain_storage/template_registation.rs b/base_layer/core/src/chain_storage/template_registation.rs new file mode 100644 index 0000000000..452fc02ef6 --- /dev/null +++ b/base_layer/core/src/chain_storage/template_registation.rs @@ -0,0 +1,31 @@ +// Copyright 2022, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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 serde::{Deserialize, Serialize}; + +use crate::transactions::transaction_components::CodeTemplateRegistration; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct TemplateRegistration { + pub registration_data: CodeTemplateRegistration, + pub height: u64, +} diff --git a/base_layer/core/src/test_helpers/blockchain.rs b/base_layer/core/src/test_helpers/blockchain.rs index 88b7e9204c..655e5a1e54 100644 --- a/base_layer/core/src/test_helpers/blockchain.rs +++ b/base_layer/core/src/test_helpers/blockchain.rs @@ -67,6 +67,7 @@ use crate::{ MmrTree, PrunedOutput, Reorg, + TemplateRegistration, UtxoMinedInfo, Validators, }, @@ -425,6 +426,10 @@ impl BlockchainBackend for TempDatabase { 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) } + + fn fetch_template_registrations(&self, from_height: u64) -> Result, ChainStorageError> { + self.db.as_ref().unwrap().fetch_template_registrations(from_height) + } } pub fn create_chained_blocks>( diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/template_registration.rs b/base_layer/core/src/transactions/transaction_components/side_chain/template_registration.rs index 83f67156f6..8f3290cd1e 100644 --- a/base_layer/core/src/transactions/transaction_components/side_chain/template_registration.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/template_registration.rs @@ -23,15 +23,19 @@ use std::io::{Error, ErrorKind, Read, Write}; use serde::{Deserialize, Serialize}; -use tari_common_types::types::{PublicKey, Signature}; - -use crate::consensus::{ - read_byte, - ConsensusDecoding, - ConsensusEncoding, - ConsensusEncodingSized, - MaxSizeBytes, - MaxSizeString, +use tari_common_types::types::{FixedHash, PublicKey, Signature}; + +use crate::{ + consensus::{ + read_byte, + ConsensusDecoding, + ConsensusEncoding, + ConsensusEncodingSized, + DomainSeparatedConsensusHasher, + MaxSizeBytes, + MaxSizeString, + }, + transactions::TransactionHashDomain, }; #[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] @@ -46,6 +50,22 @@ pub struct CodeTemplateRegistration { pub binary_url: MaxSizeString<255>, } +impl CodeTemplateRegistration { + pub fn hash(&self) -> FixedHash { + DomainSeparatedConsensusHasher::::new("template_registration") + .chain(&self.author_public_key) + .chain(&self.author_signature) + .chain(&self.template_name) + .chain(&self.template_version) + .chain(&self.template_type) + .chain(&self.build_info) + .chain(&self.binary_sha) + .chain(&self.binary_url) + .finalize() + .into() + } +} + impl ConsensusEncoding for CodeTemplateRegistration { fn consensus_encode(&self, writer: &mut W) -> Result<(), Error> { self.author_public_key.consensus_encode(writer)?;