1use std::{collections::HashSet, fmt, str::FromStr};
2
3use anyhow::{Context, anyhow};
4use bitcoin::{secp256k1, secp256k1::Secp256k1};
5use lexe_byte_array::ByteArray;
6use lexe_crypto::ed25519::{self, Signable};
7#[cfg(any(test, feature = "test-utils"))]
8use lexe_crypto::rng::FastRng;
9use lexe_hex::hex;
10use lexe_serde::hexstr_or_bytes;
11use lexe_sha256::sha256;
12use lexe_std::array;
13use lightning::util::scid_utils;
14#[cfg(any(test, feature = "test-utils"))]
15use proptest::{
16 arbitrary::{Arbitrary, any},
17 strategy::{BoxedStrategy, Strategy},
18};
19#[cfg(any(test, feature = "test-utils"))]
20use proptest_derive::Arbitrary;
21use ref_cast::RefCast;
22use serde::{Deserialize, Serialize};
23use thiserror::Error;
24
25#[cfg(any(test, feature = "test-utils"))]
26use crate::root_seed::RootSeed;
27use crate::secp256k1_ctx::SECP256K1;
28#[cfg(any(test, feature = "test-utils"))]
29use crate::test_utils::arbitrary;
30
31#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
33pub struct User {
34 pub user_pk: UserPk,
35 pub node_pk: NodePk,
36}
37
38#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
40pub struct MaybeUser {
41 pub maybe_user: Option<User>,
42}
43
44#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
49#[derive(Copy, Clone, Eq, PartialEq, Hash, RefCast, Serialize, Deserialize)]
50#[repr(transparent)]
51pub struct UserPk(#[serde(with = "hexstr_or_bytes")] [u8; 32]);
52
53#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
55#[derive(Copy, Clone, Hash, Eq, PartialEq, RefCast, Serialize, Deserialize)]
56#[repr(transparent)]
57pub struct ShortUserPk(#[serde(with = "hexstr_or_bytes")] [u8; 4]);
58
59#[derive(Debug, PartialEq, Serialize, Deserialize)]
61#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
62pub struct UserPkStruct {
63 pub user_pk: UserPk,
64}
65
66#[derive(Debug, PartialEq, Serialize, Deserialize)]
68#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
69pub struct UserPkSet {
70 #[cfg_attr(
71 any(test, feature = "test-utils"),
72 proptest(strategy = "arbitrary::any_hashset::<UserPk>()")
73 )]
74 pub user_pks: HashSet<UserPk>,
75}
76
77#[derive(Copy, Clone, Hash, Eq, PartialEq)]
96#[derive(RefCast, Serialize, Deserialize)]
97#[repr(transparent)]
98pub struct NodePk(pub secp256k1::PublicKey);
99
100#[derive(Clone, Debug, Eq, PartialEq)]
110#[derive(Serialize, Deserialize)]
111pub struct NodePkProof {
112 node_pk: NodePk,
113 sig: secp256k1::ecdsa::Signature,
114}
115
116#[derive(Debug, PartialEq, Serialize, Deserialize)]
118#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
119pub struct NodePkStruct {
120 pub node_pk: NodePk,
121}
122
123#[derive(Debug, Error)]
124#[error("invalid node pk proof signature")]
125pub struct InvalidNodePkProofSignature;
126
127#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
129#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
130#[derive(Serialize, Deserialize)]
131pub struct Scid(pub u64);
132
133#[derive(Debug, PartialEq, Serialize, Deserialize)]
135#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
136pub struct ScidStruct {
137 pub scid: Scid,
138}
139
140#[derive(Debug, PartialEq, Serialize, Deserialize)]
142#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
143pub struct Scids {
144 pub scids: Vec<Scid>,
145}
146
147#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
149#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
150pub struct UserScid {
151 pub node_pk: NodePk,
152 pub scid: Scid,
153}
154
155#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
157#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
158pub struct UserScids {
159 pub user_scids: Vec<UserScid>,
160}
161
162#[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))]
171#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
172pub struct GetNewScidsRequest {
173 pub node_pk: NodePk,
174 pub min_scids: usize,
175}
176
177impl UserPk {
180 pub const fn new(inner: [u8; 32]) -> Self {
181 Self(inner)
182 }
183
184 pub const fn from_ref(inner: &[u8; 32]) -> &Self {
185 lexe_std::const_utils::const_ref_cast(inner)
186 }
187
188 pub const fn as_ed25519(&self) -> &ed25519::PublicKey {
189 ed25519::PublicKey::from_ref(&self.0)
190 }
191
192 pub fn short(&self) -> ShortUserPk {
193 ShortUserPk::from(self)
194 }
195
196 pub fn from_u64(v: u64) -> Self {
198 let bytes = v.to_le_bytes();
200
201 let mut inner = [0u8; 32];
203 inner[0..8].copy_from_slice(&bytes);
204
205 Self(inner)
206 }
207
208 pub fn to_u64(self) -> u64 {
210 let mut bytes = [0u8; 8];
211 bytes.copy_from_slice(&self.0[0..8]);
212 u64::from_le_bytes(bytes)
213 }
214}
215
216lexe_byte_array::impl_byte_array!(UserPk, 32);
217lexe_byte_array::impl_fromstr_fromhex!(UserPk, 32);
218lexe_byte_array::impl_debug_display_as_hex!(UserPk);
219
220impl ShortUserPk {
223 pub const fn new(bytes: [u8; 4]) -> Self {
224 Self(bytes)
225 }
226
227 pub fn is_prefix_of(&self, long: &UserPk) -> bool {
229 self.0 == long.0[..4]
230 }
231}
232
233lexe_byte_array::impl_byte_array!(ShortUserPk, 4);
234lexe_byte_array::impl_fromstr_fromhex!(ShortUserPk, 4);
235lexe_byte_array::impl_debug_display_as_hex!(ShortUserPk);
236
237impl From<&UserPk> for ShortUserPk {
238 fn from(long: &UserPk) -> Self {
239 (long.0)[..4].try_into().map(Self).unwrap()
240 }
241}
242
243impl From<ed25519::PublicKey> for UserPk {
244 fn from(pk: ed25519::PublicKey) -> Self {
245 Self::new(pk.to_array())
246 }
247}
248
249impl NodePk {
252 pub fn inner(self) -> secp256k1::PublicKey {
253 self.0
254 }
255
256 pub fn as_inner(&self) -> &secp256k1::PublicKey {
257 &self.0
258 }
259
260 pub fn from_slice(bytes: &[u8]) -> Result<Self, secp256k1::Error> {
261 secp256k1::PublicKey::from_slice(bytes).map(Self)
262 }
263
264 pub fn from_inner_ref(pk: &secp256k1::PublicKey) -> &Self {
265 Self::ref_cast(pk)
266 }
267
268 pub fn to_array(&self) -> [u8; 33] {
269 self.0.serialize()
270 }
271
272 pub fn to_vec(&self) -> Vec<u8> {
273 self.0.serialize().to_vec()
274 }
275}
276
277impl FromStr for NodePk {
278 type Err = bitcoin::secp256k1::Error;
279 fn from_str(s: &str) -> Result<Self, Self::Err> {
280 secp256k1::PublicKey::from_str(s).map(Self)
282 }
283}
284
285impl fmt::Display for NodePk {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 write!(f, "{}", self.0)
289 }
290}
291
292impl fmt::Debug for NodePk {
293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 write!(f, "NodePk({self})")
295 }
296}
297
298impl From<secp256k1::PublicKey> for NodePk {
299 fn from(public_key: secp256k1::PublicKey) -> Self {
300 Self(public_key)
301 }
302}
303
304impl From<NodePk> for secp256k1::PublicKey {
305 fn from(node_pk: NodePk) -> secp256k1::PublicKey {
306 node_pk.0
307 }
308}
309
310#[cfg(any(test, feature = "test-utils"))]
311impl Arbitrary for NodePk {
312 type Parameters = ();
313 type Strategy = BoxedStrategy<Self>;
314
315 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
316 any::<FastRng>()
317 .prop_map(|mut rng| RootSeed::from_rng(&mut rng).derive_node_pk())
318 .boxed()
319 }
320}
321
322impl NodePkProof {
325 fn message(node_pk: &NodePk) -> secp256k1::Message {
327 let node_pk_bytes = node_pk.0.serialize();
328 let hash = sha256::digest_many(&[
329 &NodePkProof::DOMAIN_SEPARATOR,
330 &node_pk_bytes,
331 ]);
332 secp256k1::Message::from_digest(hash.to_array())
333 }
334
335 pub fn sign(node_key_pair: &secp256k1::Keypair) -> Self {
338 let node_pk = NodePk::from(node_key_pair.public_key());
339 let msg = Self::message(&node_pk);
340 let sig = SECP256K1.sign_ecdsa(&msg, &node_key_pair.secret_key());
341
342 Self { node_pk, sig }
343 }
344
345 pub fn verify(&self) -> Result<&NodePk, InvalidNodePkProofSignature> {
348 let msg = Self::message(&self.node_pk);
349 Secp256k1::verification_only()
350 .verify_ecdsa(&msg, &self.sig, &self.node_pk.0)
351 .map(|()| &self.node_pk)
352 .map_err(|_| InvalidNodePkProofSignature)
353 }
354
355 pub fn to_hex_string(&self) -> String {
357 hex::encode(&bcs::to_bytes(self).expect("Failed to serialize"))
358 }
359}
360
361impl Signable for NodePkProof {
362 const DOMAIN_SEPARATOR: [u8; 32] = array::pad(*b"LEXE-REALM::NodePkProof");
363}
364
365#[cfg(any(test, feature = "test-utils"))]
366impl Arbitrary for NodePkProof {
367 type Parameters = ();
368 type Strategy = BoxedStrategy<Self>;
369
370 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
371 any::<FastRng>()
372 .prop_map(|mut rng| {
373 let key_pair =
374 RootSeed::from_rng(&mut rng).derive_node_key_pair();
375 NodePkProof::sign(&key_pair)
376 })
377 .boxed()
378 }
379}
380
381impl Scid {
384 pub fn to_i64(&self) -> i64 {
387 let bytes = self.0.to_le_bytes();
388 i64::from_le_bytes(bytes)
389 }
390
391 pub fn from_i64(bytes_i64: i64) -> Self {
394 let bytes = bytes_i64.to_le_bytes();
395 Self(u64::from_le_bytes(bytes))
396 }
397}
398
399impl FromStr for Scid {
400 type Err = anyhow::Error;
401 fn from_str(s: &str) -> Result<Self, Self::Err> {
402 let (block, rest) = s
403 .split_once('x')
404 .with_context(|| format!("Bad scid '{s}'; want BLOCKxTXxVOUT"))?;
405 let (tx, vout) = rest
406 .split_once('x')
407 .with_context(|| format!("Bad scid '{s}'"))?;
408 scid_utils::scid_from_parts(
409 block.parse().context("Bad scid block")?,
410 tx.parse().context("Bad scid tx index")?,
411 vout.parse().context("Bad scid vout")?,
412 )
413 .map(Self)
414 .map_err(|e| anyhow!("Bad scid '{s}': {e:?}"))
415 }
416}
417
418impl fmt::Display for Scid {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 let block = scid_utils::block_from_scid(self.0);
422 let tx = scid_utils::tx_index_from_scid(self.0);
423 let vout = scid_utils::vout_from_scid(self.0);
424 write!(f, "{block}x{tx}x{vout}")
425 }
426}
427
428impl From<i64> for Scid {
429 fn from(i: i64) -> Self {
430 Self::from_i64(i)
431 }
432}
433impl From<Scid> for i64 {
434 fn from(scid: Scid) -> Self {
435 scid.to_i64()
436 }
437}
438
439#[cfg(test)]
440mod test {
441 use proptest::{prop_assume, proptest};
442
443 use super::*;
444 use crate::test_utils::roundtrip;
445
446 #[test]
447 fn user_node_pk_ser_examples() {
448 let mut rng = FastRng::from_u64(811011698);
449 let root_seed = RootSeed::from_rng(&mut rng);
450 let user_pk = root_seed.derive_user_pk();
451 let node_key_pair = root_seed.derive_node_key_pair();
452 let node_pk = NodePk(node_key_pair.public_key());
453 let node_pk_proof = NodePkProof::sign(&node_key_pair);
454
455 assert_eq!(
456 "52b999003525a3d905f9916eff26cee6625a3976fc25270ce5b3e79aa3c16f45",
457 user_pk.to_string()
458 );
459 assert_eq!(
460 "024de9a91aaf32588a7b0bb97ba7fad3db22fcfe62a52bc2b2d389c5fa9d946e1b",
461 node_pk.to_string(),
462 );
463 assert_eq!(
464 "024de9a91aaf32588a7b0bb97ba7fad3db22fcfe62a52bc2b2d389c5fa9d946e1b46304402206f762d23d206f3af2ffa452a71a11bca3df68838408851ab77931d7eb7fa1ef6022057141408428d6885d00ca6ca50e6d702aeab227c1550135be5fce4af4e726736",
465 node_pk_proof.to_hex_string(),
466 );
467 }
468
469 #[test]
470 fn user_pk_consistent() {
471 let user_pk1 = UserPk::new(hex::decode_const(
472 b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
473 ));
474 let user_pk2 = UserPk::new(hex::decode_const(
475 b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
476 ));
477 assert_eq!(user_pk1, user_pk2);
478 }
479
480 #[test]
481 fn user_pk_human_readable() {
482 roundtrip::fromstr_display_roundtrip_proptest::<UserPk>();
483 }
484
485 #[test]
486 fn user_pk_json() {
487 roundtrip::json_string_roundtrip_proptest::<UserPk>();
488 }
489
490 #[test]
491 fn node_pk_human_readable() {
492 roundtrip::fromstr_display_roundtrip_proptest::<NodePk>();
493 }
494
495 #[test]
496 fn node_pk_json() {
497 roundtrip::json_string_roundtrip_proptest::<NodePk>();
498 }
499
500 #[test]
501 fn node_pk_proof_bcs() {
502 roundtrip::bcs_roundtrip_proptest::<NodePkProof>();
503 }
504
505 #[test]
506 fn node_pk_proofs_verify() {
507 let arb_mutation = any::<Vec<u8>>()
508 .prop_filter("can't be empty or all zeroes", |m| {
509 !m.is_empty() && !m.iter().all(|x| x == &0u8)
510 });
511
512 proptest!(|(
513 mut rng: FastRng,
514 mut_offset in any::<usize>(),
515 mut mutation in arb_mutation,
516 )| {
517 let node_key_pair = RootSeed::from_rng(&mut rng)
518 .derive_node_key_pair();
519 let node_pk1 = NodePk::from(node_key_pair.public_key());
520
521 let proof1 = NodePkProof::sign(&node_key_pair);
522 let proof2 = NodePkProof::sign(&node_key_pair);
523
524 assert_eq!(proof1, proof2);
526
527 let node_pk2 = proof1.verify().unwrap();
529 assert_eq!(&node_pk1, node_pk2);
530
531 let mut proof_bytes = bcs::to_bytes(&proof1).unwrap();
532 mutation.truncate(proof_bytes.len());
537 prop_assume!(
538 !mutation.is_empty() && !mutation.iter().all(|x| x == &0)
539 );
540
541 for (idx_mut, m) in mutation.into_iter().enumerate() {
544 let idx_sig = idx_mut
545 .wrapping_add(mut_offset) % proof_bytes.len();
546 proof_bytes[idx_sig] ^= m;
547 }
548
549 bcs::from_bytes::<NodePkProof>(&proof_bytes)
551 .map_err(anyhow::Error::new)
552 .and_then(|proof| {
553 proof.verify()
554 .map(|_| ())
555 .map_err(anyhow::Error::new)
556 })
557 .unwrap_err();
558 });
559 }
560
561 #[test]
562 fn scid_basic() {
563 let scid = Scid(69);
564 assert_eq!(serde_json::to_string(&scid).unwrap(), "69");
565 }
566
567 #[test]
568 fn scid_human_roundtrip() {
569 let scid = Scid::from_str("802416x1618x1").unwrap();
570 assert_eq!(scid.to_string(), "802416x1618x1");
571 }
572
573 #[test]
574 fn scid_roundtrips() {
575 roundtrip::json_string_roundtrip_proptest::<Scid>();
576 roundtrip::fromstr_display_roundtrip_proptest::<Scid>();
577 }
578
579 #[test]
580 fn user_scid_roundtrips() {
581 roundtrip::json_value_roundtrip_proptest::<UserScid>();
582 }
583
584 #[test]
585 fn user_pk_struct_roundtrip() {
586 roundtrip::query_string_roundtrip_proptest::<UserPkStruct>();
587 }
588
589 #[test]
590 fn node_pk_struct_roundtrip() {
591 roundtrip::query_string_roundtrip_proptest::<NodePkStruct>();
592 }
593
594 #[test]
595 fn scid_qs_roundtrip() {
596 roundtrip::query_string_roundtrip_proptest::<ScidStruct>();
597 roundtrip::query_string_roundtrip_proptest::<GetNewScidsRequest>();
598 }
599}