From 2636cb56ddf1b67ed7bf4c4aea8c05f9369b11d0 Mon Sep 17 00:00:00 2001 From: mrnaveira <47919901+mrnaveira@users.noreply.github.com> Date: Mon, 30 May 2022 14:54:40 +0100 Subject: [PATCH] feat(core): add contract acceptance utxo features (#4145) Description --- - Adds `ContractAcceptance` struct to the side-chain output features - Implements consensus encoding/decoding for `ContractAcceptance` - Updates related gRPC types and conversions Motivation and Context --- Create structs for contract acceptance in the side-chain features. How Has This Been Tested? --- - New unit test for consensus encoding/decoding of the `ContractAcceptance` - Existing unit/integration test pass --- applications/tari_app_grpc/proto/types.proto | 6 ++ .../src/conversions/sidechain_features.rs | 35 +++++++++ .../src/conversions/signature.rs | 9 +++ base_layer/core/src/proto/transaction.proto | 6 ++ base_layer/core/src/proto/transaction.rs | 34 +++++++++ .../transaction_components/output_features.rs | 7 ++ .../side_chain/contract_acceptance.rs | 72 +++++++++++++++++++ .../transaction_components/side_chain/mod.rs | 3 + .../side_chain/sidechain_features.rs | 17 ++++- 9 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 base_layer/core/src/transactions/transaction_components/side_chain/contract_acceptance.rs diff --git a/applications/tari_app_grpc/proto/types.proto b/applications/tari_app_grpc/proto/types.proto index 6bb4ed89ad..bec35c4d04 100644 --- a/applications/tari_app_grpc/proto/types.proto +++ b/applications/tari_app_grpc/proto/types.proto @@ -232,6 +232,7 @@ message SideChainFeatures { bytes contract_id = 1; ContractDefinition definition = 2; ContractConstitution constitution = 3; + ContractAcceptance acceptance = 4; } message ContractConstitution { @@ -328,6 +329,11 @@ message FunctionRef { uint32 function_id = 2; } +message ContractAcceptance { + bytes validator_node_public_key = 1; + Signature signature = 2; +} + // The components of the block or transaction. The same struct can be used for either, since in Mimblewimble, // cut-through means that blocks and transactions have the same structure. The inputs, outputs and kernels should // be sorted by their Blake2b-256bit digest hash diff --git a/applications/tari_app_grpc/src/conversions/sidechain_features.rs b/applications/tari_app_grpc/src/conversions/sidechain_features.rs index 2e2e6a2323..6adc1abb4e 100644 --- a/applications/tari_app_grpc/src/conversions/sidechain_features.rs +++ b/applications/tari_app_grpc/src/conversions/sidechain_features.rs @@ -29,6 +29,7 @@ use tari_core::transactions::transaction_components::{ CommitteeMembers, ConstitutionChangeFlags, ConstitutionChangeRules, + ContractAcceptance, ContractAcceptanceRequirements, ContractConstitution, ContractDefinition, @@ -49,6 +50,7 @@ impl From for grpc::SideChainFeatures { contract_id: value.contract_id.to_vec(), definition: value.definition.map(Into::into), constitution: value.constitution.map(Into::into), + acceptance: value.acceptance.map(Into::into), } } } @@ -59,11 +61,13 @@ impl TryFrom for SideChainFeatures { fn try_from(features: grpc::SideChainFeatures) -> Result { let definition = features.definition.map(ContractDefinition::try_from).transpose()?; let constitution = features.constitution.map(ContractConstitution::try_from).transpose()?; + let acceptance = features.acceptance.map(ContractAcceptance::try_from).transpose()?; Ok(Self { contract_id: features.contract_id.try_into().map_err(|_| "Invalid contract_id")?, definition, constitution, + acceptance, }) } } @@ -395,3 +399,34 @@ impl TryFrom for CommitteeMembers { Ok(members) } } + +//---------------------------------- ContractAcceptance --------------------------------------------// + +impl From for grpc::ContractAcceptance { + fn from(value: ContractAcceptance) -> Self { + Self { + validator_node_public_key: value.validator_node_public_key.as_bytes().to_vec(), + signature: Some(value.signature.into()), + } + } +} + +impl TryFrom for ContractAcceptance { + type Error = String; + + fn try_from(value: grpc::ContractAcceptance) -> Result { + let validator_node_public_key = + PublicKey::from_bytes(value.validator_node_public_key.as_bytes()).map_err(|err| format!("{:?}", err))?; + + let signature = value + .signature + .ok_or_else(|| "signature not provided".to_string())? + .try_into() + .map_err(|_| "signaturecould not be converted".to_string())?; + + Ok(Self { + validator_node_public_key, + signature, + }) + } +} diff --git a/applications/tari_app_grpc/src/conversions/signature.rs b/applications/tari_app_grpc/src/conversions/signature.rs index 03c1eb47b4..c9fb21ceec 100644 --- a/applications/tari_app_grpc/src/conversions/signature.rs +++ b/applications/tari_app_grpc/src/conversions/signature.rs @@ -38,3 +38,12 @@ impl TryFrom for Signature { Ok(Self::new(public_nonce, signature)) } } + +impl From for grpc::Signature { + fn from(sig: Signature) -> Self { + Self { + public_nonce: sig.get_public_nonce().to_vec(), + signature: sig.get_signature().to_vec(), + } + } +} diff --git a/base_layer/core/src/proto/transaction.proto b/base_layer/core/src/proto/transaction.proto index d534fff806..e7f3954407 100644 --- a/base_layer/core/src/proto/transaction.proto +++ b/base_layer/core/src/proto/transaction.proto @@ -105,6 +105,7 @@ message SideChainFeatures { bytes contract_id = 1; ContractDefinition definition = 2; ContractConstitution constitution = 3; + ContractAcceptance acceptance = 4; } message ContractConstitution { @@ -200,6 +201,11 @@ message FunctionRef { uint32 function_id = 2; } +message ContractAcceptance { + bytes validator_node_public_key = 1; + Signature signature = 2; +} + // The components of the block or transaction. The same struct can be used for either, since in Mimblewimble, // cut-through means that blocks and transactions have the same structure. The inputs, outputs and kernels should // be sorted by their Blake2b-256bit digest hash diff --git a/base_layer/core/src/proto/transaction.rs b/base_layer/core/src/proto/transaction.rs index 1804613ab1..a3f942346f 100644 --- a/base_layer/core/src/proto/transaction.rs +++ b/base_layer/core/src/proto/transaction.rs @@ -46,6 +46,7 @@ use crate::{ CommitteeMembers, ConstitutionChangeFlags, ConstitutionChangeRules, + ContractAcceptance, ContractAcceptanceRequirements, ContractConstitution, ContractDefinition, @@ -350,6 +351,7 @@ impl From for proto::types::SideChainFeatures { contract_id: value.contract_id.to_vec(), definition: value.definition.map(Into::into), constitution: value.constitution.map(Into::into), + acceptance: value.acceptance.map(Into::into), } } } @@ -360,11 +362,13 @@ impl TryFrom for SideChainFeatures { fn try_from(features: proto::types::SideChainFeatures) -> Result { let definition = features.definition.map(ContractDefinition::try_from).transpose()?; let constitution = features.constitution.map(ContractConstitution::try_from).transpose()?; + let acceptance = features.acceptance.map(ContractAcceptance::try_from).transpose()?; Ok(Self { contract_id: features.contract_id.try_into().map_err(|_| "Invalid contract_id")?, definition, constitution, + acceptance, }) } } @@ -439,6 +443,36 @@ impl TryFrom for ContractAcceptanc } } +//---------------------------------- ContractAcceptance --------------------------------------------// + +impl From for proto::types::ContractAcceptance { + fn from(value: ContractAcceptance) -> Self { + Self { + validator_node_public_key: value.validator_node_public_key.as_bytes().to_vec(), + signature: Some(value.signature.into()), + } + } +} + +impl TryFrom for ContractAcceptance { + type Error = String; + + fn try_from(value: proto::types::ContractAcceptance) -> Result { + let validator_node_public_key = + PublicKey::from_bytes(value.validator_node_public_key.as_bytes()).map_err(|err| format!("{:?}", err))?; + let signature = value + .signature + .ok_or_else(|| "signature not provided".to_string())? + .try_into() + .map_err(|err: ByteArrayError| err.to_string())?; + + Ok(Self { + validator_node_public_key, + signature, + }) + } +} + //---------------------------------- SideChainConsensus --------------------------------------------// impl From for proto::types::SideChainConsensus { fn from(value: SideChainConsensus) -> Self { 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 3535f50c3b..a04d58ac9a 100644 --- a/base_layer/core/src/transactions/transaction_components/output_features.rs +++ b/base_layer/core/src/transactions/transaction_components/output_features.rs @@ -443,6 +443,8 @@ impl Display for OutputFeatures { mod test { use std::{convert::TryInto, io::ErrorKind, iter}; + use tari_common_types::types::Signature; + use super::*; use crate::{ consensus::check_consensus_encoding_correctness, @@ -457,6 +459,7 @@ mod test { SideChainConsensus, }, vec_into_fixed_string, + ContractAcceptance, ContractConstitution, ContractDefinition, ContractSpecification, @@ -528,6 +531,10 @@ mod test { ], }, }), + acceptance: Some(ContractAcceptance { + validator_node_public_key: PublicKey::default(), + signature: Signature::default(), + }), }), // Deprecated parent_public_key: Some(PublicKey::default()), diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/contract_acceptance.rs b/base_layer/core/src/transactions/transaction_components/side_chain/contract_acceptance.rs new file mode 100644 index 0000000000..ce9b9c47f5 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_components/side_chain/contract_acceptance.rs @@ -0,0 +1,72 @@ +// 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::io::{Error, Read, Write}; + +use serde::{Deserialize, Serialize}; +use tari_common_types::types::{PublicKey, Signature}; + +use crate::consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized}; + +#[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq)] +pub struct ContractAcceptance { + pub validator_node_public_key: PublicKey, + pub signature: Signature, +} + +impl ConsensusEncoding for ContractAcceptance { + fn consensus_encode(&self, writer: &mut W) -> Result<(), Error> { + self.validator_node_public_key.consensus_encode(writer)?; + self.signature.consensus_encode(writer)?; + + Ok(()) + } +} + +impl ConsensusEncodingSized for ContractAcceptance {} + +impl ConsensusDecoding for ContractAcceptance { + fn consensus_decode(reader: &mut R) -> Result { + Ok(Self { + validator_node_public_key: PublicKey::consensus_decode(reader)?, + signature: Signature::consensus_decode(reader)?, + }) + } +} + +#[cfg(test)] +mod tests { + use tari_common_types::types::PublicKey; + + use super::*; + use crate::consensus::check_consensus_encoding_correctness; + + #[test] + fn it_encodes_and_decodes_correctly() { + let subject = ContractAcceptance { + validator_node_public_key: PublicKey::default(), + signature: Signature::default(), + }; + + check_consensus_encoding_correctness(subject).unwrap(); + } +} diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/mod.rs b/base_layer/core/src/transactions/transaction_components/side_chain/mod.rs index b431606930..c861dea8ee 100644 --- a/base_layer/core/src/transactions/transaction_components/side_chain/mod.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/mod.rs @@ -20,6 +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. +mod contract_acceptance; +pub use contract_acceptance::ContractAcceptance; + mod contract_constitution; pub use contract_constitution::{ CheckpointParameters, diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/sidechain_features.rs b/base_layer/core/src/transactions/transaction_components/side_chain/sidechain_features.rs index a4d7937ad1..d8ce3d6232 100644 --- a/base_layer/core/src/transactions/transaction_components/side_chain/sidechain_features.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/sidechain_features.rs @@ -25,7 +25,7 @@ use std::io::{Error, Read, Write}; use serde::{Deserialize, Serialize}; use tari_common_types::types::FixedHash; -use super::ContractDefinition; +use super::{ContractAcceptance, ContractDefinition}; use crate::{ consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized}, transactions::transaction_components::ContractConstitution, @@ -36,6 +36,7 @@ pub struct SideChainFeatures { pub contract_id: FixedHash, pub definition: Option, pub constitution: Option, + pub acceptance: Option, } impl SideChainFeatures { @@ -53,6 +54,7 @@ impl ConsensusEncoding for SideChainFeatures { self.contract_id.consensus_encode(writer)?; self.definition.consensus_encode(writer)?; self.constitution.consensus_encode(writer)?; + self.acceptance.consensus_encode(writer)?; Ok(()) } } @@ -65,6 +67,7 @@ impl ConsensusDecoding for SideChainFeatures { contract_id: FixedHash::consensus_decode(reader)?, definition: ConsensusDecoding::consensus_decode(reader)?, constitution: ConsensusDecoding::consensus_decode(reader)?, + acceptance: ConsensusDecoding::consensus_decode(reader)?, }) } } @@ -80,6 +83,7 @@ impl SideChainFeaturesBuilder { contract_id, definition: None, constitution: None, + acceptance: None, }, } } @@ -94,6 +98,11 @@ impl SideChainFeaturesBuilder { self } + pub fn with_contract_acceptance(mut self, contract_acceptance: ContractAcceptance) -> Self { + self.features.acceptance = Some(contract_acceptance); + self + } + pub fn finish(self) -> SideChainFeatures { self.features } @@ -103,7 +112,7 @@ impl SideChainFeaturesBuilder { mod tests { use std::convert::TryInto; - use tari_common_types::types::PublicKey; + use tari_common_types::types::{PublicKey, Signature}; use super::*; use crate::{ @@ -177,6 +186,10 @@ mod tests { ], }, }), + acceptance: Some(ContractAcceptance { + validator_node_public_key: PublicKey::default(), + signature: Signature::default(), + }), }; check_consensus_encoding_correctness(subject).unwrap();