@@ -3,7 +3,7 @@ use borsh_derive::{BorshDeserialize, BorshSerialize};
33use company:: Company ;
44use contact:: Contact ;
55use serde:: { Deserialize , Serialize } ;
6- use std:: { fmt, pin:: Pin } ;
6+ use std:: { fmt, pin:: Pin , str :: FromStr } ;
77use thiserror:: Error ;
88use util:: is_blank;
99
@@ -20,6 +20,137 @@ pub mod notification;
2020mod tests;
2121pub mod util;
2222
23+ const ID_PREFIX : & str = "bitcr" ;
24+ const NETWORK_MAINNET : char = 'm' ;
25+ const NETWORK_TESTNET : char = 't' ;
26+ const NETWORK_TESTNET4 : char = 'T' ;
27+ const NETWORK_REGTEST : char = 'r' ;
28+
29+ fn network_char ( network : & bitcoin:: Network ) -> char {
30+ match network {
31+ bitcoin:: Network :: Bitcoin => NETWORK_MAINNET ,
32+ bitcoin:: Network :: Testnet => NETWORK_TESTNET ,
33+ bitcoin:: Network :: Testnet4 => NETWORK_TESTNET4 ,
34+ bitcoin:: Network :: Signet => unreachable ! ( ) ,
35+ bitcoin:: Network :: Regtest => NETWORK_REGTEST ,
36+ _ => unreachable ! ( ) ,
37+ }
38+ }
39+
40+ /// A bitcr Node ID of the format <prefix><network><pub_key>
41+ /// Example: bitcrt039180c169e5f6d7c579cf1cefa37bffd47a2b389c8125601f4068c87bea795943
42+ /// The prefix is bitcr
43+ /// The pub key is a secp256k1 public key
44+ /// The network character can be parsed like this:
45+ /// * m => Mainnet
46+ /// * t => Testnet
47+ /// * T => Testnet4
48+ /// * r => Regtest
49+ #[ derive( Debug , Clone , Eq , PartialEq , PartialOrd , Ord ) ]
50+ pub struct NodeId {
51+ pub_key : bitcoin:: secp256k1:: PublicKey ,
52+ network : bitcoin:: Network ,
53+ }
54+
55+ impl NodeId {
56+ pub fn new ( pub_key : bitcoin:: secp256k1:: PublicKey , network : bitcoin:: Network ) -> Self {
57+ Self { pub_key, network }
58+ }
59+
60+ pub fn network ( & self ) -> bitcoin:: Network {
61+ self . network
62+ }
63+
64+ pub fn pub_key ( & self ) -> bitcoin:: secp256k1:: PublicKey {
65+ self . pub_key
66+ }
67+
68+ pub fn npub ( & self ) -> nostr:: PublicKey {
69+ nostr:: PublicKey :: from ( self . pub_key . x_only_public_key ( ) . 0 )
70+ }
71+
72+ pub fn equals_npub ( & self , npub : & nostr:: PublicKey ) -> bool {
73+ self . npub ( ) == * npub
74+ }
75+ }
76+
77+ impl std:: fmt:: Display for NodeId {
78+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
79+ write ! (
80+ f,
81+ "{}{}{}" ,
82+ ID_PREFIX ,
83+ network_char( & self . network) ,
84+ self . pub_key
85+ )
86+ }
87+ }
88+
89+ impl FromStr for NodeId {
90+ type Err = ValidationError ;
91+
92+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
93+ if !s. starts_with ( ID_PREFIX ) {
94+ return Err ( ValidationError :: InvalidNodeId ) ;
95+ }
96+
97+ let network = match s. chars ( ) . nth ( ID_PREFIX . len ( ) ) {
98+ None => {
99+ return Err ( ValidationError :: InvalidNodeId ) ;
100+ }
101+ Some ( network_str) => match network_str {
102+ NETWORK_MAINNET => bitcoin:: Network :: Bitcoin ,
103+ NETWORK_TESTNET => bitcoin:: Network :: Testnet ,
104+ NETWORK_TESTNET4 => bitcoin:: Network :: Testnet4 ,
105+ NETWORK_REGTEST => bitcoin:: Network :: Regtest ,
106+ _ => {
107+ return Err ( ValidationError :: InvalidNodeId ) ;
108+ }
109+ } ,
110+ } ;
111+
112+ let pub_key_str = & s[ ID_PREFIX . len ( ) + 1 ..] ;
113+ let pub_key = bitcoin:: secp256k1:: PublicKey :: from_str ( pub_key_str)
114+ . map_err ( |_| ValidationError :: InvalidNodeId ) ?;
115+
116+ Ok ( Self { pub_key, network } )
117+ }
118+ }
119+
120+ impl serde:: Serialize for NodeId {
121+ fn serialize < S > ( & self , s : S ) -> Result < S :: Ok , S :: Error >
122+ where
123+ S : serde:: Serializer ,
124+ {
125+ s. collect_str ( self )
126+ }
127+ }
128+
129+ impl < ' de > serde:: Deserialize < ' de > for NodeId {
130+ fn deserialize < D > ( d : D ) -> Result < Self , D :: Error >
131+ where
132+ D : serde:: Deserializer < ' de > ,
133+ {
134+ let s = String :: deserialize ( d) ?;
135+ NodeId :: from_str ( & s) . map_err ( serde:: de:: Error :: custom)
136+ }
137+ }
138+
139+ impl borsh:: BorshSerialize for NodeId {
140+ fn serialize < W : std:: io:: Write > ( & self , writer : & mut W ) -> std:: io:: Result < ( ) > {
141+ let node_id_str = self . to_string ( ) ;
142+ borsh:: BorshSerialize :: serialize ( & node_id_str, writer)
143+ }
144+ }
145+
146+ impl borsh:: BorshDeserialize for NodeId {
147+ fn deserialize_reader < R : std:: io:: Read > ( reader : & mut R ) -> std:: io:: Result < Self > {
148+ let node_id_str: String = borsh:: BorshDeserialize :: deserialize_reader ( reader) ?;
149+ NodeId :: from_str ( & node_id_str)
150+ . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: InvalidData , e) )
151+ }
152+ }
153+
23154/// Return type of an async function. Can be used to avoid async_trait
24155pub type BoxedFuture < ' a , T > = Pin < Box < dyn std:: future:: Future < Output = T > + Send + ' a > > ;
25156
@@ -258,6 +389,10 @@ pub enum ValidationError {
258389 #[ error( "invalid bill type" ) ]
259390 InvalidBillType ,
260391
392+ /// errors stemming from providing an invalid bill id
393+ #[ error( "invalid bill id" ) ]
394+ InvalidBillId ,
395+
261396 /// errors stemming from when the drawee is the payee
262397 #[ error( "Drawee can't be Payee at the same time" ) ]
263398 DraweeCantBePayee ,
@@ -463,6 +598,10 @@ pub enum ValidationError {
463598 #[ error( "Invalid contact type" ) ]
464599 InvalidContactType ,
465600
601+ /// error returned if the node id is not valid
602+ #[ error( "Invalid node id" ) ]
603+ InvalidNodeId ,
604+
466605 /// error returned if the identity type is not valid
467606 #[ error( "Invalid identity type" ) ]
468607 InvalidIdentityType ,
0 commit comments