Skip to content

Commit 00610d6

Browse files
committed
bindings: add types for sequence and public_key
1 parent 85a33c5 commit 00610d6

File tree

4 files changed

+369
-1
lines changed

4 files changed

+369
-1
lines changed

lwk_bindings/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub use blockdata::wallet_tx_out::WalletTxOut;
7070

7171
pub use crate::contract::Contract;
7272
pub use crate::signer::{Bip, Signer};
73-
pub use crate::types::XOnlyPublicKey;
73+
pub use crate::types::{PublicKey, Sequence, XOnlyPublicKey};
7474
pub use crate::wollet::Wollet;
7575
pub use chain::Chain;
7676
pub use currency_code::CurrencyCode;

lwk_bindings/src/types/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66
77
mod asset_id;
88
mod hex;
9+
mod public_key;
910
mod secret_key;
11+
mod sequence;
1012
mod xonly_public_key;
1113

1214
pub use asset_id::AssetId;
1315
pub use hex::Hex;
16+
pub use public_key::PublicKey;
1417
pub use secret_key::SecretKey;
18+
pub use sequence::Sequence;
1519
pub use xonly_public_key::XOnlyPublicKey;
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
use super::SecretKey;
2+
3+
use crate::{LwkError, XOnlyPublicKey};
4+
5+
use std::fmt::Display;
6+
use std::str::FromStr;
7+
use std::sync::Arc;
8+
9+
use elements::bitcoin::secp256k1;
10+
use elements::hashes::hex::FromHex;
11+
use elements::hex::ToHex;
12+
13+
use lwk_wollet::elements_miniscript::ToPublicKey;
14+
15+
/// A Bitcoin ECDSA public key.
16+
///
17+
/// See [`elements::bitcoin::PublicKey`] for more details.
18+
#[derive(uniffi::Object, PartialEq, Eq, Debug, Clone, Copy)]
19+
pub struct PublicKey {
20+
inner: elements::bitcoin::PublicKey,
21+
}
22+
23+
impl From<elements::bitcoin::PublicKey> for PublicKey {
24+
fn from(inner: elements::bitcoin::PublicKey) -> Self {
25+
PublicKey { inner }
26+
}
27+
}
28+
29+
impl From<PublicKey> for elements::bitcoin::PublicKey {
30+
fn from(value: PublicKey) -> Self {
31+
value.inner
32+
}
33+
}
34+
35+
impl From<&PublicKey> for elements::bitcoin::PublicKey {
36+
fn from(value: &PublicKey) -> Self {
37+
value.inner
38+
}
39+
}
40+
41+
impl AsRef<elements::bitcoin::PublicKey> for PublicKey {
42+
fn as_ref(&self) -> &elements::bitcoin::PublicKey {
43+
&self.inner
44+
}
45+
}
46+
47+
impl FromStr for PublicKey {
48+
type Err = LwkError;
49+
50+
fn from_str(s: &str) -> Result<Self, Self::Err> {
51+
let inner = elements::bitcoin::PublicKey::from_str(s).map_err(|e| LwkError::Generic {
52+
msg: format!("Invalid public key: {e}"),
53+
})?;
54+
Ok(Self { inner })
55+
}
56+
}
57+
58+
impl Display for PublicKey {
59+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60+
write!(f, "{}", self.inner)
61+
}
62+
}
63+
64+
#[uniffi::export]
65+
impl PublicKey {
66+
/// See [`elements::bitcoin::PublicKey::from_slice`].
67+
#[uniffi::constructor]
68+
pub fn from_bytes(bytes: &[u8]) -> Result<Arc<Self>, LwkError> {
69+
let inner =
70+
elements::bitcoin::PublicKey::from_slice(bytes).map_err(|e| LwkError::Generic {
71+
msg: format!("Invalid public key: {e}"),
72+
})?;
73+
Ok(Arc::new(PublicKey { inner }))
74+
}
75+
76+
/// Creates a `PublicKey` from a hex string.
77+
#[uniffi::constructor]
78+
pub fn from_hex(hex: &str) -> Result<Arc<Self>, LwkError> {
79+
let bytes = Vec::<u8>::from_hex(hex).map_err(|e| LwkError::Generic {
80+
msg: format!("Invalid hex: {e}"),
81+
})?;
82+
Self::from_bytes(&bytes)
83+
}
84+
85+
/// Derives a compressed `PublicKey` from a `SecretKey`.
86+
#[uniffi::constructor]
87+
pub fn from_secret_key(secret_key: &SecretKey) -> Arc<Self> {
88+
let secp = secp256k1::Secp256k1::new();
89+
let sk: secp256k1::SecretKey = secret_key.into();
90+
let inner_pk = secp256k1::PublicKey::from_secret_key(&secp, &sk);
91+
Arc::new(PublicKey {
92+
inner: elements::bitcoin::PublicKey::new(inner_pk),
93+
})
94+
}
95+
96+
/// See [`elements::bitcoin::PublicKey::to_bytes`].
97+
pub fn to_bytes(&self) -> Vec<u8> {
98+
self.inner.to_bytes()
99+
}
100+
101+
/// Returns the hex-encoded serialization.
102+
pub fn to_hex(&self) -> String {
103+
self.inner.to_bytes().to_hex()
104+
}
105+
106+
/// See [`elements::bitcoin::PublicKey::compressed`].
107+
pub fn is_compressed(&self) -> bool {
108+
self.inner.compressed
109+
}
110+
111+
/// Converts to an x-only public key hex string.
112+
pub fn to_x_only_public_key(&self) -> XOnlyPublicKey {
113+
self.inner.to_x_only_pubkey().into()
114+
}
115+
}
116+
117+
#[cfg(feature = "simplicity")]
118+
impl PublicKey {
119+
/// Convert to simplicityhl's PublicKey type.
120+
///
121+
/// TODO: delete when the version of elements is stabilized
122+
pub fn to_simplicityhl(
123+
&self,
124+
) -> Result<lwk_simplicity_options::simplicityhl::elements::bitcoin::PublicKey, LwkError> {
125+
lwk_simplicity_options::simplicityhl::elements::bitcoin::PublicKey::from_slice(
126+
&self.to_bytes(),
127+
)
128+
.map_err(|e| LwkError::Generic {
129+
msg: format!("Invalid public key: {e}"),
130+
})
131+
}
132+
}
133+
134+
#[cfg(test)]
135+
mod tests {
136+
use super::{PublicKey, SecretKey};
137+
use elements::hashes::hex::FromHex;
138+
139+
#[test]
140+
fn test_public_key_from_bytes() {
141+
let bytes = Vec::<u8>::from_hex(
142+
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
143+
)
144+
.unwrap();
145+
let pk = PublicKey::from_bytes(&bytes).unwrap();
146+
assert_eq!(pk.to_bytes(), bytes);
147+
assert!(pk.is_compressed());
148+
}
149+
150+
#[test]
151+
fn test_public_key_from_hex() {
152+
let hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
153+
let pk = PublicKey::from_hex(hex).unwrap();
154+
assert_eq!(pk.to_hex(), hex);
155+
}
156+
157+
#[test]
158+
fn test_public_key_from_secret_key() {
159+
let sk = SecretKey::from_bytes(&[1u8; 32]).unwrap();
160+
let pk = PublicKey::from_secret_key(&sk);
161+
assert_eq!(pk.to_bytes().len(), 33);
162+
assert!(pk.is_compressed());
163+
}
164+
165+
#[test]
166+
fn test_public_key_invalid() {
167+
assert!(PublicKey::from_bytes(&[0; 32]).is_err());
168+
assert!(PublicKey::from_bytes(&[0; 34]).is_err());
169+
assert!(PublicKey::from_bytes(&[0; 33]).is_err());
170+
}
171+
172+
#[test]
173+
fn test_public_key_roundtrip() {
174+
let hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
175+
let pk = PublicKey::from_hex(hex).unwrap();
176+
let pk2 = PublicKey::from_bytes(&pk.to_bytes()).unwrap();
177+
assert_eq!(*pk, *pk2);
178+
}
179+
180+
#[test]
181+
fn test_public_key_to_x_only() {
182+
let hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
183+
let pk = PublicKey::from_hex(hex).unwrap();
184+
let xonly = pk.to_x_only_public_key();
185+
assert_eq!(xonly.to_string().len(), 64);
186+
assert_eq!(
187+
xonly.to_string(),
188+
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
189+
);
190+
}
191+
192+
#[test]
193+
fn test_public_key_display() {
194+
let hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
195+
let pk = PublicKey::from_hex(hex).unwrap();
196+
assert_eq!(pk.to_string(), hex);
197+
}
198+
199+
#[test]
200+
fn test_public_key_as_ref() {
201+
use elements::bitcoin::PublicKey as BitcoinPublicKey;
202+
let hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
203+
let pk = PublicKey::from_hex(hex).unwrap();
204+
let inner: &BitcoinPublicKey = (*pk).as_ref();
205+
assert!(inner.compressed);
206+
}
207+
208+
#[test]
209+
fn test_public_key_uncompressed() {
210+
let hex = "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8";
211+
let pk = PublicKey::from_hex(hex).unwrap();
212+
assert!(!pk.is_compressed());
213+
assert_eq!(pk.to_bytes().len(), 65);
214+
}
215+
}

