Skip to content
This repository has been archived by the owner on Apr 13, 2021. It is now read-only.

Commit

Permalink
Fungible asset issue: fungible issue command
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed Apr 19, 2020
1 parent ec43fbb commit 34dc20e
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 70 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ rgb = { git = "https://github.com/rgb-org/rgb-sdk" }

[dependencies.lnpbp]
git = "https://github.com/lnp-bp/rust-lnpbp"
branch = "feat-wallet"
branch = "staging"
features = ["use-tor", "use-tokio", "use-log", "use-daemons", "use-rgb"]
6 changes: 3 additions & 3 deletions src/accounts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
// If not, see <https://opensource.org/licenses/MIT>.


use std::{io, fs, fmt, hash::Hash};
use std::{io, fs, fmt, hash::Hash, convert::TryInto};
use std::path::PathBuf;
use std::collections::HashMap;
use rand::{thread_rng, RngCore};

use lnpbp::bp;
use lnpbp::bitcoin;
use bitcoin::secp256k1;
use bitcoin::util::bip32::{ExtendedPubKey, DerivationPath, ChildNumber};
Expand All @@ -26,7 +27,6 @@ use bitcoin_wallet::{account::Seed, context::SecpContext};
use lnpbp::csv::serialize::{self, network::*, storage::*};

use crate::error::Error;
use lnpbp::miniscript::bitcoin::hashes::core::fmt::Formatter;


