Skip to content

Commit 6828535

Browse files
committed
update
1 parent ac7d6bf commit 6828535

File tree

7 files changed

+246
-128
lines changed

7 files changed

+246
-128
lines changed

bindings/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ crate-type=["staticlib", "cdylib"]
99

1010
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1111

12+
[features]
13+
debug_assertions = ["hex"]
14+
1215
[dependencies]
1316
lightning = { path = "../lightning" }
1417
secp256k1 = "0.15"
1518
bitcoin = "0.21"
19+
bitcoin_hashes = "0.7"
20+
hex = { version = "0.3", optional = true }
1621

1722
[build-dependencies]
1823
cbindgen = "0.13.1"

bindings/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# FFI binding interface for rust-lighting
2+
3+
Currently, it only supports C#, other languages are untested.
4+
5+
## General strategy
6+
7+
8+
## Type interoperability
9+
10+
Some types can not pass FFI boundary directly, so it has to be converted to FFI-safe type.
11+
The famous example of this is that array has to be passed as a pair of two items.
12+
13+
1. pointer to the first element of the array.
14+
2. length of the array.
15+
16+
For other rust-lightning and rust-bitcoin types, the conversion could be done in several ways.
17+
Here are the conversion tables for those types.
18+
19+
1. tuple ... analogous plain struct.
20+
1. `Transaction` ... ``
21+
22+

bindings/src/adaptors/mod.rs

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::sync::{Arc};
22

33
use secp256k1::constants::{SECRET_KEY_SIZE, PUBLIC_KEY_SIZE};
4-
use bitcoin::blockdata::transaction::{Transaction};
54
use bitcoin::util::key::PrivateKey;
5+
use bitcoin::blockdata::transaction::Transaction;
6+
use bitcoin::consensus::encode::serialize;
67

78
use lightning::util::logger::{Logger, Record};
89

@@ -22,56 +23,6 @@ struct SecretKey([u8; SECRET_KEY_SIZE]);
2223
#[repr(transparent)]
2324
struct PublicKey([u8; PUBLIC_KEY_SIZE]);
2425

