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

Add create and redeem HTLC commands #19

Merged
merged 2 commits into from Mar 25, 2020
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

Some generated files are not rendered by default. Learn more.

@@ -11,6 +11,7 @@ structopt = "0.3.2"
dialoguer = "0.4.0"
bs58 = {version = "0.3.0", features=["check"]}
sodiumoxide = "0.2.5"
hex = "0.4.2"
hmac = "0.7.1"
sha2 = "0.8.0"
pbkdf2 = {version = "0.3.0", default-features=false }
@@ -15,7 +15,9 @@ pub fn cmd_hotspots(url: String, addresses: Vec<String>) -> Result {
fn print_results(results: Vec<(String, Result<Vec<Hotspot>>)>) {
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
table.set_titles(row!["Address", "Name", "Location", "City", "State", "Score"]);
table.set_titles(row![
"Address", "Name", "Location", "City", "State", "Score"
]);

for (address, result) in results {
#[allow(clippy::unused_unit)]
@@ -29,9 +31,15 @@ fn print_results(results: Vec<(String, Result<Vec<Hotspot>>)>) {
table.add_row(row![
hotspot.address,
hotspot.name.unwrap_or_else(|| "unknown".to_string()),
hotspot.location.unwrap_or_else(|| "uknown".to_string()),
hotspot.geocode.short_city.unwrap_or_else(|| "uknown".to_string()),
hotspot.geocode.short_state.unwrap_or_else(|| "uknown".to_string()),
hotspot.location.unwrap_or_else(|| "uknnown".to_string()),
hotspot
.geocode
.short_city
.unwrap_or_else(|| "unknown".to_string()),
hotspot
.geocode
.short_state
.unwrap_or_else(|| "unknown".to_string()),
hotspot.score
]);
}
@@ -0,0 +1,120 @@
use crate::{
keypair::{Keypair, PubKeyBin},
result::Result,
traits::{Sign, B58},
wallet::Wallet,
};
use helium_api::{Client, PendingTxnStatus};
use helium_proto::{BlockchainTxnCreateHtlcV1, BlockchainTxnRedeemHtlcV1, Txn};
use prettytable::Table;

pub fn cmd_create(
url: String,
wallet: &Wallet,
password: &str,
payee: String,
hashlock: String,
timelock: u64,
amount: u64,
commit: bool,
hash: bool,
) -> Result {
let client = Client::new_with_base_url(url);

let keypair = wallet.to_keypair(password.as_bytes())?;
let account = client.get_account(&keypair.public.to_b58()?)?;
let address = Keypair::gen_keypair().pubkey_bin();

let mut txn = BlockchainTxnCreateHtlcV1 {
amount,
fee: 0,
payee: PubKeyBin::from_b58(payee)?.to_vec(),
payer: keypair.pubkey_bin().to_vec(),
address: address.to_vec(),
hashlock: hex::decode(hashlock).unwrap(),
timelock,
nonce: account.speculative_nonce + 1,
signature: Vec::new(),
};
txn.sign(&keypair)?;
let wrapped_txn = Txn::CreateHtlc(txn.clone());

let status = if commit {
Some(client.submit_txn(wrapped_txn)?)
} else {
None
};

if hash {
println!("{}", status.map_or("none".to_string(), |s| s.hash));
} else {
print_create_txn(&txn, &status);
}

Ok(())
}

fn print_create_txn(txn: &BlockchainTxnCreateHtlcV1, status: &Option<PendingTxnStatus>) {
let mut table = Table::new();
table.add_row(row!["Address", "Payee", "Amount", "Hashlock", "Timelock"]);
table.add_row(row![
PubKeyBin::from_vec(&txn.address).to_b58().unwrap(),
PubKeyBin::from_vec(&txn.payee).to_b58().unwrap(),
txn.amount,
hex::encode(&txn.hashlock),
txn.timelock
]);
table.printstd();

if status.is_some() {
ptable!(
["Nonce", "Hash"],
[txn.nonce, status.as_ref().map_or("none", |s| &s.hash)]
);
}
}

pub fn cmd_redeem(
url: String,
wallet: &Wallet,
password: &str,
address: String,
preimage: String,
hash: bool,
) -> Result {
let client = Client::new_with_base_url(url);

let keypair = wallet.to_keypair(password.as_bytes())?;

let mut txn = BlockchainTxnRedeemHtlcV1 {
fee: 0,
payee: keypair.pubkey_bin().to_vec(),
address: PubKeyBin::from_b58(address)?.to_vec(),
preimage: preimage.into_bytes(),
signature: Vec::new(),
};
txn.sign(&keypair)?;
let wrapped_txn = Txn::RedeemHtlc(txn.clone());

let status = client.submit_txn(wrapped_txn)?;

if hash {
println!("{}", status.hash);
} else {
print_redeem_txn(&txn, &status);
}

Ok(())
}

fn print_redeem_txn(txn: &BlockchainTxnRedeemHtlcV1, status: &PendingTxnStatus) {
let mut table = Table::new();
table.add_row(row!["Payee", "Address", "Preimage", "Hash"]);
table.add_row(row![
PubKeyBin::from_vec(&txn.payee).to_b58().unwrap(),
PubKeyBin::from_vec(&txn.address).to_b58().unwrap(),
std::str::from_utf8(&txn.preimage).unwrap(),
status.hash
]);
table.printstd();
}
@@ -6,6 +6,7 @@ extern crate lazy_static;
mod cmd_balance;
mod cmd_create;
mod cmd_hotspots;
mod cmd_htlc;
mod cmd_info;
mod cmd_pay;
mod cmd_verify;
@@ -17,6 +18,7 @@ mod wallet;

use crate::{result::Result, traits::ReadWrite, wallet::Wallet};
use cmd_pay::Payee;
use helium_api::Hnt;
use std::{env, fs, path::PathBuf, process};
use structopt::StructOpt;

@@ -83,6 +85,8 @@ enum Cli {
#[structopt(long)]
hash: bool,
},
/// Create or Redeem from an HTLC address
Htlc(HtlcCmd),
}

#[derive(Debug, StructOpt)]
@@ -134,6 +138,58 @@ pub enum CreateCmd {
},
}

#[derive(Debug, StructOpt)]
/// Create or Redeem from an HTLC address
pub enum HtlcCmd {
/// Creates a new HTLC address with a specified hashlock and timelock (in block height), and transfers a value of tokens to it.
/// The transaction is not submitted to the system unless the '--commit' option is given.
Create {
This conversation was marked as resolved by amirhaleem

This comment has been minimized.

Copy link
@madninja

madninja Mar 24, 2020

Member

Could you do the same thing that master does for the pay command? I.e. have a --commit option that does the actual create or redeem and without it it just prints what it would end up creating (without the hash and nonce)?

/// Wallet to use as the payer
#[structopt(short = "f", long = "file", default_value = "wallet.key")]
files: Vec<PathBuf>,

/// The address of the intended payee for this HTLC
payee: String,

/// Number of hnt to send
#[structopt(long)]
hnt: Hnt,

/// A hex encoded SHA256 digest of a secret value (called a preimage) that locks this contract
#[structopt(short = "h", long = "hashlock")]
hashlock: String,

/// A specific blockheight after which the payer (you) can redeem their tokens
#[structopt(short = "t", long = "timelock")]
timelock: u64,

/// Commit the payment to the API
#[structopt(long)]
commit: bool,

/// Only output the submitted transaction hash.
#[structopt(long)]
hash: bool,
},
/// Redeem the balance from an HTLC address with the specified preimage for the hashlock
Redeem {
/// Wallet to use as the payer
#[structopt(short = "f", long = "file", default_value = "wallet.key")]
files: Vec<PathBuf>,

/// Address of the HTLC contract to redeem from
address: String,

/// The preimage used to create the hashlock for this contract address
#[structopt(short = "p", long = "preimage")]
preimage: String,

/// Only output the submitted transaction hash.
#[structopt(long)]
hash: bool,
},
}

fn main() {
let cli = Cli::from_args();
if let Err(e) = run(cli) {
@@ -233,6 +289,39 @@ fn run(cli: Cli) -> Result {
let wallet = load_wallet(files)?;
cmd_pay::cmd_pay(api_url(), &wallet, &pass, payees, commit, hash)
}
Cli::Htlc(HtlcCmd::Create {
payee,
hashlock,
timelock,
hnt,
files,
commit,
hash,
}) => {
let pass = get_password(false)?;
let wallet = load_wallet(files)?;
cmd_htlc::cmd_create(
api_url(),
&wallet,
&pass,
payee,
hashlock,
timelock,
hnt.to_bones(),
commit,
hash,
)
}
Cli::Htlc(HtlcCmd::Redeem {
address,
preimage,
files,
hash,
}) => {
let pass = get_password(false)?;
let wallet = load_wallet(files)?;
cmd_htlc::cmd_redeem(api_url(), &wallet, &pass, address, preimage, hash)
}
}
}

@@ -1,7 +1,10 @@
use crate::keypair::{Keypair, PubKeyBin, PublicKey, KEYTYPE_ED25519};
use crate::result::Result;
use bs58;
use helium_proto::{BlockchainTxnPaymentV1, BlockchainTxnPaymentV2, Message};
use helium_proto::{
BlockchainTxnCreateHtlcV1, BlockchainTxnPaymentV1, BlockchainTxnPaymentV2,
BlockchainTxnRedeemHtlcV1, Message,
};
use io::{Read, Write};
use std::io;

@@ -104,3 +107,21 @@ impl Sign for BlockchainTxnPaymentV2 {
Ok(())
}
}

impl Sign for BlockchainTxnCreateHtlcV1 {
fn sign(&mut self, keypair: &Keypair) -> Result {
let mut buf = vec![];
self.encode(&mut buf)?;
self.signature = keypair.sign(&buf);
Ok(())
}
}

impl Sign for BlockchainTxnRedeemHtlcV1 {
fn sign(&mut self, keypair: &Keypair) -> Result {
let mut buf = vec![];
self.encode(&mut buf)?;
self.signature = keypair.sign(&buf);
Ok(())
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.