#[derive(Debug)]
Expand Down Expand Up @@ -128,7 +128,7 @@ impl Keyring {
let context = SecpContext::new();
let encrypted = seed.encrypt(passphrase)
.expect("Encryption failed");
let master_key = context.master_private_key(bitcoin::Network::Bitcoin, &seed)
let master_key = context.master_private_key(bp::Network::Mainnet.try_into().unwrap(), &seed)
.expect("Public key generation failed");
let xpubkey = context.extended_public_from_private(&master_key);
Keyring::Hierarchical {
Expand Down
144 changes: 83 additions & 61 deletions src/commands/fungible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

use std::{path::PathBuf, str::FromStr};
use clap::Clap;
use regex::Regex;

use lnpbp::bitcoin;
use bitcoin::{TxIn, OutPoint};
Expand All @@ -23,6 +24,18 @@ use lnpbp::{bp, rgb};
use ::rgb::fungible;


fn ticker_validator(name: String) -> Result<(), String> {
let re = Regex::new(r"^[A-Z]{3,8}$").expect("Regex parse failure");
if !re.is_match(&name) {
Err("Ticker name must be between 2 and 8 chars, contain no spaces and \
consist only of capital letters\
".to_string())
} else {
Ok(())
}
}


/// Defines information required to generate bitcoin transaction output from
/// command-line argument
#[derive(Clone, Debug, Display)]
Expand Down Expand Up @@ -67,74 +80,83 @@ pub enum Command {
},

/// Creates a new asset
Issue {
/// Limit for the total supply (by default equals to the `amount`
#[clap(short, long)]
supply: Option<rgb::data::Amount>,

/// Enables secondary issuance/inflation; takes UTXO seal definition
/// as its value
#[clap(short, long, requires("supply"))]
inflatable: Option<OutPoint>,

/// Precision, i.e. number of digits reserved for fractional part
#[clap(short, long, default_value="0")]
precision: u8,
Issue(Issue),

/// Dust limit for asset transfers; defaults to no limit
#[clap(short="D", long, default_value="0")]
dust_limit: rgb::data::Amount,

/// Filename to export asset genesis to;
/// saves into data dir if not provided
#[clap(short, long)]
output: Option<PathBuf>,

/// Asset ticker (will be capitalized)
ticker: String,

/// Asset title
title: String,

/// Asset description
#[clap(short, long)]
description: Option<String>,
/// Transfers some asset to another party
Pay(Pay)
}

/// Asset allocation, in form of <amount>@<txid>:<vout>
#[clap(min_values=1)]
allocate: Vec<fungible::Allocation>,
},
#[derive(Clap, Clone, Debug, Display)]
#[display_from(Debug)]
pub struct Issue {
/// Limit for the total supply (by default equals to the `amount`
#[clap(short, long)]
pub supply: Option<rgb::data::Amount>,

/// Enables secondary issuance/inflation; takes UTXO seal definition
/// as its value
#[clap(short, long, requires("supply"))]
pub inflatable: Option<OutPoint>,

/// Precision, i.e. number of digits reserved for fractional part
#[clap(short, long, default_value="0")]
pub precision: u8,

/// Dust limit for asset transfers; defaults to no limit
#[clap(short="D", long)]
pub dust_limit: Option<rgb::data::Amount>,

/// Filename to export asset genesis to;
/// saves into data dir if not provided
#[clap(short, long)]
pub output: Option<PathBuf>,

/// Asset ticker
#[clap(validator=ticker_validator)]
pub ticker: String,

/// Asset title
pub title: String,

/// Asset description
#[clap(short, long)]
pub description: Option<String>,

/// Asset allocation, in form of <amount>@<txid>:<vout>
#[clap(min_values=1)]
pub allocate: Vec<fungible::Allocation>,
}

/// Transfers some asset to another party
Pay {
/// Use custom commitment output for generated witness transaction
#[clap(long)]
commit_txout: Option<Output>,
#[derive(Clap, Clone, Debug, Display)]
#[display_from(Debug)]
pub struct Pay {
/// Use custom commitment output for generated witness transaction
#[clap(long)]
commit_txout: Option<Output>,

/// Adds output(s) to generated witness transaction
#[clap(long)]
txout: Vec<Output>,
/// Adds output(s) to generated witness transaction
#[clap(long)]
txout: Vec<Output>,

/// Adds input(s) to generated witness transaction
#[clap(long)]
txin: Vec<Input>,
/// Adds input(s) to generated witness transaction
#[clap(long)]
txin: Vec<Input>,

/// Allocates other assets to custom outputs
#[clap(short, long)]
allocate: Vec<fungible::Allocation>,
/// Allocates other assets to custom outputs
#[clap(short, long)]
allocate: Vec<fungible::Allocation>,

/// Saves witness transaction to a file instead of publishing it
#[clap(short, long)]
transaction: Option<PathBuf>,
/// Saves witness transaction to a file instead of publishing it
#[clap(short, long)]
transaction: Option<PathBuf>,

/// Saves proof data to a file instead of sending it to the remote party
#[clap(short, long)]
proof: Option<PathBuf>,
/// Saves proof data to a file instead of sending it to the remote party
#[clap(short, long)]
proof: Option<PathBuf>,

/// Invoice to pay
invoice: fungible::Invoice,
/// Invoice to pay
invoice: fungible::Invoice,

/// Overrides amount provided in the invoice
amount: rgb::data::Amount,
}
}
/// Overrides amount provided in the invoice
amount: rgb::data::Amount,
}
31 changes: 26 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
// If not, see <https://opensource.org/licenses/MIT>.


use std::path::PathBuf;
use std::{io, fs, path::PathBuf};
use clap::Clap;
use lnpbp::bp;
use lnpbp::rgb::commit::TransitionCommitment;

