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

Jcli make transaction #3230

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
546d2bb
Base input and skeleton
Apr 20, 2021
3c58130
Implement simplified transaction steps:
Apr 20, 2021
c803c38
Extract simplified transaction method too
Apr 20, 2021
e208250
Minor naming refactor
Apr 20, 2021
f13b660
Added retrieve account information
Apr 21, 2021
deb6aa9
Added send fragment
Apr 22, 2021
ab4633f
Get keys ready
Apr 22, 2021
33d7268
Rename command to simplified
Apr 23, 2021
37724ae
Cleanup imports
Apr 23, 2021
964b167
Rename and clean
Apr 23, 2021
67b304b
Missed faucet cleaning
Apr 26, 2021
97fcbfc
Missed simplified cleaning
Apr 26, 2021
caab642
Refactored Staging add_account into a struct method
Apr 26, 2021
0773aba
Reorder arguments to avoid structopt order issues when parsing mandat…
Apr 26, 2021
58c30c9
Removed output
Apr 27, 2021
03f1ef9
Request fees through rest API
Apr 30, 2021
20b1df9
Added make transaction integration test
May 27, 2021
1b6f558
Add receiver account as optional parameter
May 28, 2021
d23c218
Allow clippy many arguments for make_transaction methods
May 28, 2021
d245aca
Refactor: receiver-account -> receiver
May 31, 2021
30bb77c
Add proper fees handling
May 31, 2021
ffb5b77
Fit fees and values closer in test
May 31, 2021
3905529
Trim input endline
May 31, 2021
4e455b7
Implement post fragment from make-transaction
Jun 1, 2021
bf15dbd
Fix prints issues
Jun 7, 2021
b0b4f1a
Fix printl typo
Jun 7, 2021
7aa8e45
Fix wrong printing when asking input
Jun 7, 2021
0eb6caf
Use interactive secret key read
Jun 7, 2021
3af372e
Refactor not save to file when transaction is directly posted
Jun 9, 2021
227e1a4
Update cargo lock with missing deps
Jun 10, 2021
4d82704
Fix clippy
Jul 5, 2021
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
11 changes: 11 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions jcli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ gtmpl = "0.6.0"
ed25519-bip32 = "0.3"
thiserror = "1.0"
bytes = "1.0"
rpassword = "5.0"

