Skip to content

Commit

Permalink
Issue fix for Off-Narrative-Labs#62 and other changes related to revi…
Browse files Browse the repository at this point in the history
…ew comments.

Issue fix for Off-Narrative-Labs#62 and other changes related review comments
  • Loading branch information
NadigerAmit committed Mar 4, 2024
1 parent 4caa90a commit 17469d7
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 35 deletions.
13 changes: 11 additions & 2 deletions tuxedo-core/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,30 @@ impl<B: BlockT<Extrinsic = Transaction<V, C>>, V: Verifier, C: ConstraintChecker
pub fn validate_tuxedo_transaction(
transaction: &Transaction<V, C>,
) -> Result<ValidTransaction, UtxoError<C::Error>> {
debug!(
log::info!(
target: LOG_TARGET,
"validating tuxedo transaction",
);

// Make sure there are no duplicate inputs
// Duplicate peeks are allowed, although they are inefficient and wallets should not create such transactions
{

let input_set: BTreeSet<_> = transaction.inputs.iter().map(|o| o.encode()).collect();
log::info!(
target: LOG_TARGET,
"input_set.len() {} and transaction.inputs.len() {}",input_set.len(),
transaction.inputs.len()
);
ensure!(
input_set.len() == transaction.inputs.len(),
UtxoError::DuplicateInput
);

}



// Build the stripped transaction (with the redeemers stripped) and encode it
// This will be passed to the verifiers
let mut stripped = transaction.clone();
Expand Down Expand Up @@ -402,7 +411,7 @@ impl<B: BlockT<Extrinsic = Transaction<V, C>>, V: Verifier, C: ConstraintChecker
})
};

debug!(target: LOG_TARGET, "Validation result: {:?}", r);
log::info!(target: LOG_TARGET, "Validation result: {:?}", r);

r
}
Expand Down
4 changes: 2 additions & 2 deletions tuxedo-template-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ pub enum OuterConstraintChecker {
/// Checks monetary transactions in a basic fungible cryptocurrency
Money(money::MoneyConstraintChecker<0>),
/// Checks Free Kitty transactions
FreeKittyConstraintChecker(kitties::FreeKittyConstraintChecker),
FreeKitty(kitties::FreeKittyConstraintChecker),
/// Checks Paid Kitty transactions
TradableKittyConstraintChecker(tradable_kitties::TradableKittyConstraintChecker<0>),
TradableKitty(tradable_kitties::TradableKittyConstraintChecker<0>),
/// Checks that an amoeba can split into two new amoebas
AmoebaMitosis(amoeba::AmoebaMitosis),
/// Checks that a single amoeba is simply removed from the state
Expand Down
49 changes: 47 additions & 2 deletions wallet/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,37 @@ use crate::{h256_from_string, keystore::SHAWN_PUB_KEY, output_ref_from_string, D
/// The default number of coins to be minted.
pub const DEFAULT_MINT_VALUE: &str = "100";

/// Default recipient is SHAWN_KEY and output amount is 0
pub const DEFAULT_RECIPIENT: &str = "d2bf4b844dfefd6772a8843e669f943408966a977e3ae2af1dd78e0f55f4df67 0";

/// The default name of the kitty to be created. Show be 4 character long
pub const DEFAULT_KITTY_NAME: &str = " ";

/// The default gender of the kitty to be created.
pub const DEFAULT_KITTY_GENDER: &str = "female";
use crate::keystore;


fn parse_recipient_coins(s: &str) -> Result<(H256, Vec<u128>), &'static str> {
println!("In parse_recipient_coins");
let parts: Vec<&str> = s.split_whitespace().collect();
if parts.len() >= 2 {
let mut recipient = h256_from_string(parts[0]);
let coins = parts[1..].iter().filter_map(|&c| c.parse().ok()).collect();
match recipient {
Ok(r) => {
println!("Recipient: {}", r);
return Ok((r, coins));
},
_ => {},
};
}
println!("Sending the error value ");
Err("Invalid input format")
}




/// The wallet's main CLI struct
#[derive(Debug, Parser)]
Expand Down Expand Up @@ -57,7 +83,7 @@ pub enum Command {
/// get the block hash ad print the block.
getBlock {
/// Input the blockheight to be retrived.
block_height: Option<u32>,
block_height: Option<u32>, // fixme
},

/*
Expand Down Expand Up @@ -182,6 +208,8 @@ pub struct MintCoinArgs {
pub owner: H256,
}

/*
// Old implementation
#[derive(Debug, Args)]
pub struct SpendArgs {
/// An input to be consumed by this transaction. This argument may be specified multiple times.
Expand All @@ -206,6 +234,23 @@ pub struct SpendArgs {
#[arg(long, short, verbatim_doc_comment, action = Append)]
pub output_amount: Vec<u128>,
}
*/

#[derive(Debug, Args,Clone)]
pub struct SpendArgs {
/// An input to be consumed by this transaction. This argument may be specified multiple times.
/// They must all be coins.
#[arg(long, short, verbatim_doc_comment, value_parser = output_ref_from_string)]
pub input: Vec<OutputRef>,

/// Variable number of recipients and their associated coins.
/// For example, "--recipients 0x1234 1 2 --recipients 0x5678 3 4 6"
// #[arg(long, short, verbatim_doc_comment, value_parser = parse_recipient_coins, action = Append)]
// pub recipients: Vec<(H256, Vec<u128>)>,
#[arg(long, short, verbatim_doc_comment, value_parser = parse_recipient_coins, action = Append,
default_value = DEFAULT_RECIPIENT)]
pub recipients: Vec<(H256, Vec<u128>)>,
}

