Skip to content

Commit

Permalink
feat: proposal acceptance signatures are submitted and validated (#4288)
Browse files Browse the repository at this point in the history
Description
---
* feat(base_layer): added proposal acceptance signature validation
* refactor(base_layer): DRYing the check of proposal in transaction outputs with a new `output_contains_proposal` function
* feat(dan_layer): `AcceptanceManager` now also publishes update proposals, and with the correct signature
* fix(integration_test): `proposal_id` value in fixtures is not consistent

Motivation and Context
---
Proposal acceptances need to be signed by the validator node submitting them. Also, the base layer should validate that the signature is valid.

Similar to [recent PRs](#4269), the proposal acceptance signatures follow:
`e = H_1(signer_public_key || public_nonce || H_2(contract_id||proposal_id||proposal_commitment))`

How Has This Been Tested?
---
* New unit test to check the base layer validation of proposal acceptance signatures
* For the validation node part, the existing integration test do not raise the signature error
  • Loading branch information
mrnaveira committed Jul 13, 2022
1 parent 75a8f59 commit 2bf7efe
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 67 deletions.
Expand Up @@ -527,7 +527,7 @@ impl ContractWorkerManager {
let mut acceptance_manager = self.acceptance_manager.clone();

let tx_id = acceptance_manager
.publish_acceptance(&self.identity, &contract.contract_id)
.publish_constitution_acceptance(&self.identity, &contract.contract_id)
.await?;
info!(
target: LOG_TARGET,
Expand Down
Expand Up @@ -26,11 +26,11 @@ use std::{

use futures::channel::mpsc;
use tari_app_grpc::tari_rpc::{self as rpc, TransactionOutput};
use tari_common_types::types::{FixedHash, PublicKey, Signature};
use tari_common_types::types::{FixedHash, PublicKey};
use tari_comms::NodeIdentity;
use tari_crypto::tari_utilities::ByteArray;
use tari_dan_core::{
services::{AcceptanceManager, AssetProcessor, AssetProxy, ServiceSpecification, WalletClient},
services::{AcceptanceManager, AssetProcessor, AssetProxy, ServiceSpecification},
storage::DbFactory,
};
use tari_dan_engine::instructions::Instruction;
Expand All @@ -44,7 +44,6 @@ pub struct ValidatorNodeGrpcServer<TServiceSpecification: ServiceSpecification>
db_factory: TServiceSpecification::DbFactory,
asset_processor: TServiceSpecification::AssetProcessor,
asset_proxy: TServiceSpecification::AssetProxy,
wallet_client: TServiceSpecification::WalletClient,
acceptance_manager: TServiceSpecification::AcceptanceManager,
}

Expand All @@ -54,15 +53,13 @@ impl<TServiceSpecification: ServiceSpecification> ValidatorNodeGrpcServer<TServi
db_factory: TServiceSpecification::DbFactory,
asset_processor: TServiceSpecification::AssetProcessor,
asset_proxy: TServiceSpecification::AssetProxy,
wallet_client: TServiceSpecification::WalletClient,
acceptance_manager: TServiceSpecification::AcceptanceManager,
) -> Self {
Self {
node_identity,
db_factory,
asset_processor,
asset_proxy,
wallet_client,
acceptance_manager,
}
}
Expand All @@ -84,7 +81,7 @@ impl<TServiceSpecification: ServiceSpecification + 'static> rpc::validator_node_
FixedHash::try_from(request.contract_id).map_err(|err| tonic::Status::invalid_argument(err.to_string()))?;

match acceptance_manager
.publish_acceptance(&self.node_identity, &contract_id)
.publish_constitution_acceptance(&self.node_identity, &contract_id)
.await
{
Ok(tx_id) => Ok(Response::new(rpc::PublishContractAcceptanceResponse {
Expand All @@ -105,19 +102,13 @@ impl<TServiceSpecification: ServiceSpecification + 'static> rpc::validator_node_
&self,
request: tonic::Request<rpc::PublishContractUpdateProposalAcceptanceRequest>,
) -> Result<Response<rpc::PublishContractUpdateProposalAcceptanceResponse>, tonic::Status> {
let mut wallet_client = self.wallet_client.clone();
let mut acceptance_manager = self.acceptance_manager.clone();
let request = request.into_inner();
let contract_id = FixedHash::try_from(request.contract_id).unwrap_or_default();
let validator_node_public_key = self.node_identity.public_key();
let signature = Signature::default();
let proposal_id = request.proposal_id;

match wallet_client
.submit_contract_update_proposal_acceptance(
&contract_id,
request.proposal_id,
validator_node_public_key,
&signature,
)
match acceptance_manager
.publish_proposal_acceptance(&self.node_identity, &contract_id, proposal_id)
.await
{
Ok(tx_id) => Ok(Response::new(rpc::PublishContractUpdateProposalAcceptanceResponse {
Expand Down
1 change: 0 additions & 1 deletion applications/tari_validator_node/src/main.rs
Expand Up @@ -162,7 +162,6 @@ async fn run_node(config: &ApplicationConfig) -> Result<(), ExitError> {
db_factory.clone(),
asset_processor,
asset_proxy,
wallet_client,
acceptance_manager,
);

Expand Down
Expand Up @@ -416,6 +416,19 @@ impl OutputFeatures {
.and_then(|f| f.constitution.as_ref())
.map(|c| &c.validator_committee)
}

pub fn contains_sidechain_proposal(&self, contract_id: &FixedHash, proposal_id: u64) -> bool {
let sidechain_features = match self.sidechain_features.as_ref() {
Some(value) => value,
None => return false,
};
let proposal = match sidechain_features.update_proposal.as_ref() {
Some(value) => value,
None => return false,
};

sidechain_features.contract_id == *contract_id && proposal.proposal_id == proposal_id
}
}

impl ConsensusEncoding for OutputFeatures {
Expand Down
@@ -0,0 +1,47 @@
// 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 digest::Digest;
use tari_common_types::types::{Commitment, FixedHash, HashDigest};
use tari_utilities::ByteArray;

#[derive(Debug, Clone, Copy)]
pub struct ContractUpdateProposalAcceptanceChallenge(FixedHash);

impl ContractUpdateProposalAcceptanceChallenge {
pub fn new(constiution_commitment: &Commitment, contract_id: &FixedHash, proposal_id: u64) -> Self {
// TODO: Use new tari_crypto domain-separated hashing
let hash = HashDigest::new()
.chain(constiution_commitment.as_bytes())
.chain(contract_id.as_slice())
.chain(proposal_id.to_le_bytes())
.finalize()
.into();
Self(hash)
}
}

impl AsRef<[u8]> for ContractUpdateProposalAcceptanceChallenge {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
Expand Up @@ -26,6 +26,9 @@ pub use contract_acceptance::ContractAcceptance;
mod contract_acceptance_challenge;
pub use contract_acceptance_challenge::ContractAcceptanceChallenge;

mod contract_update_proposal_acceptance_challenge;
pub use contract_update_proposal_acceptance_challenge::ContractUpdateProposalAcceptanceChallenge;

mod contract_constitution;
pub use contract_constitution::{
CheckpointParameters,
Expand Down
26 changes: 18 additions & 8 deletions base_layer/core/src/validation/dan_validators/test_helpers.rs
Expand Up @@ -48,6 +48,7 @@ use crate::{
ContractDefinition,
ContractSpecification,
ContractUpdateProposal,
ContractUpdateProposalAcceptanceChallenge,
OutputFeatures,
RequirementsForConstitutionChange,
SideChainConsensus,
Expand Down Expand Up @@ -276,22 +277,31 @@ pub fn create_contract_proposal_schema(

pub fn create_contract_update_proposal_acceptance_schema(
contract_id: FixedHash,
commitment: Commitment,
input: UnblindedOutput,
proposal_id: u64,
validator_node_public_key: PublicKey,
private_key: RistrettoSecretKey,
public_key: PublicKey,
) -> TransactionSchema {
let signature = Signature::default();
let signature = create_proposal_acceptance_signature(contract_id, proposal_id, commitment, private_key);

let acceptance_features = OutputFeatures::for_contract_update_proposal_acceptance(
contract_id,
proposal_id,
validator_node_public_key,
signature,
);
let acceptance_features =
OutputFeatures::for_contract_update_proposal_acceptance(contract_id, proposal_id, public_key, signature);

txn_schema!(from: vec![input], to: vec![0.into()], fee: 5.into(), lock: 0, features: acceptance_features)
}

pub fn create_proposal_acceptance_signature(
contract_id: FixedHash,
proposal_id: u64,
commitment: Commitment,
private_key: RistrettoSecretKey,
) -> Signature {
let challenge = ContractUpdateProposalAcceptanceChallenge::new(&commitment, &contract_id, proposal_id);

SignerSignature::sign(&private_key, &challenge).signature().clone()
}

pub fn create_contract_amendment_schema(
contract_id: FixedHash,
input: UnblindedOutput,
Expand Down

0 comments on commit 2bf7efe

Please sign in to comment.