Skip to content

Commit

Permalink
feat: publication of contract update proposal acceptances (#4199)
Browse files Browse the repository at this point in the history
Description
---
* New gRPC method in the validator node for publishing a proposal acceptance:
    * The arguments are the `contract_id` and the `proposal_id`
    * The validator node internally uses its own public key for building the acceptance.
    * The signature of the acceptance only has a mock value for now. In the future we probably would want it to be the signature of a hash of a contract update proposal.
    * Calls the new gRPC method in the wallet to publish the transaction
* New gRPC method in the wallet to submit a proposal acceptance. It builds and sends to the network a transaction with the corresponding type and features
* For now, we do not check if the `proposal_id` is valid, or any other base layer validation.

Motivation and Context
---
Validator nodes need to be able to publish contract update proposal acceptances, for contracts in which they are included as committee members and an update proposal is issued.

This PR provides a gRPC method in the validator node (and a corresponding one in the wallet) to publish proposal acceptances.

How Has This Been Tested?
---
New integration test that publishes and mines a proposal acceptance through the new validator node gRPC endpoint
  • Loading branch information
mrnaveira authored Jun 15, 2022
1 parent 8874114 commit e3b2b9b
Show file tree
Hide file tree
Showing 16 changed files with 295 additions and 2 deletions.
11 changes: 11 additions & 0 deletions applications/tari_app_grpc/proto/validator_node.proto
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ service ValidatorNode {
rpc InvokeMethod(InvokeMethodRequest) returns (InvokeMethodResponse);
rpc GetConstitutionRequests(GetConstitutionRequestsRequest) returns (stream TransactionOutput);
rpc PublishContractAcceptance(PublishContractAcceptanceRequest) returns (PublishContractAcceptanceResponse);
rpc PublishContractUpdateProposalAcceptance(PublishContractUpdateProposalAcceptanceRequest) returns (PublishContractUpdateProposalAcceptanceResponse);
}

message GetConstitutionRequestsRequest {
Expand All @@ -52,6 +53,16 @@ message PublishContractAcceptanceResponse {
uint64 tx_id = 2;
}

message PublishContractUpdateProposalAcceptanceRequest {
bytes contract_id = 1;
uint64 proposal_id = 2;
}

message PublishContractUpdateProposalAcceptanceResponse {
string status = 1;
uint64 tx_id = 2;
}

message GetMetadataResponse {
repeated SidechainMetadata sidechains = 1;
}
Expand Down
12 changes: 12 additions & 0 deletions applications/tari_app_grpc/proto/wallet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ service Wallet {
rpc SetBaseNode(SetBaseNodeRequest) returns (SetBaseNodeResponse);

rpc SubmitContractAcceptance(SubmitContractAcceptanceRequest) returns (SubmitContractAcceptanceResponse);
rpc SubmitContractUpdateProposalAcceptance(SubmitContractUpdateProposalAcceptanceRequest) returns (SubmitContractUpdateProposalAcceptanceResponse);
}

message GetVersionRequest { }
Expand Down Expand Up @@ -309,6 +310,17 @@ message SubmitContractAcceptanceResponse {
uint64 tx_id = 1;
}

message SubmitContractUpdateProposalAcceptanceRequest {
bytes contract_id = 1;
uint64 proposal_id = 2;
bytes validator_node_public_key = 3;
Signature signature = 4;
}

message SubmitContractUpdateProposalAcceptanceResponse {
uint64 tx_id = 1;
}

message GetOwnedAssetsResponse {
repeated Asset assets = 1;
}
Expand Down
49 changes: 49 additions & 0 deletions applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ use tari_app_grpc::{
SetBaseNodeResponse,
SubmitContractAcceptanceRequest,
SubmitContractAcceptanceResponse,
SubmitContractUpdateProposalAcceptanceRequest,
SubmitContractUpdateProposalAcceptanceResponse,
TransactionDirection,
TransactionInfo,
TransactionStatus,
Expand Down Expand Up @@ -696,6 +698,53 @@ impl wallet_server::Wallet for WalletGrpcServer {
}))
}

async fn submit_contract_update_proposal_acceptance(
&self,
request: Request<SubmitContractUpdateProposalAcceptanceRequest>,
) -> Result<Response<SubmitContractUpdateProposalAcceptanceResponse>, Status> {
let mut asset_manager = self.wallet.asset_manager.clone();
let mut transaction_service = self.wallet.transaction_service.clone();
let message = request.into_inner();

let contract_id = FixedHash::try_from(message.contract_id)
.map_err(|err| Status::invalid_argument(format!("Invalid contract_id:{:?}", err)))?;

let validator_node_public_key =
PublicKey::from_bytes(message.validator_node_public_key.as_slice()).map_err(|e| {
Status::invalid_argument(format!("Validator node public key was not a valid pub key:{}", e))
})?;

let signature = message
.signature
.ok_or_else(|| Status::invalid_argument("signature not provided"))?;
let signature =
Signature::try_from(signature).map_err(|e| Status::invalid_argument(format!("Invalid signature:{}", e)))?;

let (tx_id, transaction) = asset_manager
.create_contract_update_proposal_acceptance(
&contract_id,
message.proposal_id,
&validator_node_public_key,
&signature,
)
.await
.map_err(|e| Status::internal(e.to_string()))?;

let contract_id_hex = contract_id.to_vec().to_hex();
let message = format!(
"Contract update proposal acceptance for contract_id={} and proposal_id={}",
contract_id_hex, message.proposal_id
);
transaction_service
.submit_transaction(tx_id, transaction, 0.into(), message)
.await
.map_err(|e| Status::internal(e.to_string()))?;

Ok(Response::new(SubmitContractUpdateProposalAcceptanceResponse {
tx_id: tx_id.as_u64(),
}))
}