#[derive(Debug, Args)]
pub struct CreateKittyArgs {
Expand All @@ -230,7 +275,7 @@ pub struct ShowOwnedKittyArgs {
// https://docs.rs/clap/latest/clap/_derive/_cookbook/typed_derive/index.html
// shows how to specify a custom parsing function
/// Hex encoded address (sr25519 pubkey) of the owner.
#[arg(long, short, verbatim_doc_comment, value_parser = h256_from_string, default_value = SHAWN_PUB_KEY)]
#[arg(long, short, verbatim_doc_comment)]
pub owner: H256,
}

Expand Down
24 changes: 20 additions & 4 deletions wallet/src/kitty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,15 +652,16 @@ pub async fn update_kitty_name(
*/

let mut transaction = match create_tx_input_based_on_kitty_name(db, args.current_name.clone()) {
Ok((kitty_info, kitty_ref)) => {
Ok((input_kitty_info, input_kitty_ref)) => {
let mut array = [0; 4];
let kitty_name: &[u8; 4] = {
array.copy_from_slice(args.new_name.as_bytes());
&array
};

let inputs: Vec<Input> = vec![kitty_ref];
let mut updated_kitty: KittyData = kitty_info;
let inputs: Vec<Input> = vec![input_kitty_ref.clone(),input_kitty_ref];

let mut updated_kitty: KittyData = input_kitty_info.clone();
updated_kitty.name = *kitty_name;
let output = Output {
payload: updated_kitty.into(),
Expand All @@ -669,11 +670,26 @@ pub async fn update_kitty_name(
}),
};

let mut updated_kitty_fake: KittyData = input_kitty_info;
let mut array = [0; 4];
let kitty_name: &[u8; 4] = {
array.copy_from_slice("lily".as_bytes());
&array
};

updated_kitty_fake.name = *kitty_name;
let output1 = Output {
payload: updated_kitty_fake.into(),
verifier: OuterVerifier::Sr25519Signature(Sr25519Signature {
owner_pubkey: args.owner,
}),
};

// Create the Transaction
let transaction = Transaction {
inputs: inputs,
peeks: Vec::new(),
outputs: vec![output],
outputs: vec![output,output1],
checker: FreeKittyConstraintChecker::UpdateKittyName.into(),
};
transaction
Expand Down
139 changes: 136 additions & 3 deletions wallet/src/money.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Wallet features related to spending money and checking balances.
use crate::{cli::MintCoinArgs, cli::SpendArgs, rpc::fetch_storage, sync};
use crate::{cli::MintCoinArgs, cli::SpendArgs,rpc::fetch_storage, sync};

use anyhow::anyhow;
use jsonrpsee::{core::client::ClientT, http_client::HttpClient, rpc_params};
Expand Down Expand Up @@ -58,15 +58,147 @@ pub async fn mint_coins(client: &HttpClient, args: MintCoinArgs) -> anyhow::Resu

Ok(())
}

use sp_core::H256;
struct recipient_output {
pub recipient:H256,
pub output_amount:Vec<u128>
}
fn extract_recipient_list_from_args(args: SpendArgs,) -> Vec<recipient_output> {
let mut recipient_list:Vec<recipient_output> = Vec::new();
for i in args.recipients {
let rec_pient = recipient_output {
recipient:i.0,
output_amount:i.1,
};
recipient_list.push(rec_pient);
}
recipient_list
}
/// Create and send a transaction that spends coins on the network
pub async fn spend_coins(
db: &Db,
client: &HttpClient,
keystore: &LocalKeystore,
args: SpendArgs,
) -> anyhow::Result<()> {
log::debug!("The args are:: {:?}", args);

log::info!("In the spend_coins_to_multiple_recipient The args are:: {:?}", args);
let mut transaction = Transaction {
inputs: Vec::new(),
peeks: Vec::new(),
outputs: Vec::new(),
checker: OuterConstraintChecker::Money(MoneyConstraintChecker::Spend),
};

let mut recipient_list:Vec<recipient_output> = extract_recipient_list_from_args(args.clone());

let mut total_output_amount = 0;
for recipient in &recipient_list {
for amount in &recipient.output_amount {
let output = Output {
payload: Coin::<0>::new(*amount).into(),
verifier: OuterVerifier::Sr25519Signature(Sr25519Signature {
owner_pubkey: recipient.recipient,
}),
};
total_output_amount += amount;
transaction.outputs.push(output);
}
}

// The total input set will consist of any manually chosen inputs
// plus any automatically chosen to make the input amount high enough
let mut total_input_amount = 0;
let mut all_input_refs = args.input;
for output_ref in &all_input_refs {
let (_owner_pubkey, amount) = sync::get_unspent(db, output_ref)?.ok_or(anyhow!(
"user-specified output ref not found in local database"
))?;
total_input_amount += amount;
}
//TODO filtering on a specific sender

// If the supplied inputs are not valuable enough to cover the output amount
// we select the rest arbitrarily from the local db. (In many cases, this will be all the inputs.)
if total_input_amount < total_output_amount {
match sync::get_arbitrary_unspent_set(db, total_output_amount - total_input_amount)? {
Some(more_inputs) => {
all_input_refs.extend(more_inputs);
}
None => Err(anyhow!(
"Not enough value in database to construct transaction"
))?,
}
}

// Make sure each input decodes and is still present in the node's storage,
// and then push to transaction.
for output_ref in &all_input_refs {
get_coin_from_storage(output_ref, client).await?;
transaction.inputs.push(Input {
output_ref: output_ref.clone(),
redeemer: vec![], // We will sign the total transaction so this should be empty
});
}

// Keep a copy of the stripped encoded transaction for signing purposes
let stripped_encoded_transaction = transaction.clone().encode();
// Iterate back through the inputs, signing, and putting the signatures in place.
for input in &mut transaction.inputs {
// Fetch the output from storage
let utxo = fetch_storage::<OuterVerifier>(&input.output_ref, client).await?;

// Construct the proof that it can be consumed
let redeemer = match utxo.verifier {
OuterVerifier::Sr25519Signature(Sr25519Signature { owner_pubkey }) => {
let public = Public::from_h256(owner_pubkey);
crate::keystore::sign_with(keystore, &public, &stripped_encoded_transaction)?
}
OuterVerifier::UpForGrabs(_) => Vec::new(),
OuterVerifier::ThresholdMultiSignature(_) => todo!(),
};

// insert the proof
input.redeemer = redeemer;
}

// Send the transaction
let genesis_spend_hex = hex::encode(transaction.encode());
let params = rpc_params![genesis_spend_hex];
let genesis_spend_response: Result<String, _> =
client.request("author_submitExtrinsic", params).await;
log::info!(
"Node's response to spend transaction: {:?}",
genesis_spend_response
);

// Print new output refs for user to check later
let tx_hash = <BlakeTwo256 as Hash>::hash_of(&transaction.encode());
for (i, output) in transaction.outputs.iter().enumerate() {
let new_coin_ref = OutputRef {
tx_hash,
index: i as u32,
};
let amount = output.payload.extract::<Coin<0>>()?.0;

print!(
"Created {:?} worth {amount}. ",
hex::encode(new_coin_ref.encode())
);
crate::pretty_print_verifier(&output.verifier);
}

Ok(())
}
/*
/// Create and send a transaction that spends coins on the network
pub async fn spend_coins1(
db: &Db,
client: &HttpClient,
keystore: &LocalKeystore,
args: SpendArgs,
) -> anyhow::Result<()> {
log::info!("The args are:: {:?}", args);
// Construct a template Transaction to push coins into later
let mut transaction = Transaction {
Expand Down Expand Up @@ -174,6 +306,7 @@ pub async fn spend_coins(
Ok(())
}
*/

/// Given an output ref, fetch the details about this coin from the node's
/// storage.
Expand Down
4 changes: 3 additions & 1 deletion wardrobe/kitties/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
//! 3.) The game also allows Kitties to have a cooling off period inbetween breeding before they can be bred again.
//! 4.) A rest operation allows for a Mom Kitty and a Dad Kitty to be cooled off
//!
//! In order to submit a valid transaction you must structure it as follows:
//! In order to submit a valid breed transaction you must structure it as follows:
//! 1.) Input must contain 1 mom and 1 dad
//! 2.) Output must contain Mom, Dad, and newly created Child
//! 3.) A child's DNA is calculated by:
Expand Down Expand Up @@ -644,9 +644,11 @@ pub fn can_kitty_name_be_updated(
.clone()
.extract::<KittyData>()
.map_err(|_| ConstraintCheckerError::BadlyTyped)?;

if utxo_input_kitty.dna != utxo_output_kitty.dna {
return Err(ConstraintCheckerError::DnaMismatchBetweenInputAndOutput);
}

check_kitty_name_update(&utxo_input_kitty, &utxo_output_kitty)?;
}
return Ok(0);
Expand Down
Loading

0 comments on commit 17469d7

Please sign in to comment.