From 0a7208300c71e079ca230e9ac32d4976a049d990 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Tue, 4 Apr 2023 07:46:13 +0100 Subject: [PATCH 1/3] add needed rpc functions --- packages/rs-drive-abci/src/rpc/core.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/rs-drive-abci/src/rpc/core.rs b/packages/rs-drive-abci/src/rpc/core.rs index 1e000a01a3d..44f06237b00 100644 --- a/packages/rs-drive-abci/src/rpc/core.rs +++ b/packages/rs-drive-abci/src/rpc/core.rs @@ -1,10 +1,16 @@ use dashcore::{Block, BlockHash}; +<<<<<<< HEAD use dashcore_rpc::dashcore_rpc_json::{ ExtendedQuorumDetails, GetBestChainLockResult, QuorumHash, QuorumInfoResult, QuorumListResult, QuorumType, }; use dashcore_rpc::Error; use dashcore_rpc::{Auth, Client, RpcApi}; +======= +use dashcore_rpc::{Auth, Client, Error, RpcApi}; +use dashcore_rpc::json::{MasternodeListDiff, ProTxHash, ProTxInfo}; +#[cfg(feature = "fixtures-and-mocks")] +>>>>>>> b9879e802 (add needed rpc functions) use mockall::{automock, predicate::*}; use serde_json::Value; use std::collections::HashMap; @@ -47,6 +53,12 @@ pub trait CoreRPCLike { hash: &QuorumHash, o: Option, ) -> Result; + + /// Get the diff and proof between two masternode lists + fn get_protx_diff(&self, base_block: u32, block: u32) -> Result; + + /// Get the detailed information about a deterministic masternode + fn get_protx_info(&self, protx_hash: &ProTxHash) -> Result; } /// Default implementation of Dash Core RPC using DashCoreRPC client @@ -89,6 +101,7 @@ impl CoreRPCLike for DefaultCoreRPC { fn get_block_json(&self, block_hash: &BlockHash) -> Result { self.inner.get_block_json(block_hash) } + fn get_quorum_listextended( &self, height: Option, @@ -105,4 +118,12 @@ impl CoreRPCLike for DefaultCoreRPC { self.inner .get_quorum_info(quorum_type, quorum_hash, include_sk_share) } + + fn get_protx_diff(&self, base_block: u32, block: u32) -> Result { + self.inner.get_protx_diff(base_block, block) + } + + fn get_protx_info(&self, protx_hash: &ProTxHash) -> Result { + self.inner.get_protx_info(protx_hash) + } } From 08d319c25d0ada38dde61359c43c357a83cbcb13 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Tue, 4 Apr 2023 13:18:12 +0100 Subject: [PATCH 2/3] add rpc functions --- packages/rs-drive-abci/src/rpc/core.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/rs-drive-abci/src/rpc/core.rs b/packages/rs-drive-abci/src/rpc/core.rs index 44f06237b00..5f83f760498 100644 --- a/packages/rs-drive-abci/src/rpc/core.rs +++ b/packages/rs-drive-abci/src/rpc/core.rs @@ -1,16 +1,10 @@ use dashcore::{Block, BlockHash}; -<<<<<<< HEAD use dashcore_rpc::dashcore_rpc_json::{ ExtendedQuorumDetails, GetBestChainLockResult, QuorumHash, QuorumInfoResult, QuorumListResult, QuorumType, }; -use dashcore_rpc::Error; -use dashcore_rpc::{Auth, Client, RpcApi}; -======= use dashcore_rpc::{Auth, Client, Error, RpcApi}; use dashcore_rpc::json::{MasternodeListDiff, ProTxHash, ProTxInfo}; -#[cfg(feature = "fixtures-and-mocks")] ->>>>>>> b9879e802 (add needed rpc functions) use mockall::{automock, predicate::*}; use serde_json::Value; use std::collections::HashMap; From a65ce917d222cc22301d20113e425167c7e672f3 Mon Sep 17 00:00:00 2001 From: Wisdom Ogwu Date: Wed, 12 Apr 2023 09:34:19 +0100 Subject: [PATCH 3/3] more work --- Cargo.lock | 1 + packages/rs-drive-abci/Cargo.toml | 1 + .../rs-drive-abci/src/execution/engine.rs | 23 +- .../src/execution/masternode_identities.rs | 338 ++++++++++++++++++ packages/rs-drive-abci/src/execution/mod.rs | 2 + packages/rs-drive-abci/src/lib.rs | 2 + packages/rs-drive-abci/src/rpc/core.rs | 2 +- 7 files changed, 353 insertions(+), 16 deletions(-) create mode 100644 packages/rs-drive-abci/src/execution/masternode_identities.rs diff --git a/Cargo.lock b/Cargo.lock index 9dd5e00e681..4c927fb79c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1182,6 +1182,7 @@ dependencies = [ "serde", "serde_json", "serde_with", + "sha2 0.10.6", "structopt", "tempfile", "tenderdash-abci", diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index c0a16bc649d..5abf13ad705 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -55,6 +55,7 @@ tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", branc # tenderdash-abci = { path = "/home/lklimek/git/lklimek/tenderdash-abci-rs/abci", optional = true } anyhow = { version = "1.0.70" } lazy_static = "1.4.0" +sha2 = "0.10.6" [dev-dependencies] itertools = { version = "0.10.5" } diff --git a/packages/rs-drive-abci/src/execution/engine.rs b/packages/rs-drive-abci/src/execution/engine.rs index 223ff5e432e..403c02aff87 100644 --- a/packages/rs-drive-abci/src/execution/engine.rs +++ b/packages/rs-drive-abci/src/execution/engine.rs @@ -1,5 +1,10 @@ +use dashcore_rpc::json::{ProTxHash, QuorumMasternodeListItem}; use dpp::consensus::basic::identity::IdentityInsufficientBalanceError; use dpp::consensus::ConsensusError; +use dpp::identity::factory::IDENTITY_PROTOCOL_VERSION; +use dpp::identity::{Identity, KeyType, Purpose, SecurityLevel}; +use dpp::platform_value::BinaryData; +use dpp::prelude::{Identifier, IdentityPublicKey}; use dpp::state_transition::StateTransition; use dpp::validation::{ ConsensusValidationResult, SimpleConsensusValidationResult, SimpleValidationResult, @@ -9,6 +14,7 @@ use drive::drive::block_info::BlockInfo; use drive::error::Error::GroveDB; use drive::fee::result::FeeResult; use drive::grovedb::{Transaction, TransactionArg}; +use std::collections::BTreeMap; use tenderdash_abci::proto::abci::{ExecTxResult, RequestFinalizeBlock}; use crate::abci::AbciError; @@ -183,20 +189,6 @@ where Ok((aggregate_fee_result, exec_tx_results)) } - /// Update of the masternode identities - pub fn update_masternode_identities( - &self, - previous_core_height: u32, - current_core_height: u32, - ) -> Result<(), Error> { - if previous_core_height != current_core_height { - //todo: - // self.drive.fetch_full_identity() - // self.drive.add_new_non_unique_keys_to_identity() - } - Ok(()) - } - /// Run a block proposal, either from process proposal, or prepare proposal pub fn run_block_proposal( &self, @@ -291,7 +283,8 @@ where transaction, )?; - self.update_masternode_identities(previous_core_height, core_chain_locked_height)?; + // TODO: re-enable + // self.update_masternode_identities(previous_core_height, core_chain_locked_height)?; let root_hash = self .drive diff --git a/packages/rs-drive-abci/src/execution/masternode_identities.rs b/packages/rs-drive-abci/src/execution/masternode_identities.rs new file mode 100644 index 00000000000..ce09e513be3 --- /dev/null +++ b/packages/rs-drive-abci/src/execution/masternode_identities.rs @@ -0,0 +1,338 @@ +use crate::error::Error; +use crate::platform::Platform; +use crate::rpc::core::CoreRPCLike; +use dashcore_rpc::json::{ProTxHash, QuorumMasternodeListItem}; +use dpp::identifier::Identifier; +use dpp::identity::factory::IDENTITY_PROTOCOL_VERSION; +use dpp::identity::{Identity, IdentityPublicKey, KeyType, Purpose, SecurityLevel}; +use dpp::platform_value::BinaryData; +use drive::drive::block_info::BlockInfo; +use drive::grovedb::Transaction; +use sha2::{Digest, Sha256}; +use std::collections::BTreeMap; + +// TODO: clean this file up + +impl Platform +where + C: CoreRPCLike, +{ + + + // TODO: store after identity creation + fn create_owner_identity( + &self, + protx_hash: &[u8], + masternode_identifier: [u8; 32], + ) -> Result { + let mut identity = Self::create_basic_identity(masternode_identifier); + identity.add_public_keys(self.get_owner_identity_key(protx_hash)?); + Ok(identity) + } + + fn get_owner_identity_key(&self, protx_hash: &[u8]) -> Result { + let full_masternode_detail = self + .core_rpc + .get_protx_info(&ProTxHash(protx_hash.to_vec()))?; + Ok(IdentityPublicKey { + id: 0, + key_type: KeyType::ECDSA_HASH160, + purpose: Purpose::WITHDRAW, + security_level: SecurityLevel::MASTER, + read_only: true, + data: BinaryData::new(full_masternode_detail.state.payout_address), + disabled_at: None, + }) + } + + fn create_voter_identity( + &self, + voting_identifier: [u8; 32], + updated_masternode: &QuorumMasternodeListItem, + ) -> Result { + let mut identity = Self::create_basic_identity(voting_identifier); + identity.add_public_keys(self.get_voter_identity_key(&updated_masternode)?); + Ok(identity) + } + + fn get_voter_identity_key( + &self, + updated_masternode: &QuorumMasternodeListItem, + ) -> Result { + Ok(IdentityPublicKey { + id: 0, + key_type: KeyType::ECDSA_HASH160, + purpose: Purpose::WITHDRAW, // todo: is this purpose correct?? + security_level: SecurityLevel::MASTER, + read_only: true, + data: BinaryData::new(updated_masternode.voting_address.clone()), + disabled_at: None, + }) + } + + fn create_operator_identity( + &self, + protx_hash: &[u8], + operator_identifier: [u8; 32], + updated_masternode: &QuorumMasternodeListItem, + ) -> Result { + let mut identity = Self::create_basic_identity(operator_identifier); + identity.add_public_keys(self.get_operator_identity_keys(protx_hash, &updated_masternode)?); + + Ok(identity) + } + + fn get_operator_identity_keys( + &self, + protx_hash: &[u8], + updated_masternode: &QuorumMasternodeListItem, + ) -> Result, Error> { + let full_masternode_detail = self + .core_rpc + .get_protx_info(&ProTxHash(protx_hash.to_vec()))?; + // TODO: js uses something called a Script to convert this to payoutPublicKey, what is this? + let operator_payout_address = full_masternode_detail.state.payout_address; + + Ok(vec![ + IdentityPublicKey { + id: 0, + key_type: KeyType::BLS12_381, + purpose: Purpose::WITHDRAW, // todo: is this purpose correct?? + security_level: SecurityLevel::CRITICAL, + read_only: true, + data: BinaryData::new(updated_masternode.pub_key_operator.clone()), + disabled_at: None, + }, + IdentityPublicKey { + id: 1, + key_type: KeyType::BLS12_381, + purpose: Purpose::WITHDRAW, // todo: is this purpose correct?? + security_level: SecurityLevel::CRITICAL, + read_only: true, + data: BinaryData::new(operator_payout_address), + disabled_at: None, + }, + ]) + } + + fn get_owner_identifier( + &self, + updated_masternode: &QuorumMasternodeListItem, + ) -> Result<[u8; 32], Error> { + // TODO: do proper error handling + let masternode_identifier: [u8; 32] = updated_masternode + .pro_reg_tx_hash + .clone() + .try_into() + .unwrap(); + Ok(masternode_identifier) + } + + fn get_operator_identifier( + protx_hash: &[u8], + updated_masternode: &QuorumMasternodeListItem, + ) -> Result<[u8; 32], Error> { + let operator_pub_key = updated_masternode.pub_key_operator.as_slice(); + let operator_identifier = Self::hash_concat_protxhash(protx_hash, operator_pub_key)?; + Ok(operator_identifier) + } + + fn get_voter_identifier( + protx_hash: &[u8], + updated_masternode: &QuorumMasternodeListItem, + ) -> Result<[u8; 32], Error> { + let voting_address = updated_masternode.voting_address.as_slice(); + let voting_identifier = Self::hash_concat_protxhash(protx_hash, voting_address)?; + Ok(voting_identifier) + } + + fn hash_concat_protxhash(protx_hash: &[u8], key_data: &[u8]) -> Result<[u8; 32], Error> { + let mut hasher = Sha256::new(); + hasher.update(protx_hash); + hasher.update(key_data); + // TODO: handle unwrap, use custom error + Ok(hasher.finalize().try_into().unwrap()) + } + + fn create_basic_identity(id: [u8; 32]) -> Identity { + Identity { + protocol_version: IDENTITY_PROTOCOL_VERSION, + id: Identifier::new(id), + revision: 1, + balance: 0, + asset_lock_proof: None, + metadata: None, + public_keys: BTreeMap::new(), + } + } + + /// Update of the masternode identities + pub fn update_masternode_identities( + &self, + previous_core_height: u32, + current_core_height: u32, + block_info: &BlockInfo, + transaction: &Transaction, + ) -> Result<(), Error> { + if previous_core_height != current_core_height { + let masternode_list_diff = self + .core_rpc + .get_protx_diff(previous_core_height, current_core_height)?; + let updated_masternodes = masternode_list_diff.mn_list; + + for updated_masternode in updated_masternodes { + // Need to get the protx hash for this + let protx_hash = hex::decode(&updated_masternode.pro_reg_tx_hash).unwrap(); + + // TODO: remove duplication + let owner_identifier = + Self::get_owner_identifier(&updated_masternode)?; + let maybe_owner_identity = self + .drive + .fetch_full_identity(owner_identifier, Some(transaction))?; + match maybe_owner_identity { + None => self.create_owner_identity(protx_hash.as_slice(), owner_identifier)?, + Some(owner_identity) => { + let latest_owner_pub_key = self.get_owner_identity_key(protx_hash.as_slice())?; + let mut found = false; + let mut to_disable = vec![]; + // TODO: clean up + for (key, pub_key) in owner_identity.get_public_keys() { + // search the public key + // TODO: impl eq for IdentityPublicKey + if pub_key.data == latest_owner_pub_key.data { + found = true + } else { + to_disable.push(key.clone()) + } + } + if !found { + // disable all current keys + self.drive.disable_identity_keys(voter_identifer, to_disable, todo!(), &block_info, true, Some(transaction)); + // add the new key to the identity + self.drive.add_new_non_unique_keys_to_identity(owner_identifier, vec![latest_owner_pub_key], &block_info, true, Some(transaction)); + } + } + }; + + let voter_identifer = + Self::get_voter_identifier(protx_hash.as_slice(), &updated_masternode)?; + let maybe_voter_identity = self + .drive + .fetch_full_identity(voter_identifer, Some(transaction))?; + match maybe_voter_identity { + None => self.create_voter_identity(voting_identifier, &updated_masternode)?, + Some(voter_identity) => { + // TODO: remove duplication + let latest_voter_public_key = self.get_voter_identity_key(&updated_masternode)?; + let mut found = false; + let mut to_disable = vec![]; + for (key, pub_key) in voter_identity.get_public_keys() { + // search the public key + // TODO: impl eq for IdentityPublicKey + if pub_key.data == latest_voter_public_key.data { + found = true + } else { + to_disable.push(key.clone()) + } + } + if !found { + // disable all current keys + self.drive.disable_identity_keys(voter_identifer, to_disable, todo!(), &block_info, true, Some(transaction)); + // add the new key to the identity + self.drive.add_new_non_unique_keys_to_identity(voter_identifer, vec![latest_voter_public_key], &block_info, true, Some(transaction)); + } + + } + _ => todo!(), + }; + + let operator_identifier = + Self::get_operator_identifier(protx_hash.as_slice(), &updated_masternode)?; + let maybe_operator_identity = self + .drive + .fetch_full_identity(operator_identifier, Some(transaction))?; + let operator_identity = match maybe_operator_identity { + None => self.create_operator_identity( + protx_hash.as_slice(), + operator_identifier, + &updated_masternode, + )?, + _ => todo!(), + }; + + // next up if there is an identity, we need to check if the key is still the same + // if it is not the same, we need to disable that key and then add a new key + // might make sense to extract the key creation functions + // how do we check if a key has changed tho?? + // we need to filter all the keys, for non disabled, then from this new key list + // we need to check if the key we are looking for is there + // if it is not there tho + // when adding a key, we need to use the next available id + + // for x in owner_identity.public_keys.values() { + // + // } + + // need to fetch the identities associated with this masternode + // self.drive.fetch_full_identity(); + // assuming no fetch and just creation + + // // from the masternode, we need to get the identifiers for each type, then check if the key is what we expect + // // TODO: make into owner_identifier function + // // let protx_hash = hex::decode(&updated_masternode.pro_reg_tx_hash).unwrap(); + // let voting_address = updated_masternode.clone().voting_address; + // let mut hasher = Sha256::new(); + // // TODO: remove clone + // hasher.update([protx_hash, voting_address.clone()].concat().as_slice()); + // let voting_identifier: [u8; 32] = hasher.finalize().try_into().unwrap(); + // + // let previous_voter_identity = self + // .drive + // .fetch_full_identity(voting_identifier, Some(transaction))?; + // if previous_voter_identity.is_none() { + // let voter_identity = self.create_voter_identity(updated_masternode.clone())?; + // let _ = self.drive.add_new_identity( + // voter_identity, + // block_info, + // true, + // Some(transaction), + // )?; + // } else { + // // we need to confirm there has been no change in the key + // // previous_voter_identity.unwrap().public_keys.values().fil + // } + // + // // TODO: get rid of clone + // let owner_identity = self.create_owner_identity(updated_masternode.clone())?; + // let voter_identity = self.create_voter_identity(updated_masternode.clone())?; + // let operator_identity = self.create_operator_identity(updated_masternode)?; + // + // // TODO: move this into the identity creation step + // // TODO: what do we do with the fee result + // let _ = self.drive.add_new_identity( + // owner_identity, + // block_info, + // true, + // Some(transaction), + // )?; + // let _ = self.drive.add_new_identity( + // voter_identity, + // block_info, + // true, + // Some(transaction), + // )?; + // let _ = self.drive.add_new_identity( + // operator_identity, + // block_info, + // true, + // Some(transaction), + // )?; + } + // //todo: + // // self.drive.fetch_full_identity() + // // self.drive.add_new_non_unique_keys_to_identity() + } + Ok(()) + } +} diff --git a/packages/rs-drive-abci/src/execution/mod.rs b/packages/rs-drive-abci/src/execution/mod.rs index 882c89b55a2..77cfb86ef29 100644 --- a/packages/rs-drive-abci/src/execution/mod.rs +++ b/packages/rs-drive-abci/src/execution/mod.rs @@ -10,5 +10,7 @@ pub mod fee_pools; pub mod helpers; /// Initialization pub mod initialization; +/// Masternode Identities +mod masternode_identities; /// Protocol upgrade pub mod protocol_upgrade; diff --git a/packages/rs-drive-abci/src/lib.rs b/packages/rs-drive-abci/src/lib.rs index 11db0f941b5..258bf4b3f9d 100644 --- a/packages/rs-drive-abci/src/lib.rs +++ b/packages/rs-drive-abci/src/lib.rs @@ -10,6 +10,8 @@ #![forbid(unsafe_code)] #![deny(missing_docs)] +extern crate core; + /// ABCI module pub mod abci; diff --git a/packages/rs-drive-abci/src/rpc/core.rs b/packages/rs-drive-abci/src/rpc/core.rs index 5f83f760498..0834a039a71 100644 --- a/packages/rs-drive-abci/src/rpc/core.rs +++ b/packages/rs-drive-abci/src/rpc/core.rs @@ -3,8 +3,8 @@ use dashcore_rpc::dashcore_rpc_json::{ ExtendedQuorumDetails, GetBestChainLockResult, QuorumHash, QuorumInfoResult, QuorumListResult, QuorumType, }; -use dashcore_rpc::{Auth, Client, Error, RpcApi}; use dashcore_rpc::json::{MasternodeListDiff, ProTxHash, ProTxInfo}; +use dashcore_rpc::{Auth, Client, Error, RpcApi}; use mockall::{automock, predicate::*}; use serde_json::Value; use std::collections::HashMap;