async fn register_asset(
&self,
request: Request<RegisterAssetRequest>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ use std::net::SocketAddr;
use async_trait::async_trait;
use tari_app_grpc::{
tari_rpc as grpc,
tari_rpc::{CreateFollowOnAssetCheckpointRequest, SubmitContractAcceptanceRequest},
tari_rpc::{
CreateFollowOnAssetCheckpointRequest,
SubmitContractAcceptanceRequest,
SubmitContractUpdateProposalAcceptanceRequest,
},
};
use tari_common_types::types::{FixedHash, PublicKey, Signature};
use tari_comms::types::CommsPublicKey;
Expand Down Expand Up @@ -104,4 +108,30 @@ impl WalletClient for GrpcWalletClient {

Ok(res.into_inner().tx_id)
}

async fn submit_contract_update_proposal_acceptance(
&mut self,
contract_id: &FixedHash,
proposal_id: u64,
validator_node_public_key: &PublicKey,
signature: &Signature,
) -> Result<u64, DigitalAssetError> {
let inner = self.connection().await?;

let request = SubmitContractUpdateProposalAcceptanceRequest {
contract_id: contract_id.as_bytes().to_vec(),
proposal_id,
validator_node_public_key: validator_node_public_key.as_bytes().to_vec(),
signature: Some((*signature).clone().into()),
};

let res = inner
.submit_contract_update_proposal_acceptance(request)
.await
.map_err(|e| {
DigitalAssetError::FatalError(format!("Could not submit contract update proposal acceptance: {}", e))
})?;

Ok(res.into_inner().tx_id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,36 @@ impl<TServiceSpecification: ServiceSpecification + 'static> rpc::validator_node_
}
}

async fn publish_contract_update_proposal_acceptance(
&self,
request: tonic::Request<rpc::PublishContractUpdateProposalAcceptanceRequest>,
) -> Result<Response<rpc::PublishContractUpdateProposalAcceptanceResponse>, tonic::Status> {
let mut wallet_client = self.wallet_client.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();

match wallet_client
.submit_contract_update_proposal_acceptance(
&contract_id,
request.proposal_id,
validator_node_public_key,
&signature,
)
.await
{
Ok(tx_id) => Ok(Response::new(rpc::PublishContractUpdateProposalAcceptanceResponse {
tx_id,
status: "Accepted".to_string(),
})),
Err(_) => Ok(Response::new(rpc::PublishContractUpdateProposalAcceptanceResponse {
status: "Errored".to_string(),
tx_id: 0_u64,
})),
}
}

async fn get_constitution_requests(
&self,
_request: tonic::Request<rpc::GetConstitutionRequestsRequest>,
Expand Down
4 changes: 3 additions & 1 deletion base_layer/core/src/chain_storage/lmdb_db/contract_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ impl<'a> ContractIndex<'a, WriteTransaction<'a>> {
Ok(())
},
// These are collections of output hashes
OutputType::ContractValidatorAcceptance | OutputType::ContractConstitutionProposal => {
OutputType::ContractValidatorAcceptance |
OutputType::ContractConstitutionProposal |
OutputType::ContractConstitutionChangeAcceptance => {
self.assert_definition_exists(contract_id)?;
let mut hashes = self.find::<FixedHashSet>(&key)?.unwrap_or_default();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use super::{
ContractAcceptance,
ContractDefinition,
ContractUpdateProposal,
ContractUpdateProposalAcceptance,
OutputFeaturesVersion,
SideChainFeaturesBuilder,
};
Expand Down Expand Up @@ -317,6 +318,27 @@ impl OutputFeatures {
}
}

pub fn for_contract_update_proposal_acceptance(
contract_id: FixedHash,
proposal_id: u64,
validator_node_public_key: PublicKey,
signature: Signature,
) -> OutputFeatures {
Self {
output_type: OutputType::ContractConstitutionChangeAcceptance,
sidechain_features: Some(
SideChainFeatures::builder(contract_id)
.with_contract_update_proposal_acceptance(ContractUpdateProposalAcceptance {
proposal_id,
validator_node_public_key,
signature,
})
.finish(),
),
..Default::default()
}
}

pub fn for_contract_update_proposal(
contract_id: FixedHash,
update_proposal: ContractUpdateProposal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ impl SideChainFeaturesBuilder {
self
}

pub fn with_contract_update_proposal_acceptance(
mut self,
contract_update_proposal_acceptance: ContractUpdateProposalAcceptance,
) -> Self {
self.features.update_proposal_acceptance = Some(contract_update_proposal_acceptance);
self
}

pub fn with_update_proposal(mut self, update_proposal: ContractUpdateProposal) -> Self {
self.features.update_proposal = Some(update_proposal);
self
Expand Down
28 changes: 28 additions & 0 deletions base_layer/wallet/src/assets/asset_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,34 @@ impl<T: OutputManagerBackend + 'static> AssetManager<T> {
Ok((tx_id, transaction))
}

pub async fn create_contract_update_proposal_acceptance(
&mut self,
contract_id: FixedHash,
proposal_id: u64,
validator_node_public_key: PublicKey,
signature: Signature,
) -> Result<(TxId, Transaction), WalletError> {
let output = self
.output_manager
.create_output_with_features(
0.into(),
OutputFeatures::for_contract_update_proposal_acceptance(
contract_id,
proposal_id,
validator_node_public_key,
signature,
),
)
.await?;

let (tx_id, transaction) = self
.output_manager
.create_send_to_self_with_output(vec![output], ASSET_FPG.into(), None, None)
.await?;

Ok((tx_id, transaction))
}

pub async fn create_update_proposal(
&mut self,
contract_id: FixedHash,
Expand Down
27 changes: 27 additions & 0 deletions base_layer/wallet/src/assets/asset_manager_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,33 @@ impl AssetManagerHandle {
}
}

pub async fn create_contract_update_proposal_acceptance(
&mut self,
contract_id: &FixedHash,
proposal_id: u64,
validator_node_public_key: &PublicKey,
signature: &Signature,
) -> Result<(TxId, Transaction), WalletError> {
match self
.handle
.call(AssetManagerRequest::CreateContractUpdateProposalAcceptance {
contract_id: *contract_id,
proposal_id,
validator_node_public_key: Box::new(validator_node_public_key.clone()),
signature: Box::new(signature.clone()),
})
.await??
{
AssetManagerResponse::CreateContractUpdateProposalAcceptance { transaction, tx_id } => {
Ok((tx_id, *transaction))
},
_ => Err(WalletError::UnexpectedApiResponse {
method: "create_contract_update_proposal_acceptance".to_string(),
api: "AssetManagerService".to_string(),
}),
}
}

pub async fn create_update_proposal(
&mut self,
contract_id: &FixedHash,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,26 @@ impl<T: OutputManagerBackend + 'static> AssetManagerService<T> {
tx_id,
})
},
AssetManagerRequest::CreateContractUpdateProposalAcceptance {
contract_id,
proposal_id,
validator_node_public_key,
signature,
} => {
let (tx_id, transaction) = self
.manager
.create_contract_update_proposal_acceptance(
contract_id,
proposal_id,
*validator_node_public_key,
*signature,
)
.await?;
Ok(AssetManagerResponse::CreateContractUpdateProposalAcceptance {
transaction: Box::new(transaction),
tx_id,
})
},
AssetManagerRequest::CreateContractUpdateProposal {
contract_id,
update_proposal,
Expand Down
7 changes: 7 additions & 0 deletions base_layer/wallet/src/assets/infrastructure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ pub enum AssetManagerRequest {
validator_node_public_key: Box<PublicKey>,
signature: Box<Signature>,
},
CreateContractUpdateProposalAcceptance {
contract_id: FixedHash,
proposal_id: u64,
validator_node_public_key: Box<PublicKey>,
signature: Box<Signature>,
},
CreateContractUpdateProposal {
contract_id: FixedHash,
update_proposal: Box<ContractUpdateProposal>,
Expand All @@ -96,5 +102,6 @@ pub enum AssetManagerResponse {
CreateConstitutionDefinition { transaction: Box<Transaction>, tx_id: TxId },
CreateContractDefinition { transaction: Box<Transaction>, tx_id: TxId },
CreateContractAcceptance { transaction: Box<Transaction>, tx_id: TxId },
CreateContractUpdateProposalAcceptance { transaction: Box<Transaction>, tx_id: TxId },
CreateContractUpdateProposal { transaction: Box<Transaction>, tx_id: TxId },
}
8 changes: 8 additions & 0 deletions dan_layer/core/src/services/wallet_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,12 @@ pub trait WalletClient: Send + Sync {
validator_node_public_key: &PublicKey,
signature: &Signature,
) -> Result<u64, DigitalAssetError>;

async fn submit_contract_update_proposal_acceptance(
&mut self,
contract_id: &FixedHash,
proposal_id: u64,
validator_node_public_key: &PublicKey,
signature: &Signature,
) -> Result<u64, DigitalAssetError>;
}
Loading

0 comments on commit e3b2b9b

Please sign in to comment.