Skip to content

Commit

Permalink
[wip] ffi for the transfer api
Browse files Browse the repository at this point in the history
  • Loading branch information
afilini committed Jun 24, 2020
1 parent 0d00e6b commit 5f0d7be
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ name = "rgb"
[dependencies]
amplify = "~0.1.6"
amplify_derive = "~0.1.1"
base64 = "0.12"
dotenv = "~0.15.0"
clap = "=3.0.0-beta.1"
chrono = { version = "~0.4.11", features = [ "serde" ] }
Expand Down
1 change: 1 addition & 0 deletions ffi/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# for detailed documentation of every option here.

language = "C"
cpp_compat = true

############## Options for Wrapping the Contents of the Header #################

Expand Down
2 changes: 1 addition & 1 deletion ffi/nodejs/binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"target_name": "rgb_node",
"sources": [ "swig_wrap.cxx" ],
"libraries": [ '<(module_root_dir)/../../target/debug/libffi.so'],
"libraries": [ '<(module_root_dir)/../../target/debug/librgb.so'],
'include_dirs': [
'../',
],
Expand Down
22 changes: 17 additions & 5 deletions ffi/nodejs/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,31 @@ const ex = require('./build/Release/rgb_node');

const config = {
network: "testnet",
stash_endpoint: "ipc:/home/orlovsky/repo/rgb-node/data/testnet/stashd.rpc",
stash_endpoint: "ipc:/tmp/rgb-node/testnet/stashd.rpc",
contract_endpoints: {
Fungible: "ipc:/home/orlovsky/repo/rgb-node/data/testnet/fungibled.rpc"
}
Fungible: "ipc:/tmp/rgb-node/testnet/fungibled.rpc"
},
threaded: false,
datadir: "/tmp/rgb-node/"
};

