-
Notifications
You must be signed in to change notification settings - Fork 220
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(base_layer): basic validations for proposals, proposal acceptanc…
…es and amendments (#4238) Description --- * Added new base layer validations for: * Contract update proposals: * The corresponding constitution must be already published * No duplicated proposals (same `contract_id` and `proposal_id`) can be published * Contract update proposal acceptances: * The corresponding update proposal must be already published * No duplicated acceptances (same proposal and validator node) can be published * Contract amendments: * The corresponding update proposal must be already published * No duplicated amendments can be published for the same proposal * The `updated_constitution` ratified in the amendment must match exactly the one in the update proposal * Added new unit tests for all the new validations * Adapted an existing integration test for amendments due to the new validations * Added `ContractAmendment` to the base layer database index (`lmdb`) * Refactored some redundant helper code for validations Motivation and Context --- The base layer should perform basic validations (required prior data, no duplications and consistency) on update proposals, proposal acceptances and amendments How Has This Been Tested? --- * New unit test to check the new validation * The existing unit and integration tests pass
- Loading branch information
Showing
10 changed files
with
776 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
211 changes: 211 additions & 0 deletions
211
base_layer/core/src/validation/dan_validators/amendment_validator.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
// 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 tari_utilities::hex::Hex; | ||
|
||
use super::helpers::{ | ||
fetch_contract_features, | ||
fetch_contract_update_proposal, | ||
get_sidechain_features, | ||
validate_output_type, | ||
}; | ||
use crate::{ | ||
chain_storage::{BlockchainBackend, BlockchainDatabase}, | ||
transactions::transaction_components::{ | ||
ContractAmendment, | ||
ContractUpdateProposal, | ||
OutputType, | ||
SideChainFeatures, | ||
TransactionOutput, | ||
}, | ||
validation::ValidationError, | ||
}; | ||
|
||
pub fn validate_amendment<B: BlockchainBackend>( | ||
db: &BlockchainDatabase<B>, | ||
output: &TransactionOutput, | ||
) -> Result<(), ValidationError> { | ||
validate_output_type(output, OutputType::ContractAmendment)?; | ||
|
||
let sidechain_features = get_sidechain_features(output)?; | ||
let contract_id = sidechain_features.contract_id; | ||
|
||
let amendment = get_contract_amendment(sidechain_features)?; | ||
let proposal_id = amendment.proposal_id; | ||
let proposal = fetch_contract_update_proposal(db, contract_id, proposal_id)?; | ||
|
||
validate_duplication(db, contract_id, proposal_id)?; | ||
validate_updated_constiution(amendment, &proposal)?; | ||
|
||
Ok(()) | ||
} | ||
|
||
fn get_contract_amendment(sidechain_feature: &SideChainFeatures) -> Result<&ContractAmendment, ValidationError> { | ||
match sidechain_feature.amendment.as_ref() { | ||
Some(amendment) => Ok(amendment), | ||
None => Err(ValidationError::DanLayerError( | ||
"Contract amendment features not found".to_string(), | ||
)), | ||
} | ||
} | ||
|
||
fn validate_duplication<B: BlockchainBackend>( | ||
db: &BlockchainDatabase<B>, | ||
contract_id: FixedHash, | ||
proposal_id: u64, | ||
) -> Result<(), ValidationError> { | ||
let features = fetch_contract_features(db, contract_id, OutputType::ContractAmendment)?; | ||
match features | ||
.into_iter() | ||
.filter_map(|feature| feature.amendment) | ||
.find(|amendment| amendment.proposal_id == proposal_id) | ||
{ | ||
Some(_) => { | ||
let msg = format!( | ||
"Duplicated amendment for contract_id ({:?}) and proposal_id ({:?})", | ||
contract_id.to_hex(), | ||
proposal_id, | ||
); | ||
Err(ValidationError::DanLayerError(msg)) | ||
}, | ||
None => Ok(()), | ||
} | ||
} | ||
|
||
fn validate_updated_constiution( | ||
amendment: &ContractAmendment, | ||
proposal: &ContractUpdateProposal, | ||
) -> Result<(), ValidationError> { | ||
if amendment.updated_constitution != proposal.updated_constitution { | ||
return Err(ValidationError::DanLayerError( | ||
"The updated_constitution of the amendment does not match the one in the update proposal".to_string(), | ||
)); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use tari_common_types::types::PublicKey; | ||
|
||
use crate::validation::dan_validators::test_helpers::{ | ||
assert_dan_error, | ||
create_block, | ||
create_contract_amendment_schema, | ||
init_test_blockchain, | ||
publish_constitution, | ||
publish_definition, | ||
publish_update_proposal, | ||
schema_to_transaction, | ||
}; | ||
|
||
#[test] | ||
fn proposal_must_exist() { | ||
// initialise a blockchain with enough funds to spend at contract transactions | ||
let (mut blockchain, change) = init_test_blockchain(); | ||
|
||
// publish the contract definition into a block | ||
let contract_id = publish_definition(&mut blockchain, change[0].clone()); | ||
|
||
// publish the contract constitution into a block | ||
let validator_node_public_key = PublicKey::default(); | ||
let committee = vec![validator_node_public_key]; | ||
publish_constitution(&mut blockchain, change[1].clone(), contract_id, committee.clone()); | ||
|
||
// skip the publication of the contract update proposal | ||
|
||
// create an amendment transaction | ||
let proposal_id = 1; | ||
let schema = create_contract_amendment_schema(contract_id, change[1].clone(), proposal_id, committee); | ||
let (tx, _) = schema_to_transaction(&schema); | ||
|
||
// try to validate the acceptance transaction and check that we get the error | ||
assert_dan_error(&blockchain, &tx, "Contract update proposal not found"); | ||
} | ||
|
||
#[test] | ||
fn it_rejects_duplicated_amendments() { | ||
// initialise a blockchain with enough funds to spend at contract transactions | ||
let (mut blockchain, change) = init_test_blockchain(); | ||
|
||
// publish the contract definition into a block | ||
let contract_id = publish_definition(&mut blockchain, change[0].clone()); | ||
|
||
// publish the contract constitution into a block | ||
let validator_node_public_key = PublicKey::default(); | ||
let committee = vec![validator_node_public_key]; | ||
publish_constitution(&mut blockchain, change[1].clone(), contract_id, committee.clone()); | ||
|
||
// publish a contract update proposal into a block | ||
let proposal_id: u64 = 1; | ||
publish_update_proposal( | ||
&mut blockchain, | ||
change[2].clone(), | ||
contract_id, | ||
proposal_id, | ||
committee.clone(), | ||
); | ||
|
||
// publish the contract amendment into a block | ||
let schema = create_contract_amendment_schema(contract_id, change[3].clone(), proposal_id, committee.clone()); | ||
create_block(&mut blockchain, "amendment", schema); | ||
|
||
// create a (duplicated) contract amendment transaction | ||
let schema = create_contract_amendment_schema(contract_id, change[4].clone(), proposal_id, committee); | ||
let (tx, _) = schema_to_transaction(&schema); | ||
|
||
// try to validate the duplicated amendment transaction and check that we get the error | ||
assert_dan_error(&blockchain, &tx, "Duplicated amendment"); | ||
} | ||
|
||
#[test] | ||
fn it_rejects_altered_updated_constitution() { | ||
// initialise a blockchain with enough funds to spend at contract transactions | ||
let (mut blockchain, change) = init_test_blockchain(); | ||
|
||
// publish the contract definition into a block | ||
let contract_id = publish_definition(&mut blockchain, change[0].clone()); | ||
|
||
// publish the contract constitution into a block | ||
let validator_node_public_key = PublicKey::default(); | ||
let committee = vec![validator_node_public_key]; | ||
publish_constitution(&mut blockchain, change[1].clone(), contract_id, committee.clone()); | ||
|
||
// publish a contract update proposal into a block | ||
let proposal_id: u64 = 1; | ||
publish_update_proposal(&mut blockchain, change[2].clone(), contract_id, proposal_id, committee); | ||
|
||
// create an amendment with an altered committee (compared to the proposal) | ||
let altered_committee = vec![]; | ||
let schema = create_contract_amendment_schema(contract_id, change[4].clone(), proposal_id, altered_committee); | ||
let (tx, _) = schema_to_transaction(&schema); | ||
|
||
// try to validate the amendment transaction and check that we get the error | ||
assert_dan_error( | ||
&blockchain, | ||
&tx, | ||
"The updated_constitution of the amendment does not match the one in the update proposal", | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.