Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet): publish contract amendment #4200

Merged
merged 8 commits into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions applications/tari_console_wallet/src/automation/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ use tari_core::transactions::{
transaction_components::{
CheckpointParameters,
ContractAcceptanceRequirements,
ContractAmendment,
ContractDefinition,
ContractUpdateProposal,
SideChainConsensus,
Expand All @@ -64,6 +65,7 @@ use tari_wallet::{
assets::{
ConstitutionChangeRulesFileFormat,
ConstitutionDefinitionFileFormat,
ContractAmendmentFileFormat,
ContractDefinitionFileFormat,
ContractSpecificationFileFormat,
ContractUpdateProposalFileFormat,
Expand Down Expand Up @@ -798,6 +800,7 @@ async fn handle_contract_definition_command(
ContractSubcommand::PublishDefinition(args) => publish_contract_definition(wallet, args).await,
ContractSubcommand::PublishConstitution(args) => publish_contract_constitution(wallet, args).await,
ContractSubcommand::PublishUpdateProposal(args) => publish_contract_update_proposal(wallet, args).await,
ContractSubcommand::PublishAmendment(args) => publish_contract_amendment(wallet, args).await,
}
}

Expand Down Expand Up @@ -992,6 +995,40 @@ async fn publish_contract_update_proposal(wallet: &WalletSqlite, args: PublishFi
Ok(())
}

async fn publish_contract_amendment(wallet: &WalletSqlite, args: PublishFileArgs) -> Result<(), CommandError> {
let file = File::open(&args.file_path).map_err(|e| CommandError::JsonFile(e.to_string()))?;
let file_reader = BufReader::new(file);

// parse the JSON file
let amendment: ContractAmendmentFileFormat =
serde_json::from_reader(file_reader).map_err(|e| CommandError::JsonFile(e.to_string()))?;
let contract_id_hex = amendment.updated_constitution.contract_id.clone();
let contract_id = FixedHash::from_hex(&contract_id_hex).map_err(|e| CommandError::JsonFile(e.to_string()))?;
let amendment_features = ContractAmendment::try_from(amendment).map_err(CommandError::JsonFile)?;

let mut asset_manager = wallet.asset_manager.clone();
let (tx_id, transaction) = asset_manager
.create_contract_amendment(&contract_id, &amendment_features)
.await?;

let message = format!(
"Contract amendment {} for contract {}",
amendment_features.proposal_id, contract_id_hex
);

let mut transaction_service = wallet.transaction_service.clone();
transaction_service
.submit_transaction(tx_id, transaction, 0.into(), message)
.await?;

println!(
"Contract amendment transaction submitted with tx_id={} for contract with contract_id={}",
tx_id, contract_id_hex
);

Ok(())
}

fn write_utxos_to_csv_file(utxos: Vec<UnblindedOutput>, file_path: PathBuf) -> Result<(), CommandError> {
let factory = PedersenCommitmentFactory::default();
let file = File::create(file_path).map_err(|e| CommandError::CSVFile(e.to_string()))?;
Expand Down
3 changes: 3 additions & 0 deletions applications/tari_console_wallet/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ pub enum ContractSubcommand {

/// Creates and publishes a contract update proposal UTXO from the JSON spec file.
PublishUpdateProposal(PublishFileArgs),

/// Creates and publishes a contract amendment UTXO from the JSON spec file.
PublishAmendment(PublishFileArgs),
}

#[derive(Debug, Args, Clone)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ impl<'a> ContractIndex<'a, WriteTransaction<'a>> {
// These are collections of output hashes
OutputType::ContractValidatorAcceptance |
OutputType::ContractConstitutionProposal |
OutputType::ContractConstitutionChangeAcceptance => {
OutputType::ContractConstitutionChangeAcceptance |
OutputType::ContractAmendment => {
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 @@ -39,6 +39,7 @@ use tari_utilities::ByteArray;

use super::{
ContractAcceptance,
ContractAmendment,
ContractDefinition,
ContractUpdateProposal,
ContractUpdateProposalAcceptance,
Expand Down Expand Up @@ -354,6 +355,18 @@ impl OutputFeatures {
}
}

pub fn for_contract_amendment(contract_id: FixedHash, amendment: ContractAmendment) -> OutputFeatures {
Self {
output_type: OutputType::ContractAmendment,
sidechain_features: Some(
SideChainFeaturesBuilder::new(contract_id)
.with_contract_amendment(amendment)
.finish(),
),
..Default::default()
}
}

pub fn unique_asset_id(&self) -> Option<&[u8]> {
self.unique_id.as_deref()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ use std::{
io::Read,
};

use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use serde_repr::{Deserialize_repr, Serialize_repr};

use crate::consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized};

#[derive(Debug, Clone, Copy, Hash, Deserialize_repr, Serialize_repr, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, Hash, Deserialize_repr, Serialize_repr, PartialEq, Eq, FromPrimitive)]
#[repr(u8)]
pub enum OutputType {
/// An standard non-coinbase output.
Expand All @@ -52,16 +54,18 @@ pub enum OutputType {
ContractConstitutionProposal = 6,
/// Output that indicates acceptance of an existing contract constitution amendment proposal.
ContractConstitutionChangeAcceptance = 7,
/// Output that defines and amendment of a contract constitution amendment
ContractAmendment = 8,

// TODO: Remove these deprecated flags
NonFungible = 8,
AssetRegistration = 9,
MintNonFungible = 10,
BurnNonFungible = 11,
SidechainInitialCheckpoint = 12,
SidechainCheckpoint = 13,
CommitteeInitialDefinition = 14,
CommitteeDefinition = 15,
NonFungible = 9,
AssetRegistration = 10,
MintNonFungible = 11,
BurnNonFungible = 12,
SidechainInitialCheckpoint = 13,
SidechainCheckpoint = 14,
CommitteeInitialDefinition = 15,
CommitteeDefinition = 16,
}

impl OutputType {
Expand All @@ -72,16 +76,8 @@ impl OutputType {

/// Returns the OutputType that corresponds to this OutputType. If the byte does not correspond to any OutputType,
/// None is returned.
pub fn from_byte(bit: u8) -> Option<Self> {
if !Self::is_valid_byte(bit) {
return None;
}
// SAFETY: bit has been checked for validity before transmute is called
Some(unsafe { std::mem::transmute(bit) })
}

fn is_valid_byte(bit: u8) -> bool {
bit <= 15
pub fn from_byte(value: u8) -> Option<Self> {
FromPrimitive::from_u8(value)
}
}

Expand Down Expand Up @@ -133,7 +129,8 @@ mod tests {
fn it_converts_from_byte_to_output_type() {
assert_eq!(OutputType::from_byte(0), Some(OutputType::Standard));
assert_eq!(OutputType::from_byte(1), Some(OutputType::Coinbase));
assert_eq!(OutputType::from_byte(15), Some(OutputType::CommitteeDefinition));
assert_eq!(OutputType::from_byte(16), None);
assert_eq!(OutputType::from_byte(15), Some(OutputType::CommitteeInitialDefinition));
assert_eq!(OutputType::from_byte(16), Some(OutputType::CommitteeDefinition));
assert_eq!(OutputType::from_byte(17), None);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ impl SideChainFeaturesBuilder {
self
}

pub fn with_contract_amendment(mut self, contract_amendment: ContractAmendment) -> Self {
self.features.amendment = Some(contract_amendment);
self
}

pub fn finish(self) -> SideChainFeatures {
self.features
}
Expand Down
19 changes: 19 additions & 0 deletions base_layer/wallet/src/assets/asset_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use tari_common_types::{
types::{Commitment, FixedHash, PublicKey, Signature, ASSET_CHECKPOINT_ID},
};
use tari_core::transactions::transaction_components::{
ContractAmendment,
ContractDefinition,
ContractUpdateProposal,
OutputFeatures,
Expand Down Expand Up @@ -359,6 +360,24 @@ impl<T: OutputManagerBackend + 'static> AssetManager<T> {

Ok((tx_id, transaction))
}

pub async fn create_contract_amendment(
&mut self,
contract_id: FixedHash,
amendment: ContractAmendment,
) -> Result<(TxId, Transaction), WalletError> {
let output = self
.output_manager
.create_output_with_features(0.into(), OutputFeatures::for_contract_amendment(contract_id, amendment))
.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))
}
}

fn convert_to_asset(unblinded_output: DbUnblindedOutput) -> Result<Asset, WalletError> {
Expand Down
22 changes: 22 additions & 0 deletions base_layer/wallet/src/assets/asset_manager_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use tari_common_types::{
types::{Commitment, FixedHash, PublicKey, Signature},
};
use tari_core::transactions::transaction_components::{
ContractAmendment,
ContractDefinition,
ContractUpdateProposal,
OutputFeatures,
Expand Down Expand Up @@ -284,4 +285,25 @@ impl AssetManagerHandle {
}),
}
}

pub async fn create_contract_amendment(
&mut self,
contract_id: &FixedHash,
amendment: &ContractAmendment,
) -> Result<(TxId, Transaction), WalletError> {
match self
.handle
.call(AssetManagerRequest::CreateContractAmendment {
contract_id: *contract_id,
contract_amendment: Box::new(amendment.clone()),
})
.await??
{
AssetManagerResponse::CreateContractAmendment { transaction, tx_id } => Ok((tx_id, *transaction)),
_ => Err(WalletError::UnexpectedApiResponse {
method: "create_contract_amendment".to_string(),
api: "AssetManagerService".to_string(),
}),
}
}
}
78 changes: 78 additions & 0 deletions base_layer/wallet/src/assets/contract_amendment_file_format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// 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::{TryFrom, TryInto};

use serde::{Deserialize, Serialize};
use tari_common_types::types::{PrivateKey, PublicKey, Signature};
use tari_core::transactions::transaction_components::{CommitteeSignatures, ContractAmendment};
use tari_utilities::hex::Hex;

use super::ConstitutionDefinitionFileFormat;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ContractAmendmentFileFormat {
pub proposal_id: u64,
pub validator_committee: Vec<PublicKey>,
pub validator_signatures: Vec<SignatureFileFormat>,
pub updated_constitution: ConstitutionDefinitionFileFormat,
pub activation_window: u64,
}

impl TryFrom<ContractAmendmentFileFormat> for ContractAmendment {
type Error = String;

fn try_from(value: ContractAmendmentFileFormat) -> Result<Self, Self::Error> {
let validator_signature_vec: Vec<Signature> = value
.validator_signatures
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()?;
let validator_signatures =
CommitteeSignatures::try_from(validator_signature_vec).map_err(|e| format!("{}", e))?;

Ok(Self {
proposal_id: value.proposal_id,
validator_committee: value.validator_committee.try_into().map_err(|e| format!("{}", e))?,
validator_signatures,
updated_constitution: value.updated_constitution.try_into()?,
activation_window: value.activation_window,
})
}
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SignatureFileFormat {
pub public_nonce: String,
pub signature: String,
}

impl TryFrom<SignatureFileFormat> for Signature {
type Error = String;

fn try_from(value: SignatureFileFormat) -> Result<Self, Self::Error> {
let public_key = PublicKey::from_hex(&value.public_nonce).map_err(|e| format!("{}", e))?;
let signature = PrivateKey::from_hex(&value.signature).map_err(|e| format!("{}", e))?;

Ok(Signature::new(public_key, signature))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@ impl<T: OutputManagerBackend + 'static> AssetManagerService<T> {
tx_id,
})
},
AssetManagerRequest::CreateContractAmendment {
contract_id,
contract_amendment,
} => {
let (tx_id, transaction) = self
.manager
.create_contract_amendment(contract_id, *contract_amendment)
.await?;
Ok(AssetManagerResponse::CreateContractAmendment {
transaction: Box::new(transaction),
tx_id,
})
},
}
}
}
6 changes: 6 additions & 0 deletions base_layer/wallet/src/assets/infrastructure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use tari_common_types::{
types::{Commitment, FixedHash, PublicKey, Signature},
};
use tari_core::transactions::transaction_components::{
ContractAmendment,
ContractDefinition,
ContractUpdateProposal,
OutputFeatures,
Expand Down Expand Up @@ -90,6 +91,10 @@ pub enum AssetManagerRequest {
contract_id: FixedHash,
update_proposal: Box<ContractUpdateProposal>,
},
CreateContractAmendment {
contract_id: FixedHash,
contract_amendment: Box<ContractAmendment>,
},
}

pub enum AssetManagerResponse {
Expand All @@ -104,4 +109,5 @@ pub enum AssetManagerResponse {
CreateContractAcceptance { transaction: Box<Transaction>, tx_id: TxId },
CreateContractUpdateProposalAcceptance { transaction: Box<Transaction>, tx_id: TxId },
CreateContractUpdateProposal { transaction: Box<Transaction>, tx_id: TxId },
CreateContractAmendment { transaction: Box<Transaction>, tx_id: TxId },
}
2 changes: 2 additions & 0 deletions base_layer/wallet/src/assets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ pub use asset_manager_handle::AssetManagerHandle;
pub(crate) mod infrastructure;

mod constitution_definition_file_format;
mod contract_amendment_file_format;
mod contract_definition_file_format;
mod contract_update_proposal_file_format;

pub use constitution_definition_file_format::{ConstitutionChangeRulesFileFormat, ConstitutionDefinitionFileFormat};
pub use contract_amendment_file_format::ContractAmendmentFileFormat;
pub use contract_definition_file_format::{ContractDefinitionFileFormat, ContractSpecificationFileFormat};
pub use contract_update_proposal_file_format::ContractUpdateProposalFileFormat;
Loading