From f17e3f389614848cadf8439c8ddebacb68326e09 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Tue, 21 Jun 2022 19:28:59 +0100 Subject: [PATCH 01/19] create tx validation for acceptances --- .../src/validation/transaction_validators.rs | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/base_layer/core/src/validation/transaction_validators.rs b/base_layer/core/src/validation/transaction_validators.rs index b3a72e7e62..7a484fb64c 100644 --- a/base_layer/core/src/validation/transaction_validators.rs +++ b/base_layer/core/src/validation/transaction_validators.rs @@ -27,7 +27,7 @@ use crate::{ chain_storage::{BlockchainBackend, BlockchainDatabase, PrunedOutput}, consensus::ConsensusConstants, transactions::{ - transaction_components::{SpentOutput, Transaction}, + transaction_components::{OutputType, SpentOutput, Transaction}, CryptoFactories, }, validation::{ @@ -249,6 +249,61 @@ impl TxConsensusValidator { None => Ok(()), } } + + fn validate_contract_acceptances(&self, tx: &Transaction) -> Result<(), ValidationError> { + for output in tx.body().outputs() { + // we only want to validate contract acceptances + if output.features.output_type != OutputType::ContractValidatorAcceptance { + continue; + } + // !output.features.is_sidechain_contract() + let sidechain_features = output.features.sidechain_features.as_ref().unwrap(); + let contract_id = sidechain_features.contract_id; + let validator_node_publick_key = &sidechain_features + .acceptance + .as_ref() + .unwrap() + .validator_node_public_key; + + let contract_outputs = self + .db + .fetch_contract_outputs_by_contract_id_and_type(contract_id, OutputType::ContractConstitution) + .unwrap(); + if contract_outputs.is_empty() { + continue; + } + let constitution_output = contract_outputs + .first() + .unwrap() + .output + .as_transaction_output() + .unwrap(); + let constitution = constitution_output + .features + .sidechain_features + .as_ref() + .unwrap() + .constitution + .as_ref() + .unwrap(); + + let is_validator_in_committee = constitution + .validator_committee + .members() + .contains(validator_node_publick_key); + if !is_validator_in_committee { + let msg = format!( + "Invalid contract acceptance: validator node public key is not in committee ({:?})", + validator_node_publick_key + ); + return Err(ValidationError::ConsensusError(msg)); + } + + // TODO: check that the signature of the transaction is valid + } + + Ok(()) + } } impl MempoolTransactionValidation for TxConsensusValidator { @@ -265,7 +320,9 @@ impl MempoolTransactionValidation for TxConsensusValidator self.validate_versions(tx, consensus_constants)?; - self.validate_unique_asset_rules(tx) + self.validate_unique_asset_rules(tx)?; + + self.validate_contract_acceptances(tx) } } From 1fdad86639f597223a098faf71e570640686852c Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Tue, 21 Jun 2022 19:29:17 +0100 Subject: [PATCH 02/19] create unit test for acceptances validation --- .../transaction_components/output_features.rs | 13 ++ base_layer/core/src/validation/test.rs | 150 ++++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/base_layer/core/src/transactions/transaction_components/output_features.rs b/base_layer/core/src/transactions/transaction_components/output_features.rs index a5cd0df48b..94403a3dc7 100644 --- a/base_layer/core/src/transactions/transaction_components/output_features.rs +++ b/base_layer/core/src/transactions/transaction_components/output_features.rs @@ -40,6 +40,7 @@ use tari_utilities::ByteArray; use super::{ ContractAcceptance, ContractAmendment, + ContractConstitution, ContractDefinition, ContractUpdateProposal, ContractUpdateProposalAcceptance, @@ -300,6 +301,18 @@ impl OutputFeatures { } } + pub fn for_contract_constitution(contract_id: FixedHash, constitution: ContractConstitution) -> OutputFeatures { + Self { + output_type: OutputType::ContractConstitution, + sidechain_features: Some( + SideChainFeaturesBuilder::new(contract_id) + .with_contract_constitution(constitution) + .finish(), + ), + ..Default::default() + } + } + pub fn for_contract_acceptance( contract_id: FixedHash, validator_node_public_key: PublicKey, diff --git a/base_layer/core/src/validation/test.rs b/base_layer/core/src/validation/test.rs index b8787d2354..54639b8ff5 100644 --- a/base_layer/core/src/validation/test.rs +++ b/base_layer/core/src/validation/test.rs @@ -283,7 +283,37 @@ fn chain_balance_validation() { } mod transaction_validator { + use std::convert::TryInto; + + use tari_common_types::types::{FixedHash, PublicKey, Signature}; + use tari_utilities::hex::Hex; + use super::*; + use crate::{ + block_spec, + test_helpers::blockchain::TestBlockchain, + transactions::{ + tari_amount::T, + test_helpers::{spend_utxos, TransactionSchema}, + transaction_components::{ + vec_into_fixed_string, + CheckpointParameters, + CommitteeMembers, + ConstitutionChangeFlags, + ConstitutionChangeRules, + ContractAcceptanceRequirements, + ContractConstitution, + ContractDefinition, + ContractSpecification, + RequirementsForConstitutionChange, + SideChainConsensus, + Transaction, + UnblindedOutput, + }, + }, + txn_schema, + validation::transaction_validators::TxConsensusValidator, + }; #[test] fn it_rejects_coinbase_outputs() { @@ -296,4 +326,124 @@ mod transaction_validator { let err = validator.validate(&tx).unwrap_err(); unpack_enum!(ValidationError::ErroneousCoinbaseOutput = err); } + + pub fn schema_to_transaction(txns: &[TransactionSchema]) -> (Vec>, Vec) { + let mut tx = Vec::new(); + let mut utxos = Vec::new(); + txns.iter().for_each(|schema| { + let (txn, mut output) = spend_utxos(schema.clone()); + tx.push(Arc::new(txn)); + utxos.append(&mut output); + }); + (tx, utxos) + } + + pub fn create_block( + blockchain: &mut TestBlockchain, + block_name: &'static str, + schema: TransactionSchema, + ) -> Vec { + let (txs, outputs) = schema_to_transaction(&[schema]); + let (_, _) = blockchain + .append_to_tip(block_spec!(block_name, transactions: txs.iter().map(|t| (**t).clone()).collect())) + .unwrap(); + + outputs + } + + fn create_contract_definition_schema(input: UnblindedOutput) -> (FixedHash, TransactionSchema) { + let definition = ContractDefinition { + contract_name: vec_into_fixed_string("name".as_bytes().to_vec()), + contract_issuer: PublicKey::default(), + contract_spec: ContractSpecification { + runtime: vec_into_fixed_string("runtime".as_bytes().to_vec()), + public_functions: vec![], + }, + }; + let contract_id = definition.calculate_contract_id(); + let definition_features = OutputFeatures::for_contract_definition(definition); + + let tx_schema = + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: definition_features); + + (contract_id, tx_schema) + } + + fn create_contract_constitution_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { + let validator_committee: CommitteeMembers = vec![PublicKey::default()].try_into().unwrap(); + let constitution = ContractConstitution { + validator_committee, + acceptance_requirements: ContractAcceptanceRequirements { + acceptance_period_expiry: 100, + minimum_quorum_required: 5, + }, + consensus: SideChainConsensus::MerkleRoot, + checkpoint_params: CheckpointParameters { + minimum_quorum_required: 5, + abandoned_interval: 100, + }, + constitution_change_rules: ConstitutionChangeRules { + change_flags: ConstitutionChangeFlags::all(), + requirements_for_constitution_change: Some(RequirementsForConstitutionChange { + minimum_constitution_committee_signatures: 5, + constitution_committee: Some( + vec![PublicKey::default(); CommitteeMembers::MAX_MEMBERS] + .try_into() + .unwrap(), + ), + }), + }, + initial_reward: 100.into(), + }; + let constitution_features = OutputFeatures::for_contract_constitution(contract_id, constitution); + + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: constitution_features) + } + + fn create_contract_acceptance_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { + // let validator_node_public_key = PublicKey::default(); + let validator_node_public_key = + PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); + let signature = Signature::default(); + + let acceptance_features = + OutputFeatures::for_contract_acceptance(contract_id, validator_node_public_key, signature); + + let mut tx = + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: acceptance_features); + tx.output_version = None; + + tx + } + + #[test] + fn it_rejects_contract_acceptances_of_non_committee_member() { + let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet).build(); + let mut blockchain = TestBlockchain::create(consensus_manager); + let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("1")).unwrap(); + + let schema = txn_schema!(from: vec![coinbase_a], to: vec![50 * T, 50 * T, 50 * T]); + let change_outputs = create_block(&mut blockchain, "2", schema); + + let (contract_id, schema) = create_contract_definition_schema(change_outputs[0].clone()); + // let schema = txn_schema!(from: vec![change_outputs[0].clone()], to: vec![10 * T]); + create_block(&mut blockchain, "3", schema); + + // let schema = txn_schema!(from: vec![change_outputs[1].clone()], to: vec![10 * T]); + let schema = create_contract_constitution_schema(contract_id, change_outputs[1].clone()); + create_block(&mut blockchain, "4", schema); + + let schema = create_contract_acceptance_schema(contract_id, change_outputs[2].clone()); + let (txs, _) = schema_to_transaction(&[schema]); + + let validator = TxConsensusValidator::new(blockchain.db().clone()); + let err = validator.validate(txs.first().unwrap()).unwrap_err(); + + match err { + ValidationError::ConsensusError(message) => { + assert!(message.contains("Invalid contract acceptance: validator node public key is not in committee")) + }, + _ => panic!("Expected a consensus error"), + } + } } From e0b151278e61acd1ecc0f118857ea098a3c7f643 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 09:56:28 +0100 Subject: [PATCH 03/19] refactor dan validators into a separated module --- .../dan_validators/acceptance_validator.rs | 241 ++++++++++++++++++ .../core/src/validation/dan_validators/mod.rs | 44 ++++ base_layer/core/src/validation/mod.rs | 1 + base_layer/core/src/validation/test.rs | 150 ----------- .../src/validation/transaction_validators.rs | 60 +---- 5 files changed, 289 insertions(+), 207 deletions(-) create mode 100644 base_layer/core/src/validation/dan_validators/acceptance_validator.rs create mode 100644 base_layer/core/src/validation/dan_validators/mod.rs diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs new file mode 100644 index 0000000000..88cae36552 --- /dev/null +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -0,0 +1,241 @@ +// 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 crate::{ + chain_storage::{BlockchainBackend, BlockchainDatabase}, + transactions::transaction_components::{OutputType, Transaction}, + validation::ValidationError, +}; + +pub fn validate_contract_acceptances( + db: &BlockchainDatabase, + tx: &Transaction, +) -> Result<(), ValidationError> { + for output in tx.body().outputs() { + // we only want to validate contract acceptances + if output.features.output_type != OutputType::ContractValidatorAcceptance { + continue; + } + // !output.features.is_sidechain_contract() + let sidechain_features = output.features.sidechain_features.as_ref().unwrap(); + let contract_id = sidechain_features.contract_id; + let validator_node_publick_key = &sidechain_features + .acceptance + .as_ref() + .unwrap() + .validator_node_public_key; + + let contract_outputs = db + .fetch_contract_outputs_by_contract_id_and_type(contract_id, OutputType::ContractConstitution) + .unwrap(); + if contract_outputs.is_empty() { + continue; + } + let constitution_output = contract_outputs + .first() + .unwrap() + .output + .as_transaction_output() + .unwrap(); + let constitution = constitution_output + .features + .sidechain_features + .as_ref() + .unwrap() + .constitution + .as_ref() + .unwrap(); + + let is_validator_in_committee = constitution + .validator_committee + .members() + .contains(validator_node_publick_key); + if !is_validator_in_committee { + let msg = format!( + "Invalid contract acceptance: validator node public key is not in committee ({:?})", + validator_node_publick_key + ); + return Err(ValidationError::ConsensusError(msg)); + } + + // TODO: check that the signature of the transaction is valid + } + + Ok(()) +} + +#[cfg(test)] +mod test { + use std::{convert::TryInto, sync::Arc}; + + use tari_common_types::types::{FixedHash, PublicKey, Signature}; + use tari_p2p::Network; + use tari_utilities::hex::Hex; + + use crate::{ + block_spec, + consensus::ConsensusManagerBuilder, + test_helpers::blockchain::TestBlockchain, + transactions::{ + tari_amount::T, + test_helpers::{spend_utxos, TransactionSchema}, + transaction_components::{ + vec_into_fixed_string, + CheckpointParameters, + CommitteeMembers, + ConstitutionChangeFlags, + ConstitutionChangeRules, + ContractAcceptanceRequirements, + ContractConstitution, + ContractDefinition, + ContractSpecification, + OutputFeatures, + RequirementsForConstitutionChange, + SideChainConsensus, + Transaction, + UnblindedOutput, + }, + }, + txn_schema, + validation::{transaction_validators::TxConsensusValidator, MempoolTransactionValidation, ValidationError}, + }; + + #[test] + fn it_rejects_contract_acceptances_of_non_committee_member() { + let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet).build(); + let mut blockchain = TestBlockchain::create(consensus_manager); + let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("1")).unwrap(); + + let schema = txn_schema!(from: vec![coinbase_a], to: vec![50 * T, 50 * T, 50 * T]); + let change_outputs = create_block(&mut blockchain, "2", schema); + + let (contract_id, schema) = create_contract_definition_schema(change_outputs[0].clone()); + // let schema = txn_schema!(from: vec![change_outputs[0].clone()], to: vec![10 * T]); + create_block(&mut blockchain, "3", schema); + + // let schema = txn_schema!(from: vec![change_outputs[1].clone()], to: vec![10 * T]); + let schema = create_contract_constitution_schema(contract_id, change_outputs[1].clone()); + create_block(&mut blockchain, "4", schema); + + let schema = create_contract_acceptance_schema(contract_id, change_outputs[2].clone()); + let (txs, _) = schema_to_transaction(&[schema]); + + let validator = TxConsensusValidator::new(blockchain.db().clone()); + let err = validator.validate(txs.first().unwrap()).unwrap_err(); + + match err { + ValidationError::ConsensusError(message) => { + assert!(message.contains("Invalid contract acceptance: validator node public key is not in committee")) + }, + _ => panic!("Expected a consensus error"), + } + } + + pub fn schema_to_transaction(txns: &[TransactionSchema]) -> (Vec>, Vec) { + let mut tx = Vec::new(); + let mut utxos = Vec::new(); + txns.iter().for_each(|schema| { + let (txn, mut output) = spend_utxos(schema.clone()); + tx.push(Arc::new(txn)); + utxos.append(&mut output); + }); + (tx, utxos) + } + + pub fn create_block( + blockchain: &mut TestBlockchain, + block_name: &'static str, + schema: TransactionSchema, + ) -> Vec { + let (txs, outputs) = schema_to_transaction(&[schema]); + let (_, _) = blockchain + .append_to_tip(block_spec!(block_name, transactions: txs.iter().map(|t| (**t).clone()).collect())) + .unwrap(); + + outputs + } + + fn create_contract_definition_schema(input: UnblindedOutput) -> (FixedHash, TransactionSchema) { + let definition = ContractDefinition { + contract_name: vec_into_fixed_string("name".as_bytes().to_vec()), + contract_issuer: PublicKey::default(), + contract_spec: ContractSpecification { + runtime: vec_into_fixed_string("runtime".as_bytes().to_vec()), + public_functions: vec![], + }, + }; + let contract_id = definition.calculate_contract_id(); + let definition_features = OutputFeatures::for_contract_definition(definition); + + let tx_schema = + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: definition_features); + + (contract_id, tx_schema) + } + + fn create_contract_constitution_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { + let validator_committee: CommitteeMembers = vec![PublicKey::default()].try_into().unwrap(); + let constitution = ContractConstitution { + validator_committee, + acceptance_requirements: ContractAcceptanceRequirements { + acceptance_period_expiry: 100, + minimum_quorum_required: 5, + }, + consensus: SideChainConsensus::MerkleRoot, + checkpoint_params: CheckpointParameters { + minimum_quorum_required: 5, + abandoned_interval: 100, + }, + constitution_change_rules: ConstitutionChangeRules { + change_flags: ConstitutionChangeFlags::all(), + requirements_for_constitution_change: Some(RequirementsForConstitutionChange { + minimum_constitution_committee_signatures: 5, + constitution_committee: Some( + vec![PublicKey::default(); CommitteeMembers::MAX_MEMBERS] + .try_into() + .unwrap(), + ), + }), + }, + initial_reward: 100.into(), + }; + let constitution_features = OutputFeatures::for_contract_constitution(contract_id, constitution); + + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: constitution_features) + } + + fn create_contract_acceptance_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { + // let validator_node_public_key = PublicKey::default(); + let validator_node_public_key = + PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); + let signature = Signature::default(); + + let acceptance_features = + OutputFeatures::for_contract_acceptance(contract_id, validator_node_public_key, signature); + + let mut tx = + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: acceptance_features); + tx.output_version = None; + + tx + } +} diff --git a/base_layer/core/src/validation/dan_validators/mod.rs b/base_layer/core/src/validation/dan_validators/mod.rs new file mode 100644 index 0000000000..c4b2cf8788 --- /dev/null +++ b/base_layer/core/src/validation/dan_validators/mod.rs @@ -0,0 +1,44 @@ +// 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 super::ValidationError; +use crate::{ + chain_storage::{BlockchainBackend, BlockchainDatabase}, + transactions::transaction_components::{OutputType, Transaction}, +}; + +mod acceptance_validator; +use acceptance_validator::validate_contract_acceptances; + +pub fn validate_dan_transaction( + db: &BlockchainDatabase, + tx: &Transaction, +) -> Result<(), ValidationError> { + for output in tx.body().outputs() { + match output.features.output_type { + OutputType::ContractValidatorAcceptance => return validate_contract_acceptances(db, tx), + _ => continue, + } + } + + Ok(()) +} diff --git a/base_layer/core/src/validation/mod.rs b/base_layer/core/src/validation/mod.rs index 528709eb10..215620eef5 100644 --- a/base_layer/core/src/validation/mod.rs +++ b/base_layer/core/src/validation/mod.rs @@ -43,6 +43,7 @@ pub use traits::{ }; pub mod block_validators; +mod dan_validators; mod difficulty_calculator; pub use difficulty_calculator::*; pub mod header_validator; diff --git a/base_layer/core/src/validation/test.rs b/base_layer/core/src/validation/test.rs index 54639b8ff5..b8787d2354 100644 --- a/base_layer/core/src/validation/test.rs +++ b/base_layer/core/src/validation/test.rs @@ -283,37 +283,7 @@ fn chain_balance_validation() { } mod transaction_validator { - use std::convert::TryInto; - - use tari_common_types::types::{FixedHash, PublicKey, Signature}; - use tari_utilities::hex::Hex; - use super::*; - use crate::{ - block_spec, - test_helpers::blockchain::TestBlockchain, - transactions::{ - tari_amount::T, - test_helpers::{spend_utxos, TransactionSchema}, - transaction_components::{ - vec_into_fixed_string, - CheckpointParameters, - CommitteeMembers, - ConstitutionChangeFlags, - ConstitutionChangeRules, - ContractAcceptanceRequirements, - ContractConstitution, - ContractDefinition, - ContractSpecification, - RequirementsForConstitutionChange, - SideChainConsensus, - Transaction, - UnblindedOutput, - }, - }, - txn_schema, - validation::transaction_validators::TxConsensusValidator, - }; #[test] fn it_rejects_coinbase_outputs() { @@ -326,124 +296,4 @@ mod transaction_validator { let err = validator.validate(&tx).unwrap_err(); unpack_enum!(ValidationError::ErroneousCoinbaseOutput = err); } - - pub fn schema_to_transaction(txns: &[TransactionSchema]) -> (Vec>, Vec) { - let mut tx = Vec::new(); - let mut utxos = Vec::new(); - txns.iter().for_each(|schema| { - let (txn, mut output) = spend_utxos(schema.clone()); - tx.push(Arc::new(txn)); - utxos.append(&mut output); - }); - (tx, utxos) - } - - pub fn create_block( - blockchain: &mut TestBlockchain, - block_name: &'static str, - schema: TransactionSchema, - ) -> Vec { - let (txs, outputs) = schema_to_transaction(&[schema]); - let (_, _) = blockchain - .append_to_tip(block_spec!(block_name, transactions: txs.iter().map(|t| (**t).clone()).collect())) - .unwrap(); - - outputs - } - - fn create_contract_definition_schema(input: UnblindedOutput) -> (FixedHash, TransactionSchema) { - let definition = ContractDefinition { - contract_name: vec_into_fixed_string("name".as_bytes().to_vec()), - contract_issuer: PublicKey::default(), - contract_spec: ContractSpecification { - runtime: vec_into_fixed_string("runtime".as_bytes().to_vec()), - public_functions: vec![], - }, - }; - let contract_id = definition.calculate_contract_id(); - let definition_features = OutputFeatures::for_contract_definition(definition); - - let tx_schema = - txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: definition_features); - - (contract_id, tx_schema) - } - - fn create_contract_constitution_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { - let validator_committee: CommitteeMembers = vec![PublicKey::default()].try_into().unwrap(); - let constitution = ContractConstitution { - validator_committee, - acceptance_requirements: ContractAcceptanceRequirements { - acceptance_period_expiry: 100, - minimum_quorum_required: 5, - }, - consensus: SideChainConsensus::MerkleRoot, - checkpoint_params: CheckpointParameters { - minimum_quorum_required: 5, - abandoned_interval: 100, - }, - constitution_change_rules: ConstitutionChangeRules { - change_flags: ConstitutionChangeFlags::all(), - requirements_for_constitution_change: Some(RequirementsForConstitutionChange { - minimum_constitution_committee_signatures: 5, - constitution_committee: Some( - vec![PublicKey::default(); CommitteeMembers::MAX_MEMBERS] - .try_into() - .unwrap(), - ), - }), - }, - initial_reward: 100.into(), - }; - let constitution_features = OutputFeatures::for_contract_constitution(contract_id, constitution); - - txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: constitution_features) - } - - fn create_contract_acceptance_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { - // let validator_node_public_key = PublicKey::default(); - let validator_node_public_key = - PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); - let signature = Signature::default(); - - let acceptance_features = - OutputFeatures::for_contract_acceptance(contract_id, validator_node_public_key, signature); - - let mut tx = - txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: acceptance_features); - tx.output_version = None; - - tx - } - - #[test] - fn it_rejects_contract_acceptances_of_non_committee_member() { - let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet).build(); - let mut blockchain = TestBlockchain::create(consensus_manager); - let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("1")).unwrap(); - - let schema = txn_schema!(from: vec![coinbase_a], to: vec![50 * T, 50 * T, 50 * T]); - let change_outputs = create_block(&mut blockchain, "2", schema); - - let (contract_id, schema) = create_contract_definition_schema(change_outputs[0].clone()); - // let schema = txn_schema!(from: vec![change_outputs[0].clone()], to: vec![10 * T]); - create_block(&mut blockchain, "3", schema); - - // let schema = txn_schema!(from: vec![change_outputs[1].clone()], to: vec![10 * T]); - let schema = create_contract_constitution_schema(contract_id, change_outputs[1].clone()); - create_block(&mut blockchain, "4", schema); - - let schema = create_contract_acceptance_schema(contract_id, change_outputs[2].clone()); - let (txs, _) = schema_to_transaction(&[schema]); - - let validator = TxConsensusValidator::new(blockchain.db().clone()); - let err = validator.validate(txs.first().unwrap()).unwrap_err(); - - match err { - ValidationError::ConsensusError(message) => { - assert!(message.contains("Invalid contract acceptance: validator node public key is not in committee")) - }, - _ => panic!("Expected a consensus error"), - } - } } diff --git a/base_layer/core/src/validation/transaction_validators.rs b/base_layer/core/src/validation/transaction_validators.rs index 7a484fb64c..adb277a36c 100644 --- a/base_layer/core/src/validation/transaction_validators.rs +++ b/base_layer/core/src/validation/transaction_validators.rs @@ -23,11 +23,12 @@ use log::*; use tari_utilities::hex::Hex; +use super::dan_validators::validate_dan_transaction; use crate::{ chain_storage::{BlockchainBackend, BlockchainDatabase, PrunedOutput}, consensus::ConsensusConstants, transactions::{ - transaction_components::{OutputType, SpentOutput, Transaction}, + transaction_components::{SpentOutput, Transaction}, CryptoFactories, }, validation::{ @@ -249,61 +250,6 @@ impl TxConsensusValidator { None => Ok(()), } } - - fn validate_contract_acceptances(&self, tx: &Transaction) -> Result<(), ValidationError> { - for output in tx.body().outputs() { - // we only want to validate contract acceptances - if output.features.output_type != OutputType::ContractValidatorAcceptance { - continue; - } - // !output.features.is_sidechain_contract() - let sidechain_features = output.features.sidechain_features.as_ref().unwrap(); - let contract_id = sidechain_features.contract_id; - let validator_node_publick_key = &sidechain_features - .acceptance - .as_ref() - .unwrap() - .validator_node_public_key; - - let contract_outputs = self - .db - .fetch_contract_outputs_by_contract_id_and_type(contract_id, OutputType::ContractConstitution) - .unwrap(); - if contract_outputs.is_empty() { - continue; - } - let constitution_output = contract_outputs - .first() - .unwrap() - .output - .as_transaction_output() - .unwrap(); - let constitution = constitution_output - .features - .sidechain_features - .as_ref() - .unwrap() - .constitution - .as_ref() - .unwrap(); - - let is_validator_in_committee = constitution - .validator_committee - .members() - .contains(validator_node_publick_key); - if !is_validator_in_committee { - let msg = format!( - "Invalid contract acceptance: validator node public key is not in committee ({:?})", - validator_node_publick_key - ); - return Err(ValidationError::ConsensusError(msg)); - } - - // TODO: check that the signature of the transaction is valid - } - - Ok(()) - } } impl MempoolTransactionValidation for TxConsensusValidator { @@ -322,7 +268,7 @@ impl MempoolTransactionValidation for TxConsensusValidator self.validate_unique_asset_rules(tx)?; - self.validate_contract_acceptances(tx) + validate_dan_transaction(&self.db, tx) } } From 33193c84c190021c383eddf2d38ec5188bf281e4 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 10:30:24 +0100 Subject: [PATCH 04/19] create a test_helper module --- .../dan_validators/acceptance_validator.rs | 125 ++-------------- .../core/src/validation/dan_validators/mod.rs | 3 + .../validation/dan_validators/test_helpers.rs | 140 ++++++++++++++++++ 3 files changed, 155 insertions(+), 113 deletions(-) create mode 100644 base_layer/core/src/validation/dan_validators/test_helpers.rs diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs index 88cae36552..36939baceb 100644 --- a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -85,38 +85,26 @@ pub fn validate_contract_acceptances( #[cfg(test)] mod test { - use std::{convert::TryInto, sync::Arc}; - - use tari_common_types::types::{FixedHash, PublicKey, Signature}; use tari_p2p::Network; - use tari_utilities::hex::Hex; use crate::{ block_spec, consensus::ConsensusManagerBuilder, test_helpers::blockchain::TestBlockchain, - transactions::{ - tari_amount::T, - test_helpers::{spend_utxos, TransactionSchema}, - transaction_components::{ - vec_into_fixed_string, - CheckpointParameters, - CommitteeMembers, - ConstitutionChangeFlags, - ConstitutionChangeRules, - ContractAcceptanceRequirements, - ContractConstitution, - ContractDefinition, - ContractSpecification, - OutputFeatures, - RequirementsForConstitutionChange, - SideChainConsensus, - Transaction, - UnblindedOutput, + transactions::tari_amount::T, + txn_schema, + validation::{ + dan_validators::test_helpers::{ + create_block, + create_contract_acceptance_schema, + create_contract_constitution_schema, + create_contract_definition_schema, + schema_to_transaction, }, + transaction_validators::TxConsensusValidator, + MempoolTransactionValidation, + ValidationError, }, - txn_schema, - validation::{transaction_validators::TxConsensusValidator, MempoolTransactionValidation, ValidationError}, }; #[test] @@ -149,93 +137,4 @@ mod test { _ => panic!("Expected a consensus error"), } } - - pub fn schema_to_transaction(txns: &[TransactionSchema]) -> (Vec>, Vec) { - let mut tx = Vec::new(); - let mut utxos = Vec::new(); - txns.iter().for_each(|schema| { - let (txn, mut output) = spend_utxos(schema.clone()); - tx.push(Arc::new(txn)); - utxos.append(&mut output); - }); - (tx, utxos) - } - - pub fn create_block( - blockchain: &mut TestBlockchain, - block_name: &'static str, - schema: TransactionSchema, - ) -> Vec { - let (txs, outputs) = schema_to_transaction(&[schema]); - let (_, _) = blockchain - .append_to_tip(block_spec!(block_name, transactions: txs.iter().map(|t| (**t).clone()).collect())) - .unwrap(); - - outputs - } - - fn create_contract_definition_schema(input: UnblindedOutput) -> (FixedHash, TransactionSchema) { - let definition = ContractDefinition { - contract_name: vec_into_fixed_string("name".as_bytes().to_vec()), - contract_issuer: PublicKey::default(), - contract_spec: ContractSpecification { - runtime: vec_into_fixed_string("runtime".as_bytes().to_vec()), - public_functions: vec![], - }, - }; - let contract_id = definition.calculate_contract_id(); - let definition_features = OutputFeatures::for_contract_definition(definition); - - let tx_schema = - txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: definition_features); - - (contract_id, tx_schema) - } - - fn create_contract_constitution_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { - let validator_committee: CommitteeMembers = vec![PublicKey::default()].try_into().unwrap(); - let constitution = ContractConstitution { - validator_committee, - acceptance_requirements: ContractAcceptanceRequirements { - acceptance_period_expiry: 100, - minimum_quorum_required: 5, - }, - consensus: SideChainConsensus::MerkleRoot, - checkpoint_params: CheckpointParameters { - minimum_quorum_required: 5, - abandoned_interval: 100, - }, - constitution_change_rules: ConstitutionChangeRules { - change_flags: ConstitutionChangeFlags::all(), - requirements_for_constitution_change: Some(RequirementsForConstitutionChange { - minimum_constitution_committee_signatures: 5, - constitution_committee: Some( - vec![PublicKey::default(); CommitteeMembers::MAX_MEMBERS] - .try_into() - .unwrap(), - ), - }), - }, - initial_reward: 100.into(), - }; - let constitution_features = OutputFeatures::for_contract_constitution(contract_id, constitution); - - txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: constitution_features) - } - - fn create_contract_acceptance_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { - // let validator_node_public_key = PublicKey::default(); - let validator_node_public_key = - PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); - let signature = Signature::default(); - - let acceptance_features = - OutputFeatures::for_contract_acceptance(contract_id, validator_node_public_key, signature); - - let mut tx = - txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: acceptance_features); - tx.output_version = None; - - tx - } } diff --git a/base_layer/core/src/validation/dan_validators/mod.rs b/base_layer/core/src/validation/dan_validators/mod.rs index c4b2cf8788..a6e99337a8 100644 --- a/base_layer/core/src/validation/dan_validators/mod.rs +++ b/base_layer/core/src/validation/dan_validators/mod.rs @@ -29,6 +29,9 @@ use crate::{ mod acceptance_validator; use acceptance_validator::validate_contract_acceptances; +#[cfg(test)] +mod test_helpers; + pub fn validate_dan_transaction( db: &BlockchainDatabase, tx: &Transaction, diff --git a/base_layer/core/src/validation/dan_validators/test_helpers.rs b/base_layer/core/src/validation/dan_validators/test_helpers.rs new file mode 100644 index 0000000000..c2090865bf --- /dev/null +++ b/base_layer/core/src/validation/dan_validators/test_helpers.rs @@ -0,0 +1,140 @@ +// 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 std::{convert::TryInto, sync::Arc}; + +use tari_common_types::types::{FixedHash, PublicKey, Signature}; +use tari_utilities::hex::Hex; + +use crate::{ + block_spec, + test_helpers::blockchain::TestBlockchain, + transactions::{ + test_helpers::{spend_utxos, TransactionSchema}, + transaction_components::{ + vec_into_fixed_string, + CheckpointParameters, + CommitteeMembers, + ConstitutionChangeFlags, + ConstitutionChangeRules, + ContractAcceptanceRequirements, + ContractConstitution, + ContractDefinition, + ContractSpecification, + OutputFeatures, + RequirementsForConstitutionChange, + SideChainConsensus, + Transaction, + UnblindedOutput, + }, + }, + txn_schema, +}; + +pub fn schema_to_transaction(txns: &[TransactionSchema]) -> (Vec>, Vec) { + let mut tx = Vec::new(); + let mut utxos = Vec::new(); + txns.iter().for_each(|schema| { + let (txn, mut output) = spend_utxos(schema.clone()); + tx.push(Arc::new(txn)); + utxos.append(&mut output); + }); + (tx, utxos) +} + +pub fn create_block( + blockchain: &mut TestBlockchain, + block_name: &'static str, + schema: TransactionSchema, +) -> Vec { + let (txs, outputs) = schema_to_transaction(&[schema]); + let (_, _) = blockchain + .append_to_tip(block_spec!(block_name, transactions: txs.iter().map(|t| (**t).clone()).collect())) + .unwrap(); + + outputs +} + +pub fn create_contract_definition_schema(input: UnblindedOutput) -> (FixedHash, TransactionSchema) { + let definition = ContractDefinition { + contract_name: vec_into_fixed_string("name".as_bytes().to_vec()), + contract_issuer: PublicKey::default(), + contract_spec: ContractSpecification { + runtime: vec_into_fixed_string("runtime".as_bytes().to_vec()), + public_functions: vec![], + }, + }; + let contract_id = definition.calculate_contract_id(); + let definition_features = OutputFeatures::for_contract_definition(definition); + + let tx_schema = + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: definition_features); + + (contract_id, tx_schema) +} + +pub fn create_contract_constitution_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { + let validator_committee: CommitteeMembers = vec![PublicKey::default()].try_into().unwrap(); + let constitution = ContractConstitution { + validator_committee, + acceptance_requirements: ContractAcceptanceRequirements { + acceptance_period_expiry: 100, + minimum_quorum_required: 5, + }, + consensus: SideChainConsensus::MerkleRoot, + checkpoint_params: CheckpointParameters { + minimum_quorum_required: 5, + abandoned_interval: 100, + }, + constitution_change_rules: ConstitutionChangeRules { + change_flags: ConstitutionChangeFlags::all(), + requirements_for_constitution_change: Some(RequirementsForConstitutionChange { + minimum_constitution_committee_signatures: 5, + constitution_committee: Some( + vec![PublicKey::default(); CommitteeMembers::MAX_MEMBERS] + .try_into() + .unwrap(), + ), + }), + }, + initial_reward: 100.into(), + }; + let constitution_features = OutputFeatures::for_contract_constitution(contract_id, constitution); + + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: constitution_features) +} + +pub fn create_contract_acceptance_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { + // let validator_node_public_key = PublicKey::default(); + let validator_node_public_key = + PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); + let signature = Signature::default(); + + let acceptance_features = + OutputFeatures::for_contract_acceptance(contract_id, validator_node_public_key, signature); + + let mut tx = + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: acceptance_features); + tx.output_version = None; + + tx +} From c6e4b290bd938020c6e7b83c766838ece9a48be3 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 11:40:45 +0100 Subject: [PATCH 05/19] refactor dan validator into a separated struct --- applications/tari_base_node/src/builder.rs | 2 ++ base_layer/core/benches/mempool.rs | 4 ++- .../dan_validators/acceptance_validator.rs | 20 ++++++----- .../core/src/validation/dan_validators/mod.rs | 34 ++++++++++++------- base_layer/core/src/validation/mod.rs | 2 +- .../src/validation/transaction_validators.rs | 5 +-- 6 files changed, 40 insertions(+), 27 deletions(-) diff --git a/applications/tari_base_node/src/builder.rs b/applications/tari_base_node/src/builder.rs index 034272f8fe..1ba24098e0 100644 --- a/applications/tari_base_node/src/builder.rs +++ b/applications/tari_base_node/src/builder.rs @@ -38,6 +38,7 @@ use tari_core::{ transactions::CryptoFactories, validation::{ block_validators::{BodyOnlyValidator, OrphanBlockValidator}, + dan_validators::TxDanLayerValidator, header_validator::HeaderValidator, transaction_validators::{ MempoolValidator, @@ -246,6 +247,7 @@ async fn build_node_context( )), Box::new(TxInputAndMaturityValidator::new(blockchain_db.clone())), Box::new(TxConsensusValidator::new(blockchain_db.clone())), + Box::new(TxDanLayerValidator::new(blockchain_db.clone())), ]); let mempool = Mempool::new( app_config.base_node.mempool.clone(), diff --git a/base_layer/core/benches/mempool.rs b/base_layer/core/benches/mempool.rs index fd27cbad2c..4c6f332edd 100644 --- a/base_layer/core/benches/mempool.rs +++ b/base_layer/core/benches/mempool.rs @@ -47,6 +47,7 @@ mod benches { validation::transaction_validators::{ MempoolValidator, TxConsensusValidator, + TxDanLayerValidator, TxInputAndMaturityValidator, TxInternalConsistencyValidator, }, @@ -85,7 +86,8 @@ mod benches { db.clone(), )), Box::new(TxInputAndMaturityValidator::new(db.clone())), - Box::new(TxConsensusValidator::new(db)), + Box::new(TxConsensusValidator::new(db.clone())), + Box::new(TxDanLayerValidator::new(db)), ]); let mempool = Mempool::new(config, rules, Box::new(mempool_validator)); const NUM_TXNS: usize = 100; diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs index 36939baceb..b95ea0233b 100644 --- a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -26,7 +26,7 @@ use crate::{ validation::ValidationError, }; -pub fn validate_contract_acceptances( +pub fn validate_acceptance( db: &BlockchainDatabase, tx: &Transaction, ) -> Result<(), ValidationError> { @@ -94,14 +94,16 @@ mod test { transactions::tari_amount::T, txn_schema, validation::{ - dan_validators::test_helpers::{ - create_block, - create_contract_acceptance_schema, - create_contract_constitution_schema, - create_contract_definition_schema, - schema_to_transaction, + dan_validators::{ + test_helpers::{ + create_block, + create_contract_acceptance_schema, + create_contract_constitution_schema, + create_contract_definition_schema, + schema_to_transaction, + }, + TxDanLayerValidator, }, - transaction_validators::TxConsensusValidator, MempoolTransactionValidation, ValidationError, }, @@ -127,7 +129,7 @@ mod test { let schema = create_contract_acceptance_schema(contract_id, change_outputs[2].clone()); let (txs, _) = schema_to_transaction(&[schema]); - let validator = TxConsensusValidator::new(blockchain.db().clone()); + let validator = TxDanLayerValidator::new(blockchain.db().clone()); let err = validator.validate(txs.first().unwrap()).unwrap_err(); match err { diff --git a/base_layer/core/src/validation/dan_validators/mod.rs b/base_layer/core/src/validation/dan_validators/mod.rs index a6e99337a8..73f53afdca 100644 --- a/base_layer/core/src/validation/dan_validators/mod.rs +++ b/base_layer/core/src/validation/dan_validators/mod.rs @@ -20,28 +20,38 @@ // 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 super::ValidationError; +use super::{MempoolTransactionValidation, ValidationError}; use crate::{ chain_storage::{BlockchainBackend, BlockchainDatabase}, transactions::transaction_components::{OutputType, Transaction}, }; mod acceptance_validator; -use acceptance_validator::validate_contract_acceptances; +use acceptance_validator::validate_acceptance; #[cfg(test)] mod test_helpers; -pub fn validate_dan_transaction( - db: &BlockchainDatabase, - tx: &Transaction, -) -> Result<(), ValidationError> { - for output in tx.body().outputs() { - match output.features.output_type { - OutputType::ContractValidatorAcceptance => return validate_contract_acceptances(db, tx), - _ => continue, - } +#[derive(Clone)] +pub struct TxDanLayerValidator { + db: BlockchainDatabase, +} + +impl TxDanLayerValidator { + pub fn new(db: BlockchainDatabase) -> Self { + Self { db } } +} - Ok(()) +impl MempoolTransactionValidation for TxDanLayerValidator { + fn validate(&self, tx: &Transaction) -> Result<(), ValidationError> { + for output in tx.body().outputs() { + match output.features.output_type { + OutputType::ContractValidatorAcceptance => return validate_acceptance(&self.db, tx), + _ => continue, + } + } + + Ok(()) + } } diff --git a/base_layer/core/src/validation/mod.rs b/base_layer/core/src/validation/mod.rs index 215620eef5..4d3497d1bc 100644 --- a/base_layer/core/src/validation/mod.rs +++ b/base_layer/core/src/validation/mod.rs @@ -43,7 +43,7 @@ pub use traits::{ }; pub mod block_validators; -mod dan_validators; +pub mod dan_validators; mod difficulty_calculator; pub use difficulty_calculator::*; pub mod header_validator; diff --git a/base_layer/core/src/validation/transaction_validators.rs b/base_layer/core/src/validation/transaction_validators.rs index adb277a36c..b3a72e7e62 100644 --- a/base_layer/core/src/validation/transaction_validators.rs +++ b/base_layer/core/src/validation/transaction_validators.rs @@ -23,7 +23,6 @@ use log::*; use tari_utilities::hex::Hex; -use super::dan_validators::validate_dan_transaction; use crate::{ chain_storage::{BlockchainBackend, BlockchainDatabase, PrunedOutput}, consensus::ConsensusConstants, @@ -266,9 +265,7 @@ impl MempoolTransactionValidation for TxConsensusValidator self.validate_versions(tx, consensus_constants)?; - self.validate_unique_asset_rules(tx)?; - - validate_dan_transaction(&self.db, tx) + self.validate_unique_asset_rules(tx) } } From bbfc4a3ea00e416aa3ce54c0c0f07227a8889160 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 13:12:52 +0100 Subject: [PATCH 06/19] clean up the acceptance validator code --- .../dan_validators/acceptance_validator.rs | 176 +++++++++++++----- .../core/src/validation/dan_validators/mod.rs | 2 +- 2 files changed, 127 insertions(+), 51 deletions(-) diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs index b95ea0233b..365a7e5e0d 100644 --- a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -20,64 +20,140 @@ // 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 tari_common_types::types::{FixedHash, PublicKey}; + use crate::{ chain_storage::{BlockchainBackend, BlockchainDatabase}, - transactions::transaction_components::{OutputType, Transaction}, + transactions::transaction_components::{ + ContractAcceptance, + ContractConstitution, + OutputType, + SideChainFeatures, + TransactionOutput, + }, validation::ValidationError, }; pub fn validate_acceptance( db: &BlockchainDatabase, - tx: &Transaction, + output: &TransactionOutput, ) -> Result<(), ValidationError> { - for output in tx.body().outputs() { - // we only want to validate contract acceptances - if output.features.output_type != OutputType::ContractValidatorAcceptance { - continue; - } - // !output.features.is_sidechain_contract() - let sidechain_features = output.features.sidechain_features.as_ref().unwrap(); - let contract_id = sidechain_features.contract_id; - let validator_node_publick_key = &sidechain_features - .acceptance - .as_ref() - .unwrap() - .validator_node_public_key; - - let contract_outputs = db - .fetch_contract_outputs_by_contract_id_and_type(contract_id, OutputType::ContractConstitution) - .unwrap(); - if contract_outputs.is_empty() { - continue; - } - let constitution_output = contract_outputs - .first() - .unwrap() - .output - .as_transaction_output() - .unwrap(); - let constitution = constitution_output - .features - .sidechain_features - .as_ref() - .unwrap() - .constitution - .as_ref() - .unwrap(); - - let is_validator_in_committee = constitution - .validator_committee - .members() - .contains(validator_node_publick_key); - if !is_validator_in_committee { - let msg = format!( - "Invalid contract acceptance: validator node public key is not in committee ({:?})", - validator_node_publick_key - ); - return Err(ValidationError::ConsensusError(msg)); - } + validate_output_type(output)?; + + let sidechain_features = get_sidechain_features(output)?; + let contract_id = sidechain_features.contract_id; + + let acceptance_features = get_contract_acceptance(sidechain_features)?; + let validator_node_public_key = acceptance_features.validator_node_public_key.clone(); + + let constitution = get_contract_constitution(db, contract_id)?; + + validate_public_key(constitution, validator_node_public_key)?; + + // TODO: check that the signature of the transaction is valid + // TODO: check that the acceptance is inside the accpentance window of the constiution + // TODO: check that the stake of the transaction is at least the minimum specified in the constitution + // TODO: check for duplicated acceptances + + Ok(()) +} + +fn validate_output_type(output: &TransactionOutput) -> Result<(), ValidationError> { + let output_tyoe = output.features.output_type; + if output_tyoe != OutputType::ContractValidatorAcceptance { + let msg = format!("Invalid contract acceptance: invalid output type ({:?})", output_tyoe); + return Err(ValidationError::ConsensusError(msg)); + } + + Ok(()) +} + +fn get_sidechain_features(output: &TransactionOutput) -> Result<&SideChainFeatures, ValidationError> { + match output.features.sidechain_features.as_ref() { + Some(features) => Ok(features), + None => Err(ValidationError::ConsensusError( + "Invalid contract acceptance: sidechain features not found".to_string(), + )), + } +} + +fn get_contract_acceptance(sidechain_feature: &SideChainFeatures) -> Result<&ContractAcceptance, ValidationError> { + match sidechain_feature.acceptance.as_ref() { + Some(acceptance) => Ok(acceptance), + None => Err(ValidationError::ConsensusError( + "Invalid contract acceptance: acceptance features not found".to_string(), + )), + } +} + +fn get_contract_constitution( + db: &BlockchainDatabase, + contract_id: FixedHash, +) -> Result { + let contract_outputs = db + .fetch_contract_outputs_by_contract_id_and_type(contract_id, OutputType::ContractConstitution) + .unwrap(); + + if contract_outputs.is_empty() { + return Err(ValidationError::ConsensusError( + "Invalid contract acceptance: contract constitution not found".to_string(), + )); + } - // TODO: check that the signature of the transaction is valid + // we assume only one constution should be present in the blockchain + let utxo_info = match contract_outputs.first() { + Some(value) => value, + None => { + return Err(ValidationError::ConsensusError( + "Invalid contract acceptance: contract constitution UtxoMindInfo not found".to_string(), + )) + }, + }; + + let constitution_output = match utxo_info.output.as_transaction_output() { + Some(value) => value, + None => { + return Err(ValidationError::ConsensusError( + "Invalid contract acceptance: contract constitution output not found".to_string(), + )) + }, + }; + + let constitution_features = match constitution_output.features.sidechain_features.as_ref() { + Some(value) => value, + None => { + return Err(ValidationError::ConsensusError( + "Invalid contract acceptance: contract constitution output features not found".to_string(), + )) + }, + }; + + let constitution = match constitution_features.constitution.as_ref() { + Some(value) => value, + None => { + return Err(ValidationError::ConsensusError( + "Invalid contract acceptance: contract constitution data not found in the output features".to_string(), + )) + }, + }; + + Ok(constitution.clone()) +} + +fn validate_public_key( + constitution: ContractConstitution, + validator_node_public_key: PublicKey, +) -> Result<(), ValidationError> { + let is_validator_in_committee = constitution + .validator_committee + .members() + .contains(&validator_node_public_key); + if !is_validator_in_committee { + let msg = format!( + "Invalid contract acceptance: validator node public key is not in committee ({:?})", + validator_node_public_key + ); + return Err(ValidationError::ConsensusError(msg)); } Ok(()) @@ -110,7 +186,7 @@ mod test { }; #[test] - fn it_rejects_contract_acceptances_of_non_committee_member() { + fn it_rejects_contract_acceptances_of_non_committee_members() { let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet).build(); let mut blockchain = TestBlockchain::create(consensus_manager); let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("1")).unwrap(); diff --git a/base_layer/core/src/validation/dan_validators/mod.rs b/base_layer/core/src/validation/dan_validators/mod.rs index 73f53afdca..92622aeb2e 100644 --- a/base_layer/core/src/validation/dan_validators/mod.rs +++ b/base_layer/core/src/validation/dan_validators/mod.rs @@ -47,7 +47,7 @@ impl MempoolTransactionValidation for TxDanLayerValidator< fn validate(&self, tx: &Transaction) -> Result<(), ValidationError> { for output in tx.body().outputs() { match output.features.output_type { - OutputType::ContractValidatorAcceptance => return validate_acceptance(&self.db, tx), + OutputType::ContractValidatorAcceptance => validate_acceptance(&self.db, output)?, _ => continue, } } From 5acdbe03da3c2aa28fe15b60d612ea7f79746718 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 13:25:57 +0100 Subject: [PATCH 07/19] create a helper with common validation functions --- .../dan_validators/acceptance_validator.rs | 78 +------------ .../src/validation/dan_validators/helpers.rs | 108 ++++++++++++++++++ .../core/src/validation/dan_validators/mod.rs | 2 + 3 files changed, 113 insertions(+), 75 deletions(-) create mode 100644 base_layer/core/src/validation/dan_validators/helpers.rs diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs index 365a7e5e0d..a09849f5e1 100644 --- a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -20,8 +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 tari_common_types::types::{FixedHash, PublicKey}; +use tari_common_types::types::PublicKey; +use super::helpers::{get_contract_constitution, get_sidechain_features, validate_output_type}; use crate::{ chain_storage::{BlockchainBackend, BlockchainDatabase}, transactions::transaction_components::{ @@ -38,7 +39,7 @@ pub fn validate_acceptance( db: &BlockchainDatabase, output: &TransactionOutput, ) -> Result<(), ValidationError> { - validate_output_type(output)?; + validate_output_type(output, OutputType::ContractValidatorAcceptance)?; let sidechain_features = get_sidechain_features(output)?; let contract_id = sidechain_features.contract_id; @@ -58,25 +59,6 @@ pub fn validate_acceptance( Ok(()) } -fn validate_output_type(output: &TransactionOutput) -> Result<(), ValidationError> { - let output_tyoe = output.features.output_type; - if output_tyoe != OutputType::ContractValidatorAcceptance { - let msg = format!("Invalid contract acceptance: invalid output type ({:?})", output_tyoe); - return Err(ValidationError::ConsensusError(msg)); - } - - Ok(()) -} - -fn get_sidechain_features(output: &TransactionOutput) -> Result<&SideChainFeatures, ValidationError> { - match output.features.sidechain_features.as_ref() { - Some(features) => Ok(features), - None => Err(ValidationError::ConsensusError( - "Invalid contract acceptance: sidechain features not found".to_string(), - )), - } -} - fn get_contract_acceptance(sidechain_feature: &SideChainFeatures) -> Result<&ContractAcceptance, ValidationError> { match sidechain_feature.acceptance.as_ref() { Some(acceptance) => Ok(acceptance), @@ -86,60 +68,6 @@ fn get_contract_acceptance(sidechain_feature: &SideChainFeatures) -> Result<&Con } } -fn get_contract_constitution( - db: &BlockchainDatabase, - contract_id: FixedHash, -) -> Result { - let contract_outputs = db - .fetch_contract_outputs_by_contract_id_and_type(contract_id, OutputType::ContractConstitution) - .unwrap(); - - if contract_outputs.is_empty() { - return Err(ValidationError::ConsensusError( - "Invalid contract acceptance: contract constitution not found".to_string(), - )); - } - - // we assume only one constution should be present in the blockchain - let utxo_info = match contract_outputs.first() { - Some(value) => value, - None => { - return Err(ValidationError::ConsensusError( - "Invalid contract acceptance: contract constitution UtxoMindInfo not found".to_string(), - )) - }, - }; - - let constitution_output = match utxo_info.output.as_transaction_output() { - Some(value) => value, - None => { - return Err(ValidationError::ConsensusError( - "Invalid contract acceptance: contract constitution output not found".to_string(), - )) - }, - }; - - let constitution_features = match constitution_output.features.sidechain_features.as_ref() { - Some(value) => value, - None => { - return Err(ValidationError::ConsensusError( - "Invalid contract acceptance: contract constitution output features not found".to_string(), - )) - }, - }; - - let constitution = match constitution_features.constitution.as_ref() { - Some(value) => value, - None => { - return Err(ValidationError::ConsensusError( - "Invalid contract acceptance: contract constitution data not found in the output features".to_string(), - )) - }, - }; - - Ok(constitution.clone()) -} - fn validate_public_key( constitution: ContractConstitution, validator_node_public_key: PublicKey, diff --git a/base_layer/core/src/validation/dan_validators/helpers.rs b/base_layer/core/src/validation/dan_validators/helpers.rs new file mode 100644 index 0000000000..55caaf8647 --- /dev/null +++ b/base_layer/core/src/validation/dan_validators/helpers.rs @@ -0,0 +1,108 @@ +// 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 tari_common_types::types::FixedHash; + +use crate::{ + chain_storage::{BlockchainBackend, BlockchainDatabase}, + transactions::transaction_components::{ContractConstitution, OutputType, SideChainFeatures, TransactionOutput}, + validation::ValidationError, +}; + +pub fn validate_output_type( + output: &TransactionOutput, + expected_output_type: OutputType, +) -> Result<(), ValidationError> { + let output_type = output.features.output_type; + if output_type != expected_output_type { + let msg = format!( + "Invalid output type: expected {:?} but got {:?}", + expected_output_type, output_type + ); + return Err(ValidationError::ConsensusError(msg)); + } + + Ok(()) +} + +pub fn get_sidechain_features(output: &TransactionOutput) -> Result<&SideChainFeatures, ValidationError> { + match output.features.sidechain_features.as_ref() { + Some(features) => Ok(features), + None => Err(ValidationError::ConsensusError( + "Sidechain features not found".to_string(), + )), + } +} + +pub fn get_contract_constitution( + db: &BlockchainDatabase, + contract_id: FixedHash, +) -> Result { + let contract_outputs = db + .fetch_contract_outputs_by_contract_id_and_type(contract_id, OutputType::ContractConstitution) + .unwrap(); + + if contract_outputs.is_empty() { + return Err(ValidationError::ConsensusError( + "Contract constitution not found".to_string(), + )); + } + + // we assume only one constution should be present in the blockchain + let utxo_info = match contract_outputs.first() { + Some(value) => value, + None => { + return Err(ValidationError::ConsensusError( + "Contract constitution UtxoMindInfo not found".to_string(), + )) + }, + }; + + let constitution_output = match utxo_info.output.as_transaction_output() { + Some(value) => value, + None => { + return Err(ValidationError::ConsensusError( + "Contract constitution output not found".to_string(), + )) + }, + }; + + let constitution_features = match constitution_output.features.sidechain_features.as_ref() { + Some(value) => value, + None => { + return Err(ValidationError::ConsensusError( + "Contract constitution output features not found".to_string(), + )) + }, + }; + + let constitution = match constitution_features.constitution.as_ref() { + Some(value) => value, + None => { + return Err(ValidationError::ConsensusError( + "Contract constitution data not found in the output features".to_string(), + )) + }, + }; + + Ok(constitution.clone()) +} diff --git a/base_layer/core/src/validation/dan_validators/mod.rs b/base_layer/core/src/validation/dan_validators/mod.rs index 92622aeb2e..87c854e85d 100644 --- a/base_layer/core/src/validation/dan_validators/mod.rs +++ b/base_layer/core/src/validation/dan_validators/mod.rs @@ -29,6 +29,8 @@ use crate::{ mod acceptance_validator; use acceptance_validator::validate_acceptance; +mod helpers; + #[cfg(test)] mod test_helpers; From 584343131378f83a39d9166f3a8936512672217f Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 14:47:44 +0100 Subject: [PATCH 08/19] parameterize test helpers --- .../dan_validators/acceptance_validator.rs | 21 ++++++++++---- .../validation/dan_validators/test_helpers.rs | 28 ++++++++----------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs index a09849f5e1..707cf55df1 100644 --- a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -89,7 +89,9 @@ fn validate_public_key( #[cfg(test)] mod test { + use tari_common_types::types::PublicKey; use tari_p2p::Network; + use tari_utilities::hex::Hex; use crate::{ block_spec, @@ -115,27 +117,36 @@ mod test { #[test] fn it_rejects_contract_acceptances_of_non_committee_members() { + // initialize a brand new taest blockchain with a genesis block let consensus_manager = ConsensusManagerBuilder::new(Network::LocalNet).build(); let mut blockchain = TestBlockchain::create(consensus_manager); let (_, coinbase_a) = blockchain.add_next_tip(block_spec!("1")).unwrap(); + // create a block with some UTXOs to spend later at contract transactions let schema = txn_schema!(from: vec![coinbase_a], to: vec![50 * T, 50 * T, 50 * T]); let change_outputs = create_block(&mut blockchain, "2", schema); + // publish the contract definition into a block let (contract_id, schema) = create_contract_definition_schema(change_outputs[0].clone()); - // let schema = txn_schema!(from: vec![change_outputs[0].clone()], to: vec![10 * T]); create_block(&mut blockchain, "3", schema); - // let schema = txn_schema!(from: vec![change_outputs[1].clone()], to: vec![10 * T]); - let schema = create_contract_constitution_schema(contract_id, change_outputs[1].clone()); + // publish the contract constitution into a block + // we deliberately use a committee with only a defult public key to be able to trigger the committee error later + let committee = vec![PublicKey::default()]; + let schema = create_contract_constitution_schema(contract_id, change_outputs[1].clone(), committee); create_block(&mut blockchain, "4", schema); - let schema = create_contract_acceptance_schema(contract_id, change_outputs[2].clone()); + // create a contract acceptance transaction + // we use a public key that is not included in the constitution committee, to trigger the error + let validator_node_public_key = + PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); + let schema = + create_contract_acceptance_schema(contract_id, change_outputs[2].clone(), validator_node_public_key); let (txs, _) = schema_to_transaction(&[schema]); + // try to validate the acceptance transaction and check that we get the committee error let validator = TxDanLayerValidator::new(blockchain.db().clone()); let err = validator.validate(txs.first().unwrap()).unwrap_err(); - match err { ValidationError::ConsensusError(message) => { assert!(message.contains("Invalid contract acceptance: validator node public key is not in committee")) 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 c2090865bf..4332dea93a 100644 --- a/base_layer/core/src/validation/dan_validators/test_helpers.rs +++ b/base_layer/core/src/validation/dan_validators/test_helpers.rs @@ -23,7 +23,6 @@ use std::{convert::TryInto, sync::Arc}; use tari_common_types::types::{FixedHash, PublicKey, Signature}; -use tari_utilities::hex::Hex; use crate::{ block_spec, @@ -92,7 +91,11 @@ pub fn create_contract_definition_schema(input: UnblindedOutput) -> (FixedHash, (contract_id, tx_schema) } -pub fn create_contract_constitution_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { +pub fn create_contract_constitution_schema( + contract_id: FixedHash, + input: UnblindedOutput, + committee: Vec, +) -> TransactionSchema { let validator_committee: CommitteeMembers = vec![PublicKey::default()].try_into().unwrap(); let constitution = ContractConstitution { validator_committee, @@ -109,11 +112,7 @@ pub fn create_contract_constitution_schema(contract_id: FixedHash, input: Unblin change_flags: ConstitutionChangeFlags::all(), requirements_for_constitution_change: Some(RequirementsForConstitutionChange { minimum_constitution_committee_signatures: 5, - constitution_committee: Some( - vec![PublicKey::default(); CommitteeMembers::MAX_MEMBERS] - .try_into() - .unwrap(), - ), + constitution_committee: Some(committee.try_into().unwrap()), }), }, initial_reward: 100.into(), @@ -123,18 +122,15 @@ pub fn create_contract_constitution_schema(contract_id: FixedHash, input: Unblin txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: constitution_features) } -pub fn create_contract_acceptance_schema(contract_id: FixedHash, input: UnblindedOutput) -> TransactionSchema { - // let validator_node_public_key = PublicKey::default(); - let validator_node_public_key = - PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); +pub fn create_contract_acceptance_schema( + contract_id: FixedHash, + input: UnblindedOutput, + validator_node_public_key: PublicKey, +) -> TransactionSchema { let signature = Signature::default(); let acceptance_features = OutputFeatures::for_contract_acceptance(contract_id, validator_node_public_key, signature); - let mut tx = - txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: acceptance_features); - tx.output_version = None; - - tx + txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: acceptance_features) } From e5148c77f562fb2116043b6f2dda2f383fb2219f Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 14:52:12 +0100 Subject: [PATCH 09/19] create a custom dan layer error variant --- .../dan_validators/acceptance_validator.rs | 6 +++--- .../core/src/validation/dan_validators/helpers.rs | 15 ++++++++------- base_layer/core/src/validation/error.rs | 2 ++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs index 707cf55df1..d07f14e93b 100644 --- a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -62,7 +62,7 @@ pub fn validate_acceptance( fn get_contract_acceptance(sidechain_feature: &SideChainFeatures) -> Result<&ContractAcceptance, ValidationError> { match sidechain_feature.acceptance.as_ref() { Some(acceptance) => Ok(acceptance), - None => Err(ValidationError::ConsensusError( + None => Err(ValidationError::DanLayerError( "Invalid contract acceptance: acceptance features not found".to_string(), )), } @@ -81,7 +81,7 @@ fn validate_public_key( "Invalid contract acceptance: validator node public key is not in committee ({:?})", validator_node_public_key ); - return Err(ValidationError::ConsensusError(msg)); + return Err(ValidationError::DanLayerError(msg)); } Ok(()) @@ -148,7 +148,7 @@ mod test { let validator = TxDanLayerValidator::new(blockchain.db().clone()); let err = validator.validate(txs.first().unwrap()).unwrap_err(); match err { - ValidationError::ConsensusError(message) => { + ValidationError::DanLayerError(message) => { assert!(message.contains("Invalid contract acceptance: validator node public key is not in committee")) }, _ => panic!("Expected a consensus error"), diff --git a/base_layer/core/src/validation/dan_validators/helpers.rs b/base_layer/core/src/validation/dan_validators/helpers.rs index 55caaf8647..0e74c09342 100644 --- a/base_layer/core/src/validation/dan_validators/helpers.rs +++ b/base_layer/core/src/validation/dan_validators/helpers.rs @@ -38,7 +38,7 @@ pub fn validate_output_type( "Invalid output type: expected {:?} but got {:?}", expected_output_type, output_type ); - return Err(ValidationError::ConsensusError(msg)); + return Err(ValidationError::DanLayerError(msg)); } Ok(()) @@ -47,7 +47,7 @@ pub fn validate_output_type( pub fn get_sidechain_features(output: &TransactionOutput) -> Result<&SideChainFeatures, ValidationError> { match output.features.sidechain_features.as_ref() { Some(features) => Ok(features), - None => Err(ValidationError::ConsensusError( + None => Err(ValidationError::DanLayerError( "Sidechain features not found".to_string(), )), } @@ -62,16 +62,17 @@ pub fn get_contract_constitution( .unwrap(); if contract_outputs.is_empty() { - return Err(ValidationError::ConsensusError( + return Err(ValidationError::DanLayerError( "Contract constitution not found".to_string(), )); } // we assume only one constution should be present in the blockchain + // TODO: create a validation to avoid duplicated constitution publishing let utxo_info = match contract_outputs.first() { Some(value) => value, None => { - return Err(ValidationError::ConsensusError( + return Err(ValidationError::DanLayerError( "Contract constitution UtxoMindInfo not found".to_string(), )) }, @@ -80,7 +81,7 @@ pub fn get_contract_constitution( let constitution_output = match utxo_info.output.as_transaction_output() { Some(value) => value, None => { - return Err(ValidationError::ConsensusError( + return Err(ValidationError::DanLayerError( "Contract constitution output not found".to_string(), )) }, @@ -89,7 +90,7 @@ pub fn get_contract_constitution( let constitution_features = match constitution_output.features.sidechain_features.as_ref() { Some(value) => value, None => { - return Err(ValidationError::ConsensusError( + return Err(ValidationError::DanLayerError( "Contract constitution output features not found".to_string(), )) }, @@ -98,7 +99,7 @@ pub fn get_contract_constitution( let constitution = match constitution_features.constitution.as_ref() { Some(value) => value, None => { - return Err(ValidationError::ConsensusError( + return Err(ValidationError::DanLayerError( "Contract constitution data not found in the output features".to_string(), )) }, diff --git a/base_layer/core/src/validation/error.rs b/base_layer/core/src/validation/error.rs index 09385c071d..7ab54a285b 100644 --- a/base_layer/core/src/validation/error.rs +++ b/base_layer/core/src/validation/error.rs @@ -116,6 +116,8 @@ pub enum ValidationError { InvalidBlockchainVersion { version: u16 }, #[error("Standard transaction contains coinbase output")] ErroneousCoinbaseOutput, + #[error("Digital Asset Network Error: {0}")] + DanLayerError(String), } // ChainStorageError has a ValidationError variant, so to prevent a cyclic dependency we use a string representation in From c697bfcb9d890214b35e136ea6ac1f784ea8d6aa Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 15:12:27 +0100 Subject: [PATCH 10/19] add comments --- .../core/src/validation/dan_validators/acceptance_validator.rs | 3 +++ base_layer/core/src/validation/dan_validators/helpers.rs | 2 +- base_layer/core/src/validation/dan_validators/mod.rs | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs index d07f14e93b..f5f6cb6040 100644 --- a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -35,6 +35,7 @@ use crate::{ validation::ValidationError, }; +/// This validator checks that the provided output corresponds to a valid Contract Acceptance in the DAN layer pub fn validate_acceptance( db: &BlockchainDatabase, output: &TransactionOutput, @@ -59,6 +60,7 @@ pub fn validate_acceptance( Ok(()) } +/// Retrieves a contract acceptance object from the sidechain features, returns an error if not present fn get_contract_acceptance(sidechain_feature: &SideChainFeatures) -> Result<&ContractAcceptance, ValidationError> { match sidechain_feature.acceptance.as_ref() { Some(acceptance) => Ok(acceptance), @@ -68,6 +70,7 @@ fn get_contract_acceptance(sidechain_feature: &SideChainFeatures) -> Result<&Con } } +/// Checks that the validator public key is present as part of the proposed committee in the constitution fn validate_public_key( constitution: ContractConstitution, validator_node_public_key: PublicKey, diff --git a/base_layer/core/src/validation/dan_validators/helpers.rs b/base_layer/core/src/validation/dan_validators/helpers.rs index 0e74c09342..bea70f1f1d 100644 --- a/base_layer/core/src/validation/dan_validators/helpers.rs +++ b/base_layer/core/src/validation/dan_validators/helpers.rs @@ -67,7 +67,7 @@ pub fn get_contract_constitution( )); } - // we assume only one constution should be present in the blockchain + // we assume that only one constitution should be present in the blockchain for any given contract // TODO: create a validation to avoid duplicated constitution publishing let utxo_info = match contract_outputs.first() { Some(value) => value, diff --git a/base_layer/core/src/validation/dan_validators/mod.rs b/base_layer/core/src/validation/dan_validators/mod.rs index 87c854e85d..392bdd14cf 100644 --- a/base_layer/core/src/validation/dan_validators/mod.rs +++ b/base_layer/core/src/validation/dan_validators/mod.rs @@ -34,6 +34,7 @@ mod helpers; #[cfg(test)] mod test_helpers; +/// Validator of Digital Asset Network consensus rules. #[derive(Clone)] pub struct TxDanLayerValidator { db: BlockchainDatabase, From d7f979fba481349a08eb19ddbfa3607e3d2abf6a Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 16:10:14 +0100 Subject: [PATCH 11/19] fix integration test of contract acceptance --- integration_tests/features/ValidatorNode.feature | 5 ++++- integration_tests/fixtures/contract_constitution.json | 3 +-- integration_tests/fixtures/validator_node_id.json | 2 ++ integration_tests/helpers/validatorNodeProcess.js | 3 +++ 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 integration_tests/fixtures/validator_node_id.json diff --git a/integration_tests/features/ValidatorNode.feature b/integration_tests/features/ValidatorNode.feature index b1b7fcf008..d75204bc70 100644 --- a/integration_tests/features/ValidatorNode.feature +++ b/integration_tests/features/ValidatorNode.feature @@ -30,10 +30,13 @@ Feature: Validator Node And I publish a contract definition from file "fixtures/contract_definition.json" on wallet WALLET1 via command line When I mine 8 blocks using wallet WALLET1 on NODE1 Then wallet WALLET1 has at least 1 transactions that are all TRANSACTION_STATUS_MINED_CONFIRMED and not cancelled + And I publish a contract constitution from file "fixtures/contract_constitution.json" on wallet WALLET1 via command line + When I mine 8 blocks using wallet WALLET1 on NODE1 + Then wallet WALLET1 has at least 2 transactions that are all TRANSACTION_STATUS_MINED_CONFIRMED and not cancelled And I have a validator node VN1 connected to base node NODE1 and wallet WALLET1 with "constitiution_auto_accept" set to "false" Then I publish a contract acceptance transaction for the validator node VN1 When I mine 8 blocks using wallet WALLET1 on NODE1 - Then wallet WALLET1 has at least 2 transactions that are all TRANSACTION_STATUS_MINED_CONFIRMED and not cancelled + Then wallet WALLET1 has at least 3 transactions that are all TRANSACTION_STATUS_MINED_CONFIRMED and not cancelled @dan @broken Scenario: Contract auto acceptance diff --git a/integration_tests/fixtures/contract_constitution.json b/integration_tests/fixtures/contract_constitution.json index 084fa0f8ec..cc2208d01d 100644 --- a/integration_tests/fixtures/contract_constitution.json +++ b/integration_tests/fixtures/contract_constitution.json @@ -1,8 +1,7 @@ { "contract_id": "90b1da4524ea0e9479040d906db9194d8af90f28d05ff2d64c0a82eb93125177", "validator_committee": [ - "ccac168b8edd67b10d152d1ed2337efc65da9fc0b6256dd49b3c559032553d44", - "ccac168b8edd67b10d152d1ed2337efc65da9fc0b6256dd49b3c559032553d44", + "608001dffed28d058591cd65eaca11c465165592baf872cf1d984e26fb12b472", "ccac168b8edd67b10d152d1ed2337efc65da9fc0b6256dd49b3c559032553d44" ], "acceptance_parameters": { diff --git a/integration_tests/fixtures/validator_node_id.json b/integration_tests/fixtures/validator_node_id.json new file mode 100644 index 0000000000..51f45d188d --- /dev/null +++ b/integration_tests/fixtures/validator_node_id.json @@ -0,0 +1,2 @@ +// This file is generated by the Tari base node. Any changes will be overwritten. +{"node_id":"5d4a78a0e3fe5ff8dfd16e864f","public_key":"608001dffed28d058591cd65eaca11c465165592baf872cf1d984e26fb12b472","features":{"bits":0},"secret_key":"3b7299635692f60699e0852c57104836fa63e75259c6ddc65cbac4be39e3df07","public_address":"","identity_signature":{"version":0,"signature":{"public_nonce":"def0de34f759b257a95009fa0afb4ab69e61e5385c1a2445e0d5c6ca68f5c933","signature":"dd84fd349002f4ea431c6361c51dd1e9cb7c1e13ab4591f2aedd3dcd24b50e04"},"updated_at":"2022-06-22T14:22:36.417Z"}} \ No newline at end of file diff --git a/integration_tests/helpers/validatorNodeProcess.js b/integration_tests/helpers/validatorNodeProcess.js index 9d106f556a..e88f71dc52 100644 --- a/integration_tests/helpers/validatorNodeProcess.js +++ b/integration_tests/helpers/validatorNodeProcess.js @@ -170,6 +170,9 @@ class ValidatorNodeProcess { let customArgs = { "validator_node.p2p.transport.type": "tcp", + "validator_node.identity_file": path.resolve( + "./fixtures/validator_node_id.json" + ), }; if (this.baseNodeAddress) { customArgs["validator_node.base_node_grpc_address"] = From 66845d172f45cecbbf4fc3e4bdb410fca2ce7f70 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Wed, 22 Jun 2022 17:00:04 +0100 Subject: [PATCH 12/19] fix clipply waning in mempool benches --- base_layer/core/benches/mempool.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/base_layer/core/benches/mempool.rs b/base_layer/core/benches/mempool.rs index 4c6f332edd..d099013fd8 100644 --- a/base_layer/core/benches/mempool.rs +++ b/base_layer/core/benches/mempool.rs @@ -44,12 +44,14 @@ mod benches { CryptoFactories, }, tx, - validation::transaction_validators::{ - MempoolValidator, - TxConsensusValidator, - TxDanLayerValidator, - TxInputAndMaturityValidator, - TxInternalConsistencyValidator, + validation::{ + dan_validators::TxDanLayerValidator, + transaction_validators::{ + MempoolValidator, + TxConsensusValidator, + TxInputAndMaturityValidator, + TxInternalConsistencyValidator, + }, }, }; use tokio::{runtime::Runtime, task}; From 42ab3867e88cd747c780916999ba56393f2cd724 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 23 Jun 2022 08:21:45 +0100 Subject: [PATCH 13/19] increase acceptance timeout in integration tests --- integration_tests/features/support/validator_node_steps.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration_tests/features/support/validator_node_steps.js b/integration_tests/features/support/validator_node_steps.js index aedbc7abc0..fc0e3d1447 100644 --- a/integration_tests/features/support/validator_node_steps.js +++ b/integration_tests/features/support/validator_node_steps.js @@ -157,7 +157,7 @@ Given( Then( "I publish a contract acceptance transaction for the validator node {word}", - { timeout: 20 * 1000 }, + { timeout: 120 * 1000 }, async function (vn_name) { let dan_node = this.getNode(vn_name); let grpc_dan_node = await dan_node.createGrpcClient(); @@ -171,7 +171,7 @@ Then( Then( "I publish a contract update proposal acceptance transaction for the validator node {word}", - { timeout: 20 * 1000 }, + { timeout: 120 * 1000 }, async function (vn_name) { let dan_node = this.getNode(vn_name); let grpc_dan_node = await dan_node.createGrpcClient(); From 85858589c2bcd948bb14ece7b6f4c3827c12e645 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 23 Jun 2022 08:40:45 +0100 Subject: [PATCH 14/19] pass the validator public key as a reference --- .../src/validation/dan_validators/acceptance_validator.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs index f5f6cb6040..ed84e43cd6 100644 --- a/base_layer/core/src/validation/dan_validators/acceptance_validator.rs +++ b/base_layer/core/src/validation/dan_validators/acceptance_validator.rs @@ -46,7 +46,7 @@ pub fn validate_acceptance( let contract_id = sidechain_features.contract_id; let acceptance_features = get_contract_acceptance(sidechain_features)?; - let validator_node_public_key = acceptance_features.validator_node_public_key.clone(); + let validator_node_public_key = &acceptance_features.validator_node_public_key; let constitution = get_contract_constitution(db, contract_id)?; @@ -73,12 +73,12 @@ fn get_contract_acceptance(sidechain_feature: &SideChainFeatures) -> Result<&Con /// Checks that the validator public key is present as part of the proposed committee in the constitution fn validate_public_key( constitution: ContractConstitution, - validator_node_public_key: PublicKey, + validator_node_public_key: &PublicKey, ) -> Result<(), ValidationError> { let is_validator_in_committee = constitution .validator_committee .members() - .contains(&validator_node_public_key); + .contains(validator_node_public_key); if !is_validator_in_committee { let msg = format!( "Invalid contract acceptance: validator node public key is not in committee ({:?})", From 0c53e793049bdc115bba99b6775afa443d15af2e Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 23 Jun 2022 10:14:41 +0100 Subject: [PATCH 15/19] try to fix the indentity file problem in circleci --- .../features/ValidatorNode.feature | 4 +--- .../helpers/validatorNodeProcess.js | 24 +++++++++++++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/integration_tests/features/ValidatorNode.feature b/integration_tests/features/ValidatorNode.feature index 7d0125aa0c..d75204bc70 100644 --- a/integration_tests/features/ValidatorNode.feature +++ b/integration_tests/features/ValidatorNode.feature @@ -21,9 +21,7 @@ Feature: Validator Node And I create 40 NFTs And I mine 3 blocks - # Broken: needs a contract definition before publishing acceptance, however this is currently not easily done because - # GRPC methods need to be added and you cannot use the cli for a wallet while that wallet is already running - @dan @critical @broken + @dan @critical Scenario: Publish contract acceptance Given I have a seed node NODE1 And I have wallet WALLET1 connected to all seed nodes diff --git a/integration_tests/helpers/validatorNodeProcess.js b/integration_tests/helpers/validatorNodeProcess.js index e88f71dc52..05daea2a31 100644 --- a/integration_tests/helpers/validatorNodeProcess.js +++ b/integration_tests/helpers/validatorNodeProcess.js @@ -40,7 +40,7 @@ class ValidatorNodeProcess { this.port = await getFreePort(); this.grpcPort = await getFreePort(); this.name = `ValidatorNode${this.port}-${this.name}`; - this.nodeFile = this.nodeFile || "nodeid.json"; + this.nodeFile = this.nodeFile || "validator_node_id.json"; let instance = 0; do { @@ -163,6 +163,25 @@ class ValidatorNodeProcess { fs.mkdirSync(this.baseDir + "/log", { recursive: true }); } + // to avoid writing permission errors, we copy the reference identity file to the temp folder + let identity_file_name = "validator_node_id.json"; + let identity_source_path = path.resolve( + `./fixtures/${identity_file_name}` + ); + let identity_destination_path = path.resolve( + `${this.baseDir}/${identity_file_name}` + ); + fs.copyFile(identity_source_path, identity_destination_path, (err) => { + if (err) { + console.log( + "Error Found while copying validator identity file to temp folder: ", + err + ); + throw err; + } + console.log("Validator identity file was copied to destination"); + }); + let envs = []; if (!this.excludeTestEnvars) { envs = this.getOverrides(); @@ -170,9 +189,6 @@ class ValidatorNodeProcess { let customArgs = { "validator_node.p2p.transport.type": "tcp", - "validator_node.identity_file": path.resolve( - "./fixtures/validator_node_id.json" - ), }; if (this.baseNodeAddress) { customArgs["validator_node.base_node_grpc_address"] = From 0d381c4e8182628f4948695f4bc9f81e3d301202 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 23 Jun 2022 11:13:22 +0100 Subject: [PATCH 16/19] make the validator identity file writable in tests --- integration_tests/helpers/validatorNodeProcess.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/integration_tests/helpers/validatorNodeProcess.js b/integration_tests/helpers/validatorNodeProcess.js index 05daea2a31..9203aa17cc 100644 --- a/integration_tests/helpers/validatorNodeProcess.js +++ b/integration_tests/helpers/validatorNodeProcess.js @@ -180,6 +180,18 @@ class ValidatorNodeProcess { throw err; } console.log("Validator identity file was copied to destination"); + fs.chmod(identity_destination_path, 0o600, (err) => { + if (err) { + console.log( + "Error Found while changing the permissions of the validator indentity file: ", + err + ); + throw err; + } + console.log( + "Validator identity file permissions successfully modified" + ); + }); }); let envs = []; From 0efafe69acb753c2e2162f58503f5f3ffbe00c0f Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:15:38 +0100 Subject: [PATCH 17/19] fix js code format --- .../features/support/wallet_cli_steps.js | 56 +++++++++---------- integration_tests/features/support/world.js | 14 ++--- integration_tests/helpers/util.js | 4 +- .../helpers/validatorNodeProcess.js | 4 +- integration_tests/helpers/walletProcess.js | 4 +- 5 files changed, 42 insertions(+), 40 deletions(-) diff --git a/integration_tests/features/support/wallet_cli_steps.js b/integration_tests/features/support/wallet_cli_steps.js index 71de02ecd7..731d765cc1 100644 --- a/integration_tests/features/support/wallet_cli_steps.js +++ b/integration_tests/features/support/wallet_cli_steps.js @@ -22,7 +22,7 @@ const { Given, Then, When } = require("@cucumber/cucumber"); const { expect } = require("chai"); -const fs = require('fs'); +const fs = require("fs"); const { waitFor, sleep, byteArrayToHex } = require("../../helpers/util"); const path = require("path"); const uuid = require("uuid"); @@ -299,41 +299,41 @@ Then( ); Then( - "I publish the contract constitution {word} on wallet {word} via command line", - { timeout: 120 * 1000 }, - async function (constitution_name, wallet_name) { - let constitution = this.fetchContractConstitution(constitution_name); - let wallet = this.getWallet(wallet_name); - - let absolute_path = await wallet.writeConstitutionFile(constitution); - let output = await wallet_run_command( - wallet, - `contract publish-constitution ${absolute_path}` - ); - console.log(output.buffer); - } + "I publish the contract constitution {word} on wallet {word} via command line", + { timeout: 120 * 1000 }, + async function (constitution_name, wallet_name) { + let constitution = this.fetchContractConstitution(constitution_name); + let wallet = this.getWallet(wallet_name); + + let absolute_path = await wallet.writeConstitutionFile(constitution); + let output = await wallet_run_command( + wallet, + `contract publish-constitution ${absolute_path}` + ); + console.log(output.buffer); + } ); When( - "I create a contract constitution {word} for contract {word} from file {string}", - async function (constitution_name, contract_name, relative_file_path) { - let absolute_path = path.resolve(relative_file_path); - let contract_id = this.fetchContract(contract_name); + "I create a contract constitution {word} for contract {word} from file {string}", + async function (constitution_name, contract_name, relative_file_path) { + let absolute_path = path.resolve(relative_file_path); + let contract_id = this.fetchContract(contract_name); - let constitution = JSON.parse(fs.readFileSync(absolute_path, 'utf8')); - constitution['contract_id'] = contract_id; + let constitution = JSON.parse(fs.readFileSync(absolute_path, "utf8")); + constitution["contract_id"] = contract_id; - this.saveContractConstitution(constitution_name, constitution); - } + this.saveContractConstitution(constitution_name, constitution); + } ); When( - 'I add {word} to the validator committee on {word}', - async function (vn_name, constitution_name) { - let vn = this.getNode(vn_name); - let constitution = this.fetchContractConstitution(constitution_name); - constitution['validator_committee'] = [vn.getPubKey()]; - } + "I add {word} to the validator committee on {word}", + async function (vn_name, constitution_name) { + let vn = this.getNode(vn_name); + let constitution = this.fetchContractConstitution(constitution_name); + constitution["validator_committee"] = [vn.getPubKey()]; + } ); Then( diff --git a/integration_tests/features/support/world.js b/integration_tests/features/support/world.js index 4450a76cf3..bedd878520 100644 --- a/integration_tests/features/support/world.js +++ b/integration_tests/features/support/world.js @@ -132,13 +132,13 @@ class CustomWorld { const walletGrpcAddress = `127.0.0.1:${walletNode.getGrpcPort()}`; let vn = new ValidatorNodeProcess( - vn_name, - false, - [], - this.logFilePathBaseNode, - undefined, - baseNodeGrpcAddress, - walletGrpcAddress + vn_name, + false, + [], + this.logFilePathBaseNode, + undefined, + baseNodeGrpcAddress, + walletGrpcAddress ); await vn.startNew(); diff --git a/integration_tests/helpers/util.js b/integration_tests/helpers/util.js index 2a6b42fa2a..fc37abfe73 100644 --- a/integration_tests/helpers/util.js +++ b/integration_tests/helpers/util.js @@ -415,12 +415,14 @@ const findUtxoWithOutputMessage = async (wallet, message) => { }); if (accepted.length > 0) { - break + break; } await sleep(5000); } + console.log({ accepted }); + return accepted; }; diff --git a/integration_tests/helpers/validatorNodeProcess.js b/integration_tests/helpers/validatorNodeProcess.js index ac33e5fc0d..267397a9d5 100644 --- a/integration_tests/helpers/validatorNodeProcess.js +++ b/integration_tests/helpers/validatorNodeProcess.js @@ -213,8 +213,8 @@ class ValidatorNodeProcess { customArgs["validator_node.grpc_address"] = this.getGrpcAddress(); } Object.keys(this.options).forEach((k) => { - if (k.startsWith('validator_node.')) { - customArgs[k] = this.options[k] + if (k.startsWith("validator_node.")) { + customArgs[k] = this.options[k]; } }); diff --git a/integration_tests/helpers/walletProcess.js b/integration_tests/helpers/walletProcess.js index 6658575f9b..1bf7e06031 100644 --- a/integration_tests/helpers/walletProcess.js +++ b/integration_tests/helpers/walletProcess.js @@ -469,9 +469,9 @@ class WalletProcess { async writeConstitutionFile(constitution) { let data = JSON.stringify(constitution); - let absolute_path = path.resolve(this.baseDir + '/' + uuid.v4() + '.json'); + let absolute_path = path.resolve(this.baseDir + "/" + uuid.v4() + ".json"); - fs.writeFile(absolute_path, data, 'utf8',(err) => { + fs.writeFile(absolute_path, data, "utf8", (err) => { if (err) { console.log(`Error writing file: ${err}`); } else { From 53aeb314b709722cb4c157d8184579586644a47f Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:16:07 +0100 Subject: [PATCH 18/19] remove console log --- integration_tests/helpers/util.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration_tests/helpers/util.js b/integration_tests/helpers/util.js index fc37abfe73..0ae631b2b3 100644 --- a/integration_tests/helpers/util.js +++ b/integration_tests/helpers/util.js @@ -421,8 +421,6 @@ const findUtxoWithOutputMessage = async (wallet, message) => { await sleep(5000); } - console.log({ accepted }); - return accepted; }; From a10237b50d1fcbf508b613ea42ffd5fd4b09e2ec Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:51:02 +0100 Subject: [PATCH 19/19] fix eslint warnings in integration tests --- integration_tests/features/support/validator_node_steps.js | 5 ++--- integration_tests/features/support/wallet_cli_steps.js | 4 +--- integration_tests/helpers/util.js | 1 + integration_tests/helpers/walletClient.js | 4 ---- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/integration_tests/features/support/validator_node_steps.js b/integration_tests/features/support/validator_node_steps.js index 597109eae7..c8da062b8b 100644 --- a/integration_tests/features/support/validator_node_steps.js +++ b/integration_tests/features/support/validator_node_steps.js @@ -20,10 +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. -const { When, Given, Then } = require("@cucumber/cucumber"); +const { Given, Then } = require("@cucumber/cucumber"); const { expect } = require("chai"); -const { sleep, findUtxoWithOutputMessage } = require("../../helpers/util"); -const ValidatorNodeProcess = require("../../helpers/validatorNodeProcess"); +const { findUtxoWithOutputMessage } = require("../../helpers/util"); Given( "I have a validator node {word} connected to base node {word} and wallet {word}", diff --git a/integration_tests/features/support/wallet_cli_steps.js b/integration_tests/features/support/wallet_cli_steps.js index 731d765cc1..f74472444b 100644 --- a/integration_tests/features/support/wallet_cli_steps.js +++ b/integration_tests/features/support/wallet_cli_steps.js @@ -23,10 +23,8 @@ const { Given, Then, When } = require("@cucumber/cucumber"); const { expect } = require("chai"); const fs = require("fs"); -const { waitFor, sleep, byteArrayToHex } = require("../../helpers/util"); +const { waitFor, sleep } = require("../../helpers/util"); const path = require("path"); -const uuid = require("uuid"); -const dateFormat = require("dateformat"); Given( /I change the password of wallet (.*) to (.*) via command line/, diff --git a/integration_tests/helpers/util.js b/integration_tests/helpers/util.js index 0ae631b2b3..ad1d25dfe7 100644 --- a/integration_tests/helpers/util.js +++ b/integration_tests/helpers/util.js @@ -408,6 +408,7 @@ const findUtxoWithOutputMessage = async (wallet, message) => { let client = await wallet.connectClient(); let accepted = []; + /* eslint-disable no-constant-condition */ while (true) { let found_txs = await client.getCompletedTransactions(); accepted = found_txs.filter((txo) => { diff --git a/integration_tests/helpers/walletClient.js b/integration_tests/helpers/walletClient.js index 220c960405..2f13cf6aaa 100644 --- a/integration_tests/helpers/walletClient.js +++ b/integration_tests/helpers/walletClient.js @@ -8,10 +8,6 @@ const { convertStringToVec, multiAddrToSocket, } = require("./util"); -const dateFormat = require("dateformat"); -const uuid = require("uuid"); -const fs = require("fs"); -const path = require("path"); function transactionStatus() { return [