25-
/*
26-
#[repr(C)]
27-
pub struct ChannelPublicKeys{
28-
/// The public key which is used to sign all commitment transactions, as it appears in the
29-
/// on-chain channel lock-in 2-of-2 multisig output.
30-
pub funding_pubkey: PublicKey,
31-
/// The base point which is used (with derive_public_revocation_key) to derive per-commitment
32-
/// revocation keys. The per-commitment revocation private key is then revealed by the owner of
33-
/// a commitment transaction so that their counterparty can claim all available funds if they
34-
/// broadcast an old state.
35-
pub revocation_basepoint: PublicKey,
36-
/// The base point which is used (with derive_public_key) to derive a per-commitment payment
37-
/// public key which receives immediately-spendable non-HTLC-encumbered funds.
38-
pub payment_basepoint: PublicKey,
39-
/// The base point which is used (with derive_public_key) to derive a per-commitment payment
40-
/// public key which receives non-HTLC-encumbered funds which are only available for spending
41-
/// after some delay (or can be claimed via the revocation path).
42-
pub delayed_payment_basepoint: PublicKey,
43-
/// The base point which is used (with derive_public_key) to derive a per-commitment public key
44-
/// which is used to encumber HTLC-in-flight outputs.
45-
pub htlc_basepoint: PublicKey,
46-
};
47-
48-
*/
49-
50-
#[repr(C)]
51-
struct FFIInMemoryChannelKeys {
52-
/// Private key of anchor tx
53-
funding_key: SecretKey,
54-
/// Local secret key for blinded revocation pubkey
55-
revocation_base_key: SecretKey,
56-
/// Local secret key used in commitment tx htlc outputs
57-
payment_base_key: SecretKey,
58-
/// Local secret key used in HTLC tx
59-
delayed_payment_base_key: SecretKey,
60-
/// Local htlc secret key used in commitment tx htlc outputs
61-
htlc_base_key: SecretKey,
62-
/// Commitment seed
63-
commitment_seed: [u8; 32],
64-
/// Local public keys and basepoints
65-
pub(crate) local_channel_pubkeys: ChannelPublicKeys,
66-
/// Remote public keys and base points
67-
pub(crate) remote_channel_pubkeys: Option<ChannelPublicKeys>,
68-
/// The total value of this channel
69-
channel_value_satoshis: u64,
70-
}
71-
72-
// #[repr(c)]
73-
// struct FFIChannelMonitor {
74-
// }
7526
#[repr(C)]
7627
struct FFIOutPoint {
7728
txid: [u8; 32],
@@ -80,9 +31,9 @@ struct FFIOutPoint {
8031

8132
#[repr(C)]
8233
pub struct FFIManyChannelMonitor {
83-
add_monitor_ptr: extern "C" fn(&Self, funding_txo: OutPoint, monitor: ChannelMonitor<InMemoryChannelKeys>) -> i32,
84-
update_monitor_ptr: extern "C" fn(&Self, funding_txo: OutPoint, monitor: ChannelMonitorUpdate) -> i32,
85-
get_and_clear_pending_htlcs_updated_ptr: extern "C" fn(&Self) -> Vec<HTLCUpdate>,
34+
add_monitor_ptr: extern "cdecl" fn(&Self, funding_txo: OutPoint, monitor: ChannelMonitor<InMemoryChannelKeys>) -> i32,
35+
update_monitor_ptr: extern "cdecl" fn(&Self, funding_txo: OutPoint, monitor: ChannelMonitorUpdate) -> i32,
36+
get_and_clear_pending_htlcs_updated_ptr: extern "cdecl" fn(&Self) -> Vec<HTLCUpdate>,
8637
}
8738
impl FFIManyChannelMonitor {
8839
fn errorcode_to_result(errorcode: i32) -> Result<(), ChannelMonitorUpdateErr> {
@@ -115,18 +66,19 @@ impl ManyChannelMonitor<InMemoryChannelKeys> for FFIManyChannelMonitor {
11566

11667
#[repr(C)]
11768
pub struct FFIBroadCaster {
118-
broadcast_transaction_ptr: extern "C" fn(&Self, tx: &Transaction),
69+
broadcast_transaction_ptr: extern "cdecl" fn(tx: *const u8, tx_len: u64),
11970
}
12071

12172
impl BroadcasterInterface for FFIBroadCaster {
12273
fn broadcast_transaction(&self, tx: &Transaction) {
123-
(self.broadcast_transaction_ptr)(self, tx)
74+
let b_tx = serialize(tx);
75+
(self.broadcast_transaction_ptr)(b_tx.as_ptr(), b_tx.len() as u64)
12476
}
12577
}
12678

12779
#[repr(C)]
12880
pub struct FFIFeeEstimator {
129-
get_est_sat_per_1000_weight_ptr: extern "C" fn (&Self, ConfirmationTarget) -> u64,
81+
get_est_sat_per_1000_weight_ptr: extern "cdecl" fn (&Self, ConfirmationTarget) -> u64,
13082
}
13183

13284
impl FeeEstimator for FFIFeeEstimator {

bindings/src/channelmanager.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use bitcoin::blockdata::transaction::Transaction;
12
use std::sync::Arc;
23
use std::time::{SystemTime, UNIX_EPOCH};
34

45
use lightning::util::config::UserConfig;
56
use lightning::chain::keysinterface::{KeysManager, InMemoryChannelKeys};
67
use lightning::ln::channelmanager::{ChannelManager};
8+
use lightning::chain::chaininterface::{BroadcasterInterface};
79

810
use crate::handle::{Out, Ref, RefMut,HandleShared};
911
use crate::error::FFIResult;
@@ -45,3 +47,14 @@ ffi_no_catch!{
4547
FFIResult::ok()
4648
}
4749
}
50+
51+
52+
#[cfg(feature = "debug_assertions")]
53+
ffi! {
54+
fn test_broadcaster(broadcaster_ptr: Ref<FFIBroadCaster>) -> FFIResult {
55+
let broadcaster = unsafe_block!("" => broadcaster_ptr.as_ref());
56+
let tx: Transaction = bitcoin::consensus::deserialize(&hex::decode("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700")?)?;
57+
broadcaster.broadcast_transaction(&tx);
58+
FFIResult::ok()
59+
}
60+
}

bindings/src/channelmonitor.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use std::sync::Arc;
2+
3+
use bitcoin::blockdata::script::Script;
4+
use bitcoin::blockdata::transaction::Transaction;
5+
use bitcoin::blockdata::block::Block;
6+
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
7+
8+
use lightning::chain::transaction::OutPoint;
9+
use lightning::chain::chaininterface::{ChainWatchInterface, ChainError};
10+
use lightning::chain::keysinterface::{InMemoryChannelKeys};
11+
use lightning::ln::channelmonitor::SimpleManyChannelMonitor;
12+
13+
use crate::adaptors::{FFIBroadCaster, FFIFeeEstimator, FFILogger};
14+
use crate::error::FFIResult;
15+
use crate::handle::{Ref, HandleShared};
16+
17+
#[repr(C)]
18+
pub struct FFIOutPoint {
19+
pub txid: Sha256dHash,
20+
pub index: u16,
21+
}
22+
23+
mod ChainWatchInterfaceFn {
24+
use super::{FFIChainWatchInterface, FFIOutPoint};
25+
pub type install_watch_tx_ptr = extern "cdecl" fn(*const FFIChainWatchInterface, script_pub_key_ptr: *const u8, script_pub_key_len: u64);
26+
pub type install_watch_outpoint_ptr = extern "cdecl" fn(*const FFIChainWatchInterface, outpoint: FFIOutPoint, out_script_ptr: *const u8, out_script_len: u64);
27+
pub type watch_all_txn = extern "cdecl" fn(*const FFIChainWatchInterface);
28+
// pub type get_chain_utxo = extern "cdecl" fn(*const FFIChainWatchInterface, genesis_hash: );
29+
}
30+
31+
#[repr(C)]
32+
pub struct FFIChainWatchInterface {
33+
install_watch_tx_ptr: ChainWatchInterfaceFn::install_watch_tx_ptr,
34+
install_watch_outpoint_ptr: ChainWatchInterfaceFn::install_watch_tx_ptr,
35+
}
36+
37+
impl ChainWatchInterface for FFIChainWatchInterface {
38+
fn install_watch_tx(&self, txid: &Sha256dHash, script_pub_key: &Script) {
39+
unimplemented!()
40+
}
41+
fn install_watch_outpoint(&self, outpoint: (Sha256dHash, u32), out_script: &Script) {
42+
unimplemented!()
43+
}
44+
fn watch_all_txn(&self) {
45+
unimplemented!()
46+
}
47+
fn get_chain_utxo(&self, genesis_hash: Sha256dHash, unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> {
48+
unimplemented!()
49+
}
50+
fn filter_block<'a>(&self, block: &'a Block) -> (Vec<&'a Transaction>, Vec<u32>) {
51+
unimplemented!()
52+
}
53+
fn reentered(&self) -> usize {
54+
unimplemented!()
55+
}
56+
}
57+
58+
type FFIManyChannelMonitor = SimpleManyChannelMonitor<FFIOutPoint, InMemoryChannelKeys, Arc<FFIBroadCaster>, Arc<FFIFeeEstimator>>;
59+
type FFIManyChannelMonitorHandle<'a> = HandleShared<'a, FFIManyChannelMonitor>;
60+
61+
62+
ffi_no_catch! {
63+
fn create_ffi_channel_monitor(
64+
chainwather: Ref<FFIChainWatchInterface>,
65+
broadcaster: Ref<FFIBroadCaster>,
66+
logger: Ref<FFILogger>,
67+
fee_est: Ref<FFIFeeEstimator>
68+
) -> FFIResult {
69+
// FFIManyChannelMonitor::new();
70+
FFIResult::ok()
71+
}
72+
}
73+
74+
ffi! {
75+
fn release_ffi_channel_monitor(handle: FFIManyChannelMonitorHandle) -> FFIResult {
76+
unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFIManyChannelMonitorHandle::dealloc(handle, |mut handle| {
77+
FFIResult::ok()
78+
}))
79+
}
80+
81+
}

bindings/src/ffi_test_utils.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::sync::Arc;
2+
use crate::error::FFIResult;
3+
4+
use crate::handle::{Out, Ref, HandleShared};
5+
6+
/// Lower level items for debugging wrapper library.
7+
/// When the FFI crashes without error messages, testing with these might be useful to understand
8+
/// what is the exact cause of the crash.
9+
10+
/// These tests should be used for asserting that the wrapper code can see the expected
11+
/// error messages when it fails (or succeeds).
12+
ffi! {
13+
fn ffi_test_error() -> FFIResult {
14+
use std::io;
15+
16+
FFIResult::internal_error().context(io::Error::new(io::ErrorKind::Other, "A test error."))
17+
}
18+
19+
fn ffi_test_ok() -> FFIResult {
20+
FFIResult::ok()
21+
}
22+
}
23+
24+
25+
#[repr(C)]
26+
pub struct FFITestInputStructWithPtr {
27+
fn_ptr: extern "cdecl" fn(*const Self),
28+
}
29+
impl FFITestInputStructWithPtr {
30+
pub fn call(&self) {
31+
(self.fn_ptr)(self as *const _)
32+
}
33+
}
34+
35+
pub struct FFITestOutputStruct {
36+
field: Arc<FFITestInputStructWithPtr>
37+
}
38+
39+
impl FFITestOutputStruct {
40+
fn complete(&self) {
41+
println!("finished!");
42+
self.field.call();
43+
}
44+
}
45+
46+
type FFITestOutputStructHandle<'a> = HandleShared<'a, FFITestOutputStruct>;
47+
48+
type FFIFunctionPointer = extern "cdecl" fn (input: *const FFITestInputStructWithPtr);
49+
type FFIFunctionPointerWithReturn = extern "cdecl" fn (input: i32) -> i32;
50+
type FFIFunctionPointerWithReturnRef = extern "cdecl" fn (input: *const i32) -> *const i32;
51+
52+
ffi! {
53+
fn ffi_test_function_ptr(struct_arg_ptr: Ref<FFITestInputStructWithPtr>, fn_ptr: Ref<FFIFunctionPointer>) -> FFIResult {
54+
// verify that if we pass functoin pointer and wrapper object separately and only calls the formar, it works.
55+
let func = unsafe_block!("" => fn_ptr.as_ref());
56+
let struct_arg = unsafe_block!("" => struct_arg_ptr.as_ref());
57+
(func)(struct_arg);
58+
FFIResult::ok()
59+
}
60+
61+
fn ffi_simple_nested_struct(input: Ref<FFIFunctionPointer>, output: Out<FFITestOutputStructHandle>) -> FFIResult {
62+
// verify that function pointer can be a member of other object.
63+
let input_ref = unsafe_block!(" " => input.as_ref());
64+
let result = FFITestOutputStruct { field: Arc::new(FFITestInputStructWithPtr{ fn_ptr: *input_ref }) };
65+
result.complete();
66+
unsafe_block!("" => output.init(HandleShared::alloc(result)));
67+
FFIResult::ok()
68+
}
69+
70+
fn ffi_simple_nested_struct_with_return_value(val_return_fn: Ref<FFIFunctionPointerWithReturn>, ref_return_fn: Ref<FFIFunctionPointerWithReturnRef>) -> FFIResult {
71+
// verify that functoin pointer can return value. and it could be seen in wrapper lang side.
72+
let val_return_fn_ref = unsafe_block!("" => val_return_fn.as_ref());
73+
let ref_return_fn_ref = unsafe_block!("" => ref_return_fn.as_ref());
74+
75+
let val = (val_return_fn_ref)(33);
76+
let r_ptr = (ref_return_fn_ref)(&44);
77+
let r = unsafe_block!("" => *r_ptr);
78+
println!("val was {}", val);
79+
println!("ref was {}", r);
80+
FFIResult::ok()
81+
}
82+
83+
fn ffi_test_simple_struct_with_function_pointer(
84+
input: Ref<FFITestInputStructWithPtr>,
85+
output: Out<FFITestOutputStructHandle>
86+
) -> FFIResult {
87+
// this test does not work, it seems that calling a function passed by wrapper struct will cause program to crash.
88+
let input_ref = unsafe_block!(" " => input.as_ref());
89+
// input_ref.call();
90+
let input_arc = unsafe_block!(" " => input.as_arc());
91+
// input_arc.call();
92+
// let result = FFITestOutputStruct{ field: input_arc };
93+
// result.field.call();
94+
// unsafe_block!("We know output is not null by wrapper macro. And we know `Out` is writable" => output.init(HandleShared::alloc(result)));
95+
FFIResult::ok()
96+
}
97+
98+
fn ffi_test_release(handle: FFITestOutputStructHandle) -> FFIResult {
99+
unsafe_block!("The upstream caller guarantees the handle will not be accessed after being freed" => FFITestOutputStructHandle::dealloc(handle, |mut handle| {
100+
handle.complete();
101+
102+
FFIResult::ok()
103+
}))
104+
}
105+
106+
}

0 commit comments

Comments
 (0)