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): new command to publish a contract definition transaction #4133

Merged
merged 38 commits into from
May 26, 2022
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
563f47f
create initial version of the command
mrnaveira May 11, 2022
63facab
parse contract definition json file
mrnaveira May 11, 2022
2791478
complete contract definition structure
mrnaveira May 11, 2022
5c50f9d
move contract features to base layer wallet
mrnaveira May 11, 2022
f38c147
boilerplate for contract definition transaction
mrnaveira May 12, 2022
e2badc2
Merge branch 'development' of https://github.com/mrnaveira/tari into ud1
mrnaveira May 13, 2022
387e613
create integration test
mrnaveira May 13, 2022
46ec143
test that block is mined
mrnaveira May 16, 2022
9421515
flag a broken wallet-cli test
mrnaveira May 16, 2022
43c047e
create simple feature for contract definition
mrnaveira May 16, 2022
4c84fa2
Merge branch 'development' of https://github.com/mrnaveira/tari into ud1
mrnaveira May 23, 2022
e856921
use the new flag for contract definition
mrnaveira May 23, 2022
254bcf3
create nested contract specification
mrnaveira May 23, 2022
f725362
make contract_issuer field into a public key
mrnaveira May 23, 2022
56b0327
make contract_id field as a hash
mrnaveira May 23, 2022
b107fc8
add public function struct
mrnaveira May 23, 2022
489dcd0
add function ref type
mrnaveira May 23, 2022
4d85a3a
assert transaction status in integration test
mrnaveira May 24, 2022
d2657cf
show proper feedback in wallet cli
mrnaveira May 24, 2022
cfd7097
improve naming and comments in the new features
mrnaveira May 24, 2022
28168e4
simplify contract definition features mapping
mrnaveira May 24, 2022
37e583e
remove old TODO comment
mrnaveira May 24, 2022
7d7ce58
allow many lines in wallet handle request
mrnaveira May 24, 2022
1bbda7c
fix integration test lint warnings
mrnaveira May 24, 2022
0170b4b
add cucumber flag for dan_layer tests
mrnaveira May 24, 2022
8246eb2
auto-calculate contract_id as a hash
mrnaveira May 24, 2022
9f05111
Update base_layer/core/src/transactions/transaction_components/contra…
mrnaveira May 25, 2022
f37684d
make contract_name an array
mrnaveira May 25, 2022
79ba18f
Merge branch 'development' into ud1
aviator-app[bot] May 25, 2022
57a9fe1
make all string fields as fixed strings
mrnaveira May 25, 2022
0be5c20
Merge branch 'ud1' of https://github.com/mrnaveira/tari into ud1
mrnaveira May 25, 2022
05e98c4
make contract_issuer a public key type
mrnaveira May 25, 2022
02fc05d
Merge branch 'development' into ud1
mrnaveira May 25, 2022
00ba098
solve conflicts with development branch
mrnaveira May 25, 2022
2d747b6
Merge branch 'development' of https://github.com/mrnaveira/tari into ud1
mrnaveira May 26, 2022
a04c499
Merge branch 'development' of https://github.com/mrnaveira/tari into ud1
mrnaveira May 26, 2022
795653b
remove unnecesary println
mrnaveira May 26, 2022
5157f02
Merge branch 'development' into ud1
aviator-app[bot] May 26, 2022
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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion applications/tari_app_grpc/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ message OutputFeatures {

message SideChainFeatures {
bytes contract_id = 1;
// ContractDefinition definition = 2;
ContractDefinition definition = 2;
ContractConstitution constitution = 3;
}

Expand Down Expand Up @@ -306,6 +306,28 @@ message CommitteeDefinitionFeatures {
uint64 effective_sidechain_height = 2;
}

message ContractDefinition {
bytes contract_id = 1;
bytes contract_name = 2;
bytes contract_issuer = 3;
ContractSpecification contract_spec = 4;
}

message ContractSpecification {
bytes runtime = 1;
repeated PublicFunction public_functions = 2;
}

message PublicFunction {
bytes name = 1;
FunctionRef function = 2;
}

message FunctionRef {
bytes template_id = 1;
uint32 function_id = 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
Expand Down
132 changes: 131 additions & 1 deletion applications/tari_app_grpc/src/conversions/sidechain_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@

use std::convert::{TryFrom, TryInto};

use tari_common_types::types::PublicKey;
use tari_common_types::types::{FixedHash, PublicKey};
use tari_core::transactions::transaction_components::{
vec_into_fixed_string,
CheckpointParameters,
CommitteeMembers,
ConstitutionChangeFlags,
ConstitutionChangeRules,
ContractAcceptanceRequirements,
ContractConstitution,
ContractDefinition,
ContractSpecification,
FunctionRef,
PublicFunction,
RequirementsForConstitutionChange,
SideChainConsensus,
SideChainFeatures,
Expand All @@ -42,6 +47,7 @@ impl From<SideChainFeatures> for grpc::SideChainFeatures {
fn from(value: SideChainFeatures) -> Self {
Self {
contract_id: value.contract_id.to_vec(),
definition: value.definition.map(Into::into),
constitution: value.constitution.map(Into::into),
}
}
Expand All @@ -51,15 +57,139 @@ impl TryFrom<grpc::SideChainFeatures> for SideChainFeatures {
type Error = String;

fn try_from(features: grpc::SideChainFeatures) -> Result<Self, Self::Error> {
let definition = features.definition.map(ContractDefinition::try_from).transpose()?;
let constitution = features.constitution.map(ContractConstitution::try_from).transpose()?;

Ok(Self {
contract_id: features.contract_id.try_into().map_err(|_| "Invalid contract_id")?,
definition,
constitution,
})
}
}

//---------------------------------- ContractDefinition --------------------------------------------//

impl TryFrom<grpc::ContractDefinition> for ContractDefinition {
type Error = String;

fn try_from(value: grpc::ContractDefinition) -> Result<Self, Self::Error> {
let contract_id = FixedHash::try_from(value.contract_id).map_err(|err| format!("{:?}", err))?;

let contract_name = vec_into_fixed_string(value.contract_name);

let contract_issuer =
PublicKey::from_bytes(value.contract_issuer.as_bytes()).map_err(|err| format!("{:?}", err))?;

let contract_spec = value
.contract_spec
.map(ContractSpecification::try_from)
.ok_or_else(|| "contract_spec is missing".to_string())?
.map_err(|err| err)?;

Ok(Self {
contract_id,
contract_name,
contract_issuer,
contract_spec,
})
}
}

impl From<ContractDefinition> for grpc::ContractDefinition {
fn from(value: ContractDefinition) -> Self {
let contract_id = value.contract_id.as_bytes().to_vec();
let contract_name = value.contract_name.as_bytes().to_vec();
let contract_issuer = value.contract_issuer.as_bytes().to_vec();

Self {
contract_id,
contract_name,
contract_issuer,
contract_spec: Some(value.contract_spec.into()),
}
}
}

impl TryFrom<grpc::ContractSpecification> for ContractSpecification {
type Error = String;

fn try_from(value: grpc::ContractSpecification) -> Result<Self, Self::Error> {
let runtime = vec_into_fixed_string(value.runtime);
let public_functions = value
.public_functions
.into_iter()
.map(PublicFunction::try_from)
.collect::<Result<_, _>>()?;

Ok(Self {
runtime,
public_functions,
})
}
}

impl From<ContractSpecification> for grpc::ContractSpecification {
fn from(value: ContractSpecification) -> Self {
let public_functions = value.public_functions.into_iter().map(|f| f.into()).collect();
Self {
runtime: value.runtime.as_bytes().to_vec(),
public_functions,
}
}
}

impl TryFrom<grpc::PublicFunction> for PublicFunction {
type Error = String;

fn try_from(value: grpc::PublicFunction) -> Result<Self, Self::Error> {
let function = value
.function
.map(FunctionRef::try_from)
.ok_or_else(|| "function is missing".to_string())?
.map_err(|err| err)?;

Ok(Self {
name: vec_into_fixed_string(value.name),
function,
})
}
}

impl From<PublicFunction> for grpc::PublicFunction {
fn from(value: PublicFunction) -> Self {
Self {
name: value.name.as_bytes().to_vec(),
function: Some(value.function.into()),
}
}
}

impl TryFrom<grpc::FunctionRef> for FunctionRef {
type Error = String;

fn try_from(value: grpc::FunctionRef) -> Result<Self, Self::Error> {
let template_id = FixedHash::try_from(value.template_id).map_err(|err| format!("{:?}", err))?;
let function_id = u16::try_from(value.function_id).map_err(|_| "Invalid function_id: overflowed u16")?;

Ok(Self {
template_id,
function_id,
})
}
}

impl From<FunctionRef> for grpc::FunctionRef {
fn from(value: FunctionRef) -> Self {
let template_id = value.template_id.as_bytes().to_vec();

Self {
template_id,
function_id: value.function_id.into(),
}
}
}

//---------------------------------- ContractConstitution --------------------------------------------//
impl From<ContractConstitution> for grpc::ContractConstitution {
fn from(value: ContractConstitution) -> Self {
Expand Down
2 changes: 2 additions & 0 deletions applications/tari_console_wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ qrcode = { version = "0.12" }
regex = "1.5.4"
rpassword = "5.0"
rustyline = "9.0"
serde = "1.0.136"
serde_json = "1.0.79"
strum = "0.22"
strum_macros = "0.22"
thiserror = "1.0.26"
Expand Down
22 changes: 22 additions & 0 deletions applications/tari_console_wallet/src/automation/command_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl Display for ParsedCommand {
CreateInitialCheckpoint => "create-initial-checkpoint",
CreateCommitteeDefinition => "create-committee-definition",
RevalidateWalletDb => "revalidate-wallet-db",
PublishContractDefinition => "publish-contract-definition",
};

let args = self
Expand All @@ -94,6 +95,7 @@ pub enum ParsedArgument {
Address(Multiaddr),
Negotiated(bool),
Hash(Vec<u8>),
JSONFileName(String),
}

impl Display for ParsedArgument {
Expand All @@ -112,6 +114,7 @@ impl Display for ParsedArgument {
Address(v) => write!(f, "{}", v),
Negotiated(v) => write!(f, "{}", v),
Hash(v) => write!(f, "{}", v.to_hex()),
JSONFileName(v) => write!(f, "{}", v),
}
}
}
Expand Down Expand Up @@ -148,6 +151,7 @@ pub fn parse_command(command: &str) -> Result<ParsedCommand, ParseError> {
CreateInitialCheckpoint => parser_builder(args).pub_key().text().build()?,
CreateCommitteeDefinition => parser_builder(args).pub_key().pub_key_array().build()?,
RevalidateWalletDb => Vec::new(),
PublishContractDefinition => parse_publish_contract_definition(args)?,
};

Ok(ParsedCommand { command, args })
Expand Down Expand Up @@ -483,6 +487,24 @@ fn parse_coin_split(mut args: SplitWhitespace) -> Result<Vec<ParsedArgument>, Pa
Ok(parsed_args)
}

fn parse_publish_contract_definition(mut args: SplitWhitespace) -> Result<Vec<ParsedArgument>, ParseError> {
let mut parsed_args = Vec::new();

let usage = "Usage:\n publish-contract-definition\n publish-contract-definition --json-file <file name>";

let arg = args.next().ok_or_else(|| ParseError::Empty("json-file".to_string()))?;
if arg != "--json-file" {
return Err(ParseError::Empty(format!("'--json-file' qualifier\n {}", usage)));
}

let file_name = args
.next()
.ok_or_else(|| ParseError::Empty(format!("file name\n {}", usage)))?;
parsed_args.push(ParsedArgument::JSONFileName(file_name.to_string()));

Ok(parsed_args)
}

#[cfg(test)]
mod test {
use std::str::FromStr;
Expand Down
40 changes: 37 additions & 3 deletions applications/tari_console_wallet/src/automation/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

use std::{
fs::File,
io::{LineWriter, Write},
io::{BufReader, LineWriter, Write},
time::{Duration, Instant},
};

Expand All @@ -45,12 +45,12 @@ use tari_comms::{
use tari_comms_dht::{envelope::NodeDestination, DhtDiscoveryRequester};
use tari_core::transactions::{
tari_amount::{uT, MicroTari, Tari},
transaction_components::{TransactionOutput, UnblindedOutput},
transaction_components::{ContractDefinition, TransactionOutput, UnblindedOutput},
};
use tari_crypto::{keys::PublicKey as PublicKeyTrait, ristretto::pedersen::PedersenCommitmentFactory};
use tari_utilities::{hex::Hex, ByteArray, Hashable};
use tari_wallet::{
assets::KEY_MANAGER_ASSET_BRANCH,
assets::{ContractDefinitionFileFormat, KEY_MANAGER_ASSET_BRANCH},
error::WalletError,
key_manager_service::KeyManagerInterface,
output_manager_service::handle::OutputManagerHandle,
Expand Down Expand Up @@ -97,6 +97,7 @@ pub enum WalletCommand {
CreateInitialCheckpoint,
CreateCommitteeDefinition,
RevalidateWalletDb,
PublishContractDefinition,
}

#[derive(Debug)]
Expand Down Expand Up @@ -908,6 +909,39 @@ pub async fn command_runner(
.await
.map_err(CommandError::TransactionServiceError)?;
},
PublishContractDefinition => {
// open the JSON file with the contract definition values
let file_path = match parsed.args.get(0) {
Some(ParsedArgument::JSONFileName(ref file_path)) => Ok(file_path),
_ => Err(CommandError::Argument),
}?;
let file = File::open(file_path).map_err(|e| CommandError::JSONFile(e.to_string()))?;
let file_reader = BufReader::new(file);

// parse the JSON file
let contract_definition: ContractDefinitionFileFormat =
serde_json::from_reader(file_reader).map_err(|e| CommandError::JSONFile(e.to_string()))?;
let contract_definition_features = ContractDefinition::from(contract_definition);
let contract_id_hex = contract_definition_features.contract_id.to_vec().to_hex();

// create the contract definition transaction
let mut asset_manager = wallet.asset_manager.clone();
let (tx_id, transaction) = asset_manager
.create_contract_definition(&contract_definition_features)
.await?;

// publish the contract definition transaction
let message = format!("Contract definition for contract with id={}", contract_id_hex);
transaction_service
.submit_transaction(tx_id, transaction, 0.into(), message)
.await?;

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

Expand Down
2 changes: 2 additions & 0 deletions applications/tari_console_wallet/src/automation/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ pub enum CommandError {
HexError(#[from] HexError),
#[error("Error `{0}`")]
ShaError(String),
#[error("JSON file error `{0}`")]
JSONFile(String),
}

impl From<CommandError> for ExitError {
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/blocks/genesis_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ fn get_dibbler_genesis_block_raw() -> Block {
asset: None,
mint_non_fungible: None,
sidechain_checkpoint: None,
committee_definition: None
committee_definition: None,
},
Commitment::from_hex("e2b9ee8fdf05f9fa8fd7598d4568b539eef694e58cdae84c779140271a96d733 ").unwrap(),
BulletRangeProof::from_hex("0c02c6d9bdbd1c21b29ee0f83bed597ed07f71a60f99ddcbc02550059c4c08020438f8fc25c69160dc6af81f84c037fc79b9f4a7baa93ab4d6ef1640356d9b575efe1f3f8b40f6c64d1a964aab215491f79738ccac1712d756607626442dda37ac30010dc663985153786cc53c865c01bfec186a803c1edb1a34efa3088ec221016a63cdbd2b58fa4c258bfbf0cff6b793ab62f5db0fb781f046effb5f4e7c0a956c2e042e3f0c16a72f20a624625fa6dc0b742e49e0158a50c8abde54834e04bb35baef0c258da30b738256549e3a2612ff89b4f6bfe82d16aa10b38daabe0df6b922717cb4b1604ab97a2a5efa4d325beb56c5419cff185d61e1a0fc9e374098bf4a10404d788141e2c77de222d68c14b421b62f300898c25487f491aff26be85e54c011e90cc96aff6b31993ce74233674fb150de929fbc259bcc7808a84432cf28bf83c2a0fbf2b47a6244fbafa02ca4f5c9d46c5380fe8eaed734f1d56e769e59800137900cb5905191bbb463cbcb4ea0a2073d716f18878ed4455d19426a2c1133bf703510bf0f1b3b70e9e5ee6fbb70a8e710fd0a4b8f37eacfdeef3df66e461f16ffdb270a7181505b1358f56578840bbfa284444c35160794f0294300ecb3fde701a3f5ed9234e4c196b93fd70633069eeb184ab53685b5324c963a7428094f0c7d4306b5da6ef5fb68d085c32adabe004bebcbf335ee8fc92e5e034edcb035872d08f139e9445539241ff9b9fbebbc0e7b248cbd97fa7c6f3d7823085893c9ced1685d69d2a7cf111f81e086927565c301d4e33639def1139bd5245a0ae9085d5ba10cdc1f89fc7a7fa95cc3aa11784ec40ebf57475ffb4f2b2042018e3dbe905ebd5d0ebe533f36f43f709110372c94258a59e53c9b319adca30c8e9f4f92d5937f994ff36a5bb38a15682187dc8734162f45e169a97a36fb5a05").unwrap(),
Expand Down
Loading