ex.start_rgb(JSON.stringify(config))
.then(r => ex.issue(r, JSON.stringify({
network: "bitcoin",
/*.then(r => ex.issue(r, JSON.stringify({
network: "testnet",
ticker: "USDT",
name: "USD Tether",
issue_structure: "SingleIssue",
allocations: [{ coins: 100, vout:0, txid: "0313ba7cfcaa66029a1a63918ebc426259f00953016c461663315d1bf6b83ab4" }],
precision: 0,
})))*/
.then(r => ex.transfer(r, JSON.stringify({
inputs: ["0313ba7cfcaa66029a1a63918ebc426259f00953016c461663315d1bf6b83ab4:0"],
allocate: [{ coins: 100, vout:1, txid: "0313ba7cfcaa66029a1a63918ebc426259f00953016c461663315d1bf6b83ab4" }],
invoice: "rgb20:outpoint1mzu8vz3jly3rzzkdpph583yahv9wktljtfcln6pe2le6n7ehqulstu967t?amount=5&asset=rgb:id1yqqqxya60n725eszngdx8yvwh3pxyk0sp9fszmzxze3nzhgm76ur4dqf2f7gy",
prototype_psbt: "cHNidP8BAFICAAAAAZ38ZijCbFiZ/hvT3DOGZb/VXXraEPYiCXPfLTht7BJ2AQAAAAD/////AfA9zR0AAAAAFgAUezoAv9wU0neVwrdJAdCdpu8TNXkAAAAATwEENYfPAto/0AiAAAAAlwSLGtBEWx7IJ1UXcnyHtOTrwYogP/oPlMAVZr046QADUbdDiH7h1A3DKmBDck8tZFmztaTXPa7I+64EcvO8Q+IM2QxqT64AAIAAAACATwEENYfPAto/0AiAAAABuQRSQnE5zXjCz/JES+NTzVhgXj5RMoXlKLQH+uP2FzUD0wpel8itvFV9rCrZp+OcFyLrrGnmaLbyZnzB1nHIPKsM2QxqT64AAIABAACAAAEBKwBlzR0AAAAAIgAgLFSGEmxJeAeagU4TcV1l82RZ5NbMre0mbQUIZFuvpjIBBUdSIQKdoSzbWyNWkrkVNq/v5ckcOrlHPY5DtTODarRWKZyIcSEDNys0I07Xz5wf6l0F1EFVeSe+lUKxYusC4ass6AIkwAtSriIGAp2hLNtbI1aSuRU2r+/lyRw6uUc9jkO1M4NqtFYpnIhxENkMak+uAACAAAAAgAAAAAAiBgM3KzQjTtfPnB/qXQXUQVV5J76VQrFi6wLhqyzoAiTACxDZDGpPrgAAgAEAAIAAAAAAACICA57/H1R6HV+S36K6evaslxpL0DukpzSwMVaiVritOh75EO3kXMUAAACAAAAAgAEAAIAA",
fee: 346,
change: "0313ba7cfcaa66029a1a63918ebc426259f00953016c461663315d1bf6b83ab4:2",
consignment_file: "/tmp/rgb-node/output/consignment",
transaction_file: "/tmp/rgb-node/output/transaction"
})))
.catch(e => console.log(e));
58 changes: 53 additions & 5 deletions ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ use log::info;

use serde::Deserialize;

use rgb::lnpbp::bitcoin::OutPoint;

use rgb::lnpbp::bp;
use rgb::lnpbp::lnp::transport::zmq::{SocketLocator, UrlError};
use rgb::lnpbp::rgb::Amount;

use rgb::fungible::{IssueStructure, Outcoins};
use rgb::fungible::{IssueStructure, Outcoins, Invoice};
use rgb::i9n::*;
use rgb::rgbd::ContractName;
use rgb::util::SealSpec;
Expand Down Expand Up @@ -150,13 +152,19 @@ fn _start_rgb(json: *mut c_char) -> Result<Runtime, String> {
Runtime::init(config).map_err(|e| format!("{:?}", e))
}

#[no_mangle]
pub extern "C" fn start_rgb(json: *mut c_char) -> CResult {
if cfg!(target_os = "android") {
#[cfg(target_os = "android")]
fn start_logger() {
android_logger::init_once(
android_logger::Config::default().with_min_level(log::Level::Debug),
);
}
}

#[cfg(not(target_os = "android"))]
fn start_logger() {}

#[no_mangle]
pub extern "C" fn start_rgb(json: *mut c_char) -> CResult {
start_logger();

info!("Starting RGB...");

Expand Down Expand Up @@ -206,3 +214,43 @@ fn _issue(runtime: &COpaqueStruct, json: *mut c_char) -> Result<(), String> {
pub extern "C" fn issue(runtime: &COpaqueStruct, json: *mut c_char) -> CResult {
_issue(runtime, json).into()
}

#[derive(Debug, Deserialize)]
struct TransferArgs {
inputs: Vec<OutPoint>,
allocate: Vec<Outcoins>,
#[serde(with = "serde_with::rust::display_fromstr")]
invoice: Invoice,
prototype_psbt: String,
fee: u64,
change: OutPoint,
consignment_file: String,
transaction_file: String,
}

fn _transfer(runtime: &COpaqueStruct, json: *mut c_char) -> Result<(), String> {
let runtime = Runtime::from_opaque(runtime)?;
let data: TransferArgs =
serde_json::from_str(ptr_to_string(json)?.as_str()).map_err(|e| format!("{:?}", e))?;
info!("{:?}", data);

runtime
.transfer(
data.inputs,
data.allocate,
data.invoice,
data.prototype_psbt,
data.fee,
data.change,
data.consignment_file,
data.transaction_file,
)
.map_err(|e| format!("{:?}", e))
.map(|_| ())
//.and_then(|r| serde_json::to_string(&r).map_err(|e| format!("{:?}", e)))
}

#[no_mangle]
pub extern "C" fn transfer(runtime: &COpaqueStruct, json: *mut c_char) -> CResult {
_transfer(runtime, json).into()
}
11 changes: 10 additions & 1 deletion src/i9n/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use crate::api::reply;
use crate::error::ServiceErrorDomain;

#[derive(Clone, Debug, Display, Error, From)]
#[derive(Debug, Display, Error, From)]
#[display_from(Debug)]
pub enum Error {
#[derive_from]
Expand All @@ -23,5 +23,14 @@ pub enum Error {
#[derive_from]
Reply(reply::Failure),

#[derive_from]
Base64(base64::DecodeError),

#[derive_from]
Bitcoin(lnpbp::bitcoin::consensus::encode::Error),

#[derive_from]
Encoding(lnpbp::strict_encoding::Error),

UnexpectedResponse,
}
84 changes: 79 additions & 5 deletions src/i9n/fungible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,24 @@
// If not, see <https://opensource.org/licenses/MIT>.

use ::std::sync::Arc;
use std::fs::File;
use std::path::PathBuf;

use lnpbp::bitcoin::OutPoint;
use lnpbp::bitcoin::consensus::encode::{deserialize, Encodable};
use lnpbp::bitcoin::util::psbt::{raw::Key, PartiallySignedTransaction};

use lnpbp::bp;
use lnpbp::lnp::presentation::Encode;
use lnpbp::lnp::Unmarshall;
use lnpbp::rgb::Amount;
use lnpbp::rgb::{Amount, PSBT_PUBKEY_KEY, PSBT_FEE_KEY};

use super::{Error, Runtime};
use crate::api::{fungible::Issue, fungible::Request, reply, Reply};
use crate::api::{fungible::Issue, fungible::Request, fungible::TransferApi, reply, Reply};
use crate::error::ServiceErrorDomain;
use crate::fungible::{IssueStructure, Outcoins};
use crate::fungible::{Invoice, IssueStructure, Outcoins, Outcoincealed, Outpoint};
use crate::util::SealSpec;
use crate::util::file::ReadWrite;

impl Runtime {
fn command(&mut self, command: Request) -> Result<Arc<Reply>, ServiceErrorDomain> {
Expand Down Expand Up @@ -70,8 +77,75 @@ impl Runtime {
}
}

pub fn transfer(&mut self) -> Result<(), Error> {
unimplemented!()
pub fn transfer(
&mut self,
inputs: Vec<OutPoint>,
allocate: Vec<Outcoins>,
invoice: Invoice,
prototype_psbt: String,
fee: u64,
change: OutPoint,
consignment_file: String,
transaction_file: String,
) -> Result<(), Error> {
let seal_confidential = match invoice.outpoint {
Outpoint::BlindedUtxo(outpoint_hash) => outpoint_hash,
Outpoint::Address(_address) => unimplemented!(),
};

let pubkey_key = Key {
type_value: 0xFC,
key: PSBT_PUBKEY_KEY.to_vec(),
};
let fee_key = Key {
type_value: 0xFC,
key: PSBT_FEE_KEY.to_vec(),
};

let psbt_bytes = base64::decode(&prototype_psbt)?;
let mut psbt: PartiallySignedTransaction = deserialize(&psbt_bytes)?;

psbt.global
.unknown
.insert(fee_key, fee.to_be_bytes().to_vec());
for output in &mut psbt.outputs {
output.unknown.insert(
pubkey_key.clone(),
output.hd_keypaths.keys().next().unwrap().to_bytes(),
);
}
// trace!("{:?}", psbt);

let api = TransferApi {
psbt,
contract_id: invoice.contract_id,
inputs,
ours: allocate,
theirs: vec![Outcoincealed {
coins: invoice.amount,
seal_confidential,
}],
change,
};

// TODO: Do tx output reorg for deterministic ordering

match &*self.command(Request::Transfer(api))? {
Reply::Failure(failure) => Err(Error::Reply(failure.clone())),
Reply::Transfer(transfer) => {
transfer.consignment.write_file(PathBuf::from(&consignment_file))?;
let out_file = File::create(&transaction_file)
.expect("can't create output transaction file");
transfer.psbt.consensus_encode(out_file)?;
println!(
"Transfer succeeded, consignment data are written to {:?}, partially signed witness transaction to {:?}",
consignment_file, transaction_file
);

Ok(())
}
_ => Err(Error::UnexpectedResponse),
}
}

pub fn sync(&mut self) -> Result<reply::SyncFormat, Error> {
Expand Down

0 comments on commit 5f0d7be

Please sign in to comment.