use crate::constants::*;
use crate::commands::*;
Expand Down Expand Up @@ -48,8 +50,8 @@ pub struct Opts {
pub bpd_subscr: String,

/// Network to use
#[clap(global=true, short, long, default_value="testnet", env="KALEIDOSCOPE_NETWORK")]
pub network: lnpbp::bitcoin::Network,
#[clap(global=true, short, long, default_value="signet", env="KALEIDOSCOPE_NETWORK")]
pub network: bp::Network,

#[clap(subcommand)]
pub command: Command
Expand All @@ -63,7 +65,7 @@ pub struct Opts {
#[display_from(Debug)]
pub struct Config {
pub verbose: u8,
pub network: lnpbp::bitcoin::Network,
pub network: bp::Network,
pub data_dir: PathBuf,
pub bpd_api: String,
pub bpd_subscr: String,
Expand Down Expand Up @@ -91,7 +93,7 @@ impl Default for Config {
fn default() -> Self {
Self {
verbose: 0,
network: lnpbp::bitcoin::Network::Testnet,
network: bp::Network::Signet,
data_dir: DATA_DIR.parse().expect("Parse of DATA_DIR constant has failed"),
bpd_api: BPD_API_ADDR.to_string(),
bpd_subscr: BPD_PUSH_ADDR.to_string()
Expand All @@ -104,6 +106,8 @@ impl Default for Config {
pub enum DataItem {
Root,
KeyringVault,
ContractsVault,
ContractGenesis(TransitionCommitment)
}

impl Config {
Expand All @@ -112,7 +116,24 @@ impl Config {
match item {
DataItem::Root => (),
DataItem::KeyringVault => path.push("vault.dat"),
DataItem::ContractsVault => {
path.push("contracts");
if !path.exists() {
fs::create_dir_all(path.clone()).unwrap();
}
},
DataItem::ContractGenesis(cmt) => {
path = self.data_path(DataItem::ContractsVault);
path.push(format!("{}", cmt));
path.set_extension("rgb");
},
}
path
}

pub fn data_writer(&self, item: DataItem) -> Result<impl io::Write, io::Error> {
let file_name = self.data_path(item);
let file = fs::File::create(file_name)?;
Ok(io::BufWriter::new(file))
}
}
4 changes: 4 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::io;
use tokio::task::JoinError;

use lnpbp::csv::serialize;
use lnpbp::rgb;


#[derive(Debug, Display, From)]
Expand All @@ -43,6 +44,9 @@ pub enum Error {
OperationNotSupported(String),

UnknownKeyring(usize),

#[derive_from]
FungibleSchemataError(rgb::schemata::fungible::Error)
}

impl std::error::Error for Error { }
Expand Down
5 changes: 5 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ async fn main() -> Result<(), Error> {
runtime.account_create(name, derivation_path, description.unwrap_or_default()),
_ => unimplemented!()
},
Command::Fungible(subcommand) => match subcommand {
fungible::Command::Issue(issue) =>
runtime.fungible_issue(issue),
_ => unimplemented!()
}
//Command::Query { query } => runtime.command_query(query).await?,
_ => unimplemented!()
}
Expand Down
31 changes: 31 additions & 0 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@ use bitcoin::network::constants::Network;
use bitcoin::Address;
use bitcoin_wallet::{account::*, context::*};

use crate::lnpbp::rgb::commit::Identifiable;
use lnpbp::rgb::data::amount;
use lnpbp::rgb::Rgb1;

use super::*;
use crate::constants::*;
use crate::error::Error;
use crate::accounts::{KeyringManager, Account};
use lnpbp::csv::Storage;


pub struct Runtime {
Expand Down Expand Up @@ -113,6 +118,32 @@ impl Runtime {
println!("New account {} successfully added to the default keyring and saved to the vault", name);
Ok(())
}

pub fn fungible_issue(mut self, issue: commands::fungible::Issue) -> Result<(), Error> {
info!("Issuing asset with parameters {}", issue);
let balances = issue.allocate.iter().map(|alloc| {
let confidential = amount::Confidential::from(alloc.amount);
(alloc.seal, confidential.commitment)
}).collect();
let genesis = Rgb1::issue(
self.config.network,
&issue.ticker,
&issue.title,
issue.description.as_deref(),
balances,
issue.precision,
issue.supply,
issue.dust_limit
)?;

let asset_id = genesis.commitment()
.expect("Probability of the commitment generation failure is less than negligible");
println!("New asset {} is issued with ContractId={}", issue.ticker, asset_id);

genesis.storage_serialize(self.config.data_writer(DataItem::ContractGenesis(asset_id))?)?;

Ok(())
}
}

impl Drop for Runtime {
Expand Down

0 comments on commit 34dc20e

Please sign in to comment.