[dependencies.clap]
version = "2.33"
Expand Down
18 changes: 13 additions & 5 deletions jcli/src/jcli_lib/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,26 @@ pub struct Input {

impl Input {
pub fn open(&self) -> Result<impl BufRead, Error> {
io::open_file_read(&self.input_file).map_err(|source| Error::InputInvalid {
source,
path: self.input_file.clone().unwrap_or_default(),
})
open_block_file(&self.input_file)
}

pub fn load_block(&self) -> Result<Block, Error> {
let reader = self.open()?;
Block::deserialize(reader).map_err(Error::BlockFileCorrupted)
load_block(reader)
}
}

pub fn open_block_file(input_file: &Option<PathBuf>) -> Result<impl BufRead, Error> {
io::open_file_read(input_file).map_err(|source| Error::InputInvalid {
source,
path: input_file.clone().unwrap_or_default(),
})
}

pub fn load_block(block_reader: impl BufRead) -> Result<Block, Error> {
Block::deserialize(block_reader).map_err(Error::BlockFileCorrupted)
}

#[derive(StructOpt)]
pub struct Common {
#[structopt(flatten)]
Expand Down
1 change: 1 addition & 0 deletions jcli/src/jcli_lib/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ fn bytes_to_priv_key<K: AsymmetricKey>(bytes: &[u8]) -> Result<String, Error> {

#[derive(Debug)]
struct Seed([u8; 32]);

impl std::str::FromStr for Seed {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Expand Down
4 changes: 2 additions & 2 deletions jcli/src/jcli_lib/rest/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ use std::path::PathBuf;
use structopt::StructOpt;
use thiserror::Error;

#[derive(StructOpt)]
#[derive(StructOpt, Clone)]
pub struct RestArgs {
/// node API address. Must always have `http://` or `https://` prefix.
/// E.g. `-h http://127.0.0.1`, `--host https://node.com:8443/cardano/api`
#[structopt(short, long, env = "JORMUNGANDR_RESTAPI_URL")]
host: Url,
pub host: Url,
danielSanchezQ marked this conversation as resolved.
Show resolved Hide resolved
/// print additional debug information to stderr.
/// The output format is intentionally undocumented and unstable
#[structopt(long)]
Expand Down
6 changes: 4 additions & 2 deletions jcli/src/jcli_lib/rest/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
mod config;
mod v0;
pub mod v0;

use crate::jcli_lib::utils::{io::ReadYamlError, output_format};
use config::RestArgs;
pub use config::RestArgs;
use hex::FromHexError;
use structopt::StructOpt;
use thiserror::Error;
Expand All @@ -29,6 +29,8 @@ pub enum Error {
InputHexMalformed(#[from] FromHexError),
#[error("error when trying to perform an HTTP request")]
RequestError(#[from] config::Error),
#[error("error loading data from response")]
SerdeError(#[from] serde_json::Error),
}

impl From<ReadYamlError> for Error {
Expand Down
20 changes: 14 additions & 6 deletions jcli/src/jcli_lib/rest/v0/account/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::jcli_lib::rest::{Error, RestArgs};
use crate::jcli_lib::utils::{AccountId, OutputFormat};
use jormungandr_lib::interfaces::AccountState;
use structopt::StructOpt;

#[derive(StructOpt)]
Expand All @@ -24,13 +25,20 @@ impl Account {
output_format,
account_id,
} = self;
let state = args
.client()?
.get(&["v0", "account", &account_id.to_url_arg()])
.execute()?
.json()?;
let formatted = output_format.format_json(state)?;
let state = request_account_information(args, account_id)?;
let formatted = output_format.format_json(serde_json::to_value(state)?)?;
println!("{}", formatted);
Ok(())
}
}

pub fn request_account_information(
args: RestArgs,
account_id: AccountId,
) -> Result<AccountState, Error> {
args.client()?
.get(&["v0", "account", &account_id.to_url_arg()])
.execute()?
.json()
.map_err(Into::into)
}
15 changes: 10 additions & 5 deletions jcli/src/jcli_lib/rest/v0/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::jcli_lib::{
rest::{Error, RestArgs},
utils::{io, OutputFormat},
};
use chain_core::property::Deserialize;
use chain_core::property::{Deserialize, Serialize};
use chain_impl_mockchain::fragment::Fragment;
use std::path::PathBuf;
use structopt::StructOpt;
Expand Down Expand Up @@ -57,14 +57,19 @@ fn get_logs(args: RestArgs, output_format: OutputFormat) -> Result<(), Error> {
fn post_message(args: RestArgs, file: Option<PathBuf>) -> Result<(), Error> {
let msg_hex = io::read_line(&file)?;
let msg_bin = hex::decode(&msg_hex)?;
let _fragment =
let fragment =
Fragment::deserialize(msg_bin.as_slice()).map_err(Error::InputFragmentMalformed)?;
let fragment_id = post_fragment(args, fragment)?;
println!("{}", fragment_id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be replaced with a more informative message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept it as it was. I understand that this may be used in order to pipe the output to something else. But I'm down to change it.

Ok(())
}

pub fn post_fragment(args: RestArgs, fragment: Fragment) -> Result<String, Error> {
let fragment_id = args
.client()?
.post(&["v0", "message"])
.body(msg_bin)
.body(fragment.serialize_as_vec()?)
.execute()?
.text()?;
println!("{}", fragment_id);
Ok(())
Ok(fragment_id)
}
6 changes: 3 additions & 3 deletions jcli/src/jcli_lib/rest/v0/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
mod account;
pub mod account;
mod block;
mod diagnostic;
mod leaders;
mod message;
pub mod message;
mod network;
mod node;
mod rewards;
mod settings;
pub mod settings;
mod shutdown;
mod stake;
mod stake_pool;
Expand Down
11 changes: 9 additions & 2 deletions jcli/src/jcli_lib/rest/v0/settings/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::jcli_lib::rest::{Error, RestArgs};
use crate::jcli_lib::utils::OutputFormat;
use jormungandr_lib::interfaces::SettingsDto;

use structopt::StructOpt;

#[derive(StructOpt)]
Expand All @@ -20,9 +22,14 @@ impl Settings {
args,
output_format,
} = self;
let response = args.client()?.get(&["v0", "settings"]).execute()?.json()?;
let formatted = output_format.format_json(response)?;
let settings = request_settings(args)?;
let formatted = output_format.format_json(serde_json::to_value(&settings)?)?;
println!("{}", formatted);
Ok(())
}
}

pub fn request_settings(args: RestArgs) -> Result<SettingsDto, Error> {
serde_json::from_str(&(args.client()?.get(&["v0", "settings"]).execute()?.text()?))
.map_err(Error::SerdeError)
}
19 changes: 1 addition & 18 deletions jcli/src/jcli_lib/transaction/add_account.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::jcli_lib::transaction::{common, Error};
use chain_addr::{Address, Kind};
use chain_impl_mockchain::transaction::UnspecifiedAccountIdentifier;
use jormungandr_lib::interfaces;
use structopt::StructOpt;

Expand All @@ -22,22 +20,7 @@ pub struct AddAccount {
impl AddAccount {
pub fn exec(self) -> Result<(), Error> {
let mut transaction = self.common.load()?;

let account_id = match Address::from(self.account).kind() {
Kind::Account(key) => {
UnspecifiedAccountIdentifier::from_single_account(key.clone().into())
}
Kind::Multisig(key) => UnspecifiedAccountIdentifier::from_multi_account((*key).into()),
Kind::Single(_) => return Err(Error::AccountAddressSingle),
Kind::Group(_, _) => return Err(Error::AccountAddressGroup),
Kind::Script(_) => return Err(Error::AccountAddressScript),
};

transaction.add_input(interfaces::TransactionInput {
input: interfaces::TransactionInputType::Account(account_id.into()),
value: self.value,
})?;

transaction.add_account(self.account, self.value)?;
self.common.store(&transaction)
}
}
23 changes: 16 additions & 7 deletions jcli/src/jcli_lib/transaction/finalize.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::jcli_lib::transaction::{common, Error};
use crate::transaction::staging::Staging;
use chain_impl_mockchain::transaction::OutputPolicy;
use jormungandr_lib::interfaces;
use structopt::StructOpt;
Expand All @@ -20,15 +21,23 @@ impl Finalize {
pub fn exec(self) -> Result<(), Error> {
let mut transaction = self.common.load()?;

let fee_algo = self.fee.linear_fee();
let output_policy = match self.change {
None => OutputPolicy::Forget,
Some(change) => OutputPolicy::One(change.into()),
};

let _balance = transaction.balance_inputs_outputs(&fee_algo, output_policy)?;
finalize(self.fee, self.change, &mut transaction)?;

self.common.store(&transaction)?;
Ok(())
}
}

pub fn finalize(
fee: common::CommonFees,
change: Option<interfaces::Address>,
transaction: &mut Staging,
) -> Result<(), Error> {
let fee_algo = fee.linear_fee();
let output_policy = match change {
None => OutputPolicy::Forget,
Some(change) => OutputPolicy::One(change.into()),
};
let _balance = transaction.balance_inputs_outputs(&fee_algo, output_policy)?;
Ok(())
}
68 changes: 36 additions & 32 deletions jcli/src/jcli_lib/transaction/mk_witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::jcli_lib::{
};
use bech32::{self, ToBase32 as _};
use chain_core::property::Serialize as _;
use chain_impl_mockchain::key::EitherEd25519SecretKey;
use chain_impl_mockchain::{
account::SpendingCounter,
header::HeaderId,
Expand Down Expand Up @@ -62,38 +63,14 @@ impl std::str::FromStr for WitnessType {

impl MkWitness {
pub fn exec(self) -> Result<(), Error> {
let witness = match self.witness_type {
WitnessType::UTxO => {
let secret_key = read_ed25519_secret_key_from_file(&self.secret)?;
Witness::new_utxo(&self.genesis_block_hash, &self.sign_data_hash, |d| {
secret_key.sign(d)
})
}
WitnessType::OldUTxO => {
let secret_key = read_ed25519_secret_key_from_file(&self.secret)?;
Witness::new_old_utxo(
&self.genesis_block_hash,
&self.sign_data_hash,
|d| (secret_key.to_public(), secret_key.sign(d)),
&[0; 32],
)
}
WitnessType::Account => {
let account_spending_counter = self
.account_spending_counter
.ok_or(Error::MakeWitnessAccountCounterMissing)
.map(SpendingCounter::from)?;

let secret_key = read_ed25519_secret_key_from_file(&self.secret)?;
Witness::new_account(
&self.genesis_block_hash,
&self.sign_data_hash,
account_spending_counter,
|d| secret_key.sign(d),
)
}
};

let secret_key = read_ed25519_secret_key_from_file(&self.secret)?;
let witness = make_witness(
&self.witness_type,
&self.genesis_block_hash,
&self.sign_data_hash,
self.account_spending_counter.map(SpendingCounter::from),
&secret_key,
)?;
self.write_witness(&witness)
}

Expand All @@ -115,3 +92,30 @@ impl MkWitness {
})
}
}

pub fn make_witness(
witness_type: &WitnessType,
genesis_block_hash: &HeaderId,
sign_data_hash: &TransactionSignDataHash,
account_spending_counter: Option<SpendingCounter>,
secret_key: &EitherEd25519SecretKey,
) -> Result<Witness, Error> {
let witness = match witness_type {
WitnessType::UTxO => {
Witness::new_utxo(genesis_block_hash, sign_data_hash, |d| secret_key.sign(d))
}
WitnessType::OldUTxO => Witness::new_old_utxo(
genesis_block_hash,
sign_data_hash,
|d| (secret_key.to_public(), secret_key.sign(d)),
&[0; 32],
),
WitnessType::Account => Witness::new_account(
genesis_block_hash,
sign_data_hash,
account_spending_counter.ok_or(Error::MakeWitnessAccountCounterMissing)?,
|d| secret_key.sign(d),
),
};
Ok(witness)
}
Loading