Skip to content

Commit ad26d98

Browse files
committed
Map bitcoin::Address via a type rather than a string
...allowing users to build an `Address` safely.
1 parent 4c10412 commit ad26d98

File tree

3 files changed

+53
-4
lines changed

3 files changed

+53
-4
lines changed

c-bindings-gen/src/types.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
10761076
"str" => Some("crate::c_types::Str"),
10771077
"alloc::string::String"|"String"|"std::path::PathBuf" => Some("crate::c_types::Str"),
10781078

1079-
"bitcoin::address::Address"|"bitcoin::Address" => Some("crate::c_types::Str"),
1079+
"bitcoin::address::Address"|"bitcoin::Address" => Some("crate::c_types::Address"),
10801080

10811081
"std::time::Duration"|"core::time::Duration" => Some("u64"),
10821082
"std::time::SystemTime" => Some("u64"),
@@ -1206,6 +1206,8 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
12061206
"core::num::ParseIntError" => Some("u8::from_str_radix(\" a\", 10).unwrap_err() /*"),
12071207
"core::str::Utf8Error" => Some("core::str::from_utf8(&[0xff]).unwrap_err() /*"),
12081208

1209+
"bitcoin::address::Address"|"bitcoin::Address" => Some(""),
1210+
12091211
"std::time::Duration"|"core::time::Duration" => Some("core::time::Duration::from_secs("),
12101212
"std::time::SystemTime" => Some("(::std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_secs("),
12111213

@@ -1327,6 +1329,8 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
13271329
"core::num::ParseIntError" => Some("*/"),
13281330
"core::str::Utf8Error" => Some("*/"),
13291331

1332+
"bitcoin::address::Address"|"bitcoin::Address" => Some(".into_rust()"),
1333+
13301334
"std::time::Duration"|"core::time::Duration" => Some(")"),
13311335
"std::time::SystemTime" => Some("))"),
13321336

@@ -1442,7 +1446,8 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
14421446
"str" => Some(""),
14431447
"alloc::string::String"|"String"|"std::path::PathBuf" => Some(""),
14441448

1445-
"bitcoin::address::Address"|"bitcoin::Address" => Some("alloc::string::ToString::to_string(&"),
1449+
"bitcoin::address::Address"|"bitcoin::Address" if is_ref => Some("crate::c_types::Address::from_rust("),
1450+
"bitcoin::address::Address"|"bitcoin::Address" if !is_ref => Some("crate::c_types::Address::from_rust(&"),
14461451

14471452
"std::time::Duration"|"core::time::Duration" => Some(""),
14481453
"std::time::SystemTime" => Some(""),
@@ -1558,7 +1563,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
15581563
"alloc::string::String"|"String"|"std::path::PathBuf" if is_ref => Some(".as_str().into()"),
15591564
"alloc::string::String"|"String"|"std::path::PathBuf" => Some(".into()"),
15601565

1561-
"bitcoin::address::Address"|"bitcoin::Address" => Some(").into()"),
1566+
"bitcoin::address::Address"|"bitcoin::Address" => Some(")"),
15621567

15631568
"std::time::Duration"|"core::time::Duration" => Some(".as_secs()"),
15641569
"std::time::SystemTime" => Some(".duration_since(::std::time::SystemTime::UNIX_EPOCH).expect(\"Times must be post-1970\").as_secs()"),

genbindings.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ if [ "$CFLAGS_aarch64_apple_darwin" != "" -a "$HOST_OSX" = "true" ]; then
241241
fi
242242
cbindgen -v --config cbindgen.toml -o include/lightning.h >/dev/null 2>&1
243243

244+
echo "struct LDKBitcoinAddress;" >> include/ldk_rust_types.h
245+
echo "typedef struct LDKBitcoinAddress LDKBitcoinAddress;" >> include/ldk_rust_types.h
246+
244247
# cbindgen is relatively braindead when exporting typedefs -
245248
# it happily exports all our typedefs for private types, even with the
246249
# generics we specified in C mode! So we drop all those types manually here.

lightning-c-bindings/src/c_types/mod.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub mod derived;
55

66
use bitcoin::Transaction as BitcoinTransaction;
77
use bitcoin::Witness as BitcoinWitness;
8-
use bitcoin::address;
8+
use bitcoin::Address as BitcoinAddress;
99
use bitcoin::WitnessProgram as BitcoinWitnessProgram;
1010
use bitcoin::WitnessVersion as BitcoinWitnessVersion;
1111
use bitcoin::key::TweakedPublicKey as BitcoinTweakedPublicKey;
@@ -22,7 +22,11 @@ use bitcoin::secp256k1::Scalar as SecpScalar;
2222
use bech32;
2323

2424
use core::convert::TryInto; // Bindings need at least rustc 1.34
25+
use core::str::FromStr;
26+
2527
use alloc::borrow::ToOwned;
28+
use alloc::string::ToString;
29+
2630
use core::ffi::c_void;
2731

2832
pub(crate) use bitcoin::io::{self, Cursor, Read};
@@ -149,6 +153,43 @@ pub extern "C" fn WitnessProgram_clone(orig: &WitnessProgram) -> WitnessProgram
149153
/// Releases any memory held by the given `WitnessProgram` (which is currently none)
150154
pub extern "C" fn WitnessProgram_free(o: WitnessProgram) { }
151155

156+
#[derive(Clone)]
157+
#[repr(C)]
158+
/// Represents a valid Bitcoin on-chain address.
159+
pub struct Address {
160+
address: Box<BitcoinAddress>,
161+
}
162+
impl Address {
163+
pub(crate) fn into_rust(&self) -> BitcoinAddress {
164+
(*self.address).clone()
165+
}
166+
pub(crate) fn from_rust(address: &BitcoinAddress) -> Self {
167+
Self { address: Box::new(address.clone()) }
168+
}
169+
}
170+
171+
#[no_mangle]
172+
/// Gets the string representation of the address in `addr`
173+
pub extern "C" fn Address_to_string(addr: &Address) -> Str {
174+
addr.address.to_string().into()
175+
}
176+
#[no_mangle]
177+
/// Constructs a new `Address` (option) from the given string representation.
178+
///
179+
/// Returns `None` only if the address is invalid.
180+
pub extern "C" fn Address_new(s: Str) -> derived::COption_AddressZ {
181+
match BitcoinAddress::from_str(s.into_str()) {
182+
Ok(a) => derived::COption_AddressZ::Some(Address { address: Box::new(a.assume_checked()) }),
183+
Err(_) => derived::COption_AddressZ::None,
184+
}
185+
}
186+
#[no_mangle]
187+
/// Releases any memory held by the given `Address`
188+
pub extern "C" fn Address_free(o: Address) { }
189+
#[no_mangle]
190+
/// Creates a new Address which has the same data as `orig`
191+
pub extern "C" fn Address_clone(orig: &Address) -> Address { orig.clone() }
192+
152193
#[derive(Clone)]
153194
#[repr(C)]
154195
/// Represents a valid secp256k1 public key serialized in "compressed form" as a 33 byte array.

0 commit comments

Comments
 (0)