lwk_bindings/src/types/sequence.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use crate::LwkError;
2+
3+
use std::sync::Arc;
4+
5+
/// Bitcoin transaction input sequence number.
6+
///
7+
/// See [`elements::Sequence`] for more details.
8+
#[derive(uniffi::Object, PartialEq, Eq, Debug, Clone, Copy)]
9+
pub struct Sequence {
10+
inner: elements::Sequence,
11+
}
12+
13+
impl From<elements::Sequence> for Sequence {
14+
fn from(inner: elements::Sequence) -> Self {
15+
Sequence { inner }
16+
}
17+
}
18+
19+
impl From<Sequence> for elements::Sequence {
20+
fn from(value: Sequence) -> Self {
21+
value.inner
22+
}
23+
}
24+
25+
impl From<&Sequence> for elements::Sequence {
26+
fn from(value: &Sequence) -> Self {
27+
value.inner
28+
}
29+
}
30+
31+
impl AsRef<elements::Sequence> for Sequence {
32+
fn as_ref(&self) -> &elements::Sequence {
33+
&self.inner
34+
}
35+
}
36+
37+
#[uniffi::export]
38+
impl Sequence {
39+
/// See [`elements::Sequence::from_consensus`].
40+
#[uniffi::constructor]
41+
pub fn from_consensus(value: u32) -> Arc<Self> {
42+
Arc::new(Sequence {
43+
inner: elements::Sequence::from_consensus(value),
44+
})
45+
}
46+
47+
/// See [`elements::Sequence::ZERO`].
48+
#[uniffi::constructor]
49+
pub fn zero() -> Arc<Self> {
50+
Arc::new(Sequence {
51+
inner: elements::Sequence::ZERO,
52+
})
53+
}
54+
55+
/// See [`elements::Sequence::MAX`].
56+
#[uniffi::constructor]
57+
pub fn max() -> Arc<Self> {
58+
Arc::new(Sequence {
59+
inner: elements::Sequence::MAX,
60+
})
61+
}
62+
63+
/// See [`elements::Sequence::ENABLE_RBF_NO_LOCKTIME`].
64+
#[uniffi::constructor]
65+
pub fn enable_rbf_no_locktime() -> Arc<Self> {
66+
Arc::new(Sequence {
67+
inner: elements::Sequence::ENABLE_RBF_NO_LOCKTIME,
68+
})
69+
}
70+
71+
/// See [`elements::Sequence::ENABLE_LOCKTIME_NO_RBF`].
72+
#[uniffi::constructor]
73+
pub fn enable_locktime_no_rbf() -> Arc<Self> {
74+
Arc::new(Sequence {
75+
inner: elements::Sequence::ENABLE_LOCKTIME_NO_RBF,
76+
})
77+
}
78+
79+
/// See [`elements::Sequence::from_height`].
80+
#[uniffi::constructor]
81+
pub fn from_height(height: u16) -> Arc<Self> {
82+
Arc::new(Sequence {
83+
inner: elements::Sequence::from_height(height),
84+
})
85+
}
86+
87+
/// See [`elements::Sequence::from_512_second_intervals`].
88+
#[uniffi::constructor]
89+
pub fn from_512_second_intervals(intervals: u16) -> Arc<Self> {
90+
Arc::new(Sequence {
91+
inner: elements::Sequence::from_512_second_intervals(intervals),
92+
})
93+
}
94+
95+
/// See [`elements::Sequence::from_seconds_floor`].
96+
#[uniffi::constructor]
97+
pub fn from_seconds_floor(seconds: u32) -> Result<Arc<Self>, LwkError> {
98+
let inner =
99+
elements::Sequence::from_seconds_floor(seconds).map_err(|e| LwkError::Generic {
100+
msg: format!("Sequence from_seconds_floor error: {e}"),
101+
})?;
102+
Ok(Arc::new(Sequence { inner }))
103+
}
104+
105+
/// See [`elements::Sequence::from_seconds_ceil`].
106+
#[uniffi::constructor]
107+
pub fn from_seconds_ceil(seconds: u32) -> Result<Arc<Self>, LwkError> {
108+
let inner =
109+
elements::Sequence::from_seconds_ceil(seconds).map_err(|e| LwkError::Generic {
110+
msg: format!("Sequence from_seconds_ceil error: {e}"),
111+
})?;
112+
Ok(Arc::new(Sequence { inner }))
113+
}
114+
115+
/// See [`elements::Sequence::to_consensus_u32`].
116+
pub fn to_consensus_u32(&self) -> u32 {
117+
self.inner.to_consensus_u32()
118+
}
119+
120+
/// See [`elements::Sequence::is_final`].
121+
pub fn is_final(&self) -> bool {
122+
self.inner.is_final()
123+
}
124+
125+
/// See [`elements::Sequence::is_rbf`].
126+
pub fn is_rbf(&self) -> bool {
127+
self.inner.is_rbf()
128+
}
129+
130+
/// See [`elements::Sequence::is_relative_lock_time`].
131+
pub fn is_relative_lock_time(&self) -> bool {
132+
self.inner.is_relative_lock_time()
133+
}
134+
135+
/// See [`elements::Sequence::is_height_locked`].
136+
pub fn is_height_locked(&self) -> bool {
137+
self.inner.is_height_locked()
138+
}
139+
140+
/// See [`elements::Sequence::is_time_locked`].
141+
pub fn is_time_locked(&self) -> bool {
142+
self.inner.is_time_locked()
143+
}
144+
145+
/// See [`elements::Sequence::enables_absolute_lock_time`].
146+
pub fn enables_absolute_lock_time(&self) -> bool {
147+
self.inner.enables_absolute_lock_time()
148+
}
149+
}

0 commit comments

Comments
 (0)