@@ -21,6 +21,9 @@ use crate::{
2121/// keytype as defined in ELIP0100
2222pub const PSBT_ELEMENTS_HWW_GLOBAL_ASSET_METADATA : u8 = 0x00u8 ;
2323
24+ /// Token keytype as defined in ELIP0100
25+ pub const PSBT_ELEMENTS_HWW_GLOBAL_REISSUANCE_TOKEN : u8 = 0x01u8 ;
26+
2427/// Prefix for PSET hardware wallet extension as defined in ELIP0100
2528pub const PSET_HWW_PREFIX : & [ u8 ] = b"pset_hww" ;
2629
@@ -33,7 +36,7 @@ impl PartiallySignedTransaction {
3336 asset_id : AssetId ,
3437 asset_meta : & AssetMetadata ,
3538 ) -> Option < Result < AssetMetadata , encode:: Error > > {
36- let key = prop_key ( & asset_id) ;
39+ let key = prop_key ( & asset_id, PSBT_ELEMENTS_HWW_GLOBAL_ASSET_METADATA ) ;
3740 self . global
3841 . proprietary
3942 . insert ( key, asset_meta. serialize ( ) )
@@ -46,13 +49,41 @@ impl PartiallySignedTransaction {
4649 & self ,
4750 asset_id : AssetId ,
4851 ) -> Option < Result < AssetMetadata , encode:: Error > > {
49- let key = prop_key ( & asset_id) ;
52+ let key = prop_key ( & asset_id, PSBT_ELEMENTS_HWW_GLOBAL_ASSET_METADATA ) ;
5053
5154 self . global
5255 . proprietary
5356 . get ( & key)
5457 . map ( |data| AssetMetadata :: deserialize ( data) )
5558 }
59+
60+ /// Add token information to the PSET, returns None if it wasn't present or Some with the old
61+ /// data if already in the PSET
62+ pub fn add_token_metadata (
63+ & mut self ,
64+ token_id : AssetId ,
65+ token_meta : & TokenMetadata
66+ ) -> Option < Result < TokenMetadata , encode:: Error > > {
67+ let key = prop_key ( & token_id, PSBT_ELEMENTS_HWW_GLOBAL_REISSUANCE_TOKEN ) ;
68+ self . global
69+ . proprietary
70+ . insert ( key, token_meta. serialize ( ) )
71+ . map ( |old| TokenMetadata :: deserialize ( & old) )
72+ }
73+
74+ /// Get token information from the PSET, returns None if there are no information regarding
75+ /// the given `token_id`` in the PSET
76+ pub fn get_token_metadata (
77+ & self ,
78+ token_id : AssetId
79+ ) -> Option < Result < TokenMetadata , encode:: Error > > {
80+ let key = prop_key ( & token_id, PSBT_ELEMENTS_HWW_GLOBAL_REISSUANCE_TOKEN ) ;
81+
82+ self . global
83+ . proprietary
84+ . get ( & key)
85+ . map ( |data| TokenMetadata :: deserialize ( data) )
86+ }
5687}
5788
5889/// Asset metadata, the contract and the outpoint used to issue the asset
@@ -62,15 +93,22 @@ pub struct AssetMetadata {
6293 issuance_prevout : OutPoint ,
6394}
6495
65- fn prop_key ( asset_id : & AssetId ) -> ProprietaryKey {
96+ /// Token metadata, the asset id and whether the issuance was blinded
97+ #[ derive( Debug , PartialEq , Eq ) ]
98+ pub struct TokenMetadata {
99+ asset_id : AssetId ,
100+ issuance_blinded : bool ,
101+ }
102+
103+ fn prop_key ( asset_id : & AssetId , keytype : u8 ) -> ProprietaryKey {
66104 let mut key = Vec :: with_capacity ( 32 ) ;
67105 asset_id
68106 . consensus_encode ( & mut key)
69107 . expect ( "vec doesn't err" ) ; // equivalent to asset_tag
70108
71109 ProprietaryKey {
72110 prefix : PSET_HWW_PREFIX . to_vec ( ) ,
73- subtype : 0x00 ,
111+ subtype : keytype ,
74112 key,
75113 }
76114}
@@ -126,6 +164,57 @@ impl AssetMetadata {
126164 }
127165}
128166
167+ impl TokenMetadata {
168+
169+ /// Create a new [`TokenMetadata`]
170+ pub fn new ( asset_id : AssetId , issuance_blinded : bool ) -> Self {
171+ Self { asset_id, issuance_blinded }
172+ }
173+
174+ /// Returns the asset_id
175+ pub fn asset_id ( & self ) -> & AssetId {
176+ & self . asset_id
177+ }
178+
179+ /// Returns whether the issuance was blinded or not
180+ pub fn issuance_blinded ( & self ) -> bool {
181+ self . issuance_blinded
182+ }
183+
184+ /// Serialize this metadata as defined by ELIP0100
185+ ///
186+ /// `<1 byte boolean issuanceBlinded><32-byte asset tag>`
187+ pub fn serialize ( & self ) -> Vec < u8 > {
188+ let mut result = vec ! [ ] ;
189+
190+ result. push ( self . issuance_blinded as u8 ) ;
191+
192+ self . asset_id
193+ . consensus_encode ( & mut result)
194+ . expect ( "vec doesn't err" ) ;
195+
196+ result
197+ }
198+
199+ /// Deserialize this metadata as defined by ELIP0100
200+ pub fn deserialize ( data : & [ u8 ] ) -> Result < TokenMetadata , encode:: Error > {
201+ let mut cursor = Cursor :: new ( data) ;
202+
203+ let byte = u8:: consensus_decode ( & mut cursor) ?;
204+ if byte > 1 {
205+ return Err ( encode:: Error :: ParseFailed ( "invalid issuanceBlinded" ) ) ;
206+ }
207+ let issuance_blinded = byte == 1 ;
208+
209+ let asset_id = AssetId :: consensus_decode ( & mut cursor) ?;
210+
211+ Ok ( TokenMetadata {
212+ asset_id,
213+ issuance_blinded
214+ } )
215+ }
216+ }
217+
129218#[ cfg( test) ]
130219mod test {
131220 use std:: str:: FromStr ;
@@ -142,7 +231,7 @@ mod test {
142231 AssetId ,
143232 } ;
144233
145- use super :: { prop_key, AssetMetadata } ;
234+ use super :: { prop_key, AssetMetadata , PSBT_ELEMENTS_HWW_GLOBAL_ASSET_METADATA , TokenMetadata } ;
146235
147236 #[ cfg( feature = "json-contract" ) ]
148237 const CONTRACT_HASH : & str = "3c7f0a53c2ff5b99590620d7f6604a7a3a7bfbaaa6aa61f7bfc7833ca03cde82" ;
@@ -155,6 +244,9 @@ mod test {
155244 const ELIP0100_IDENTIFIER : & str = "fc08707365745f68777700" ;
156245 const ELIP0100_ASSET_TAG : & str =
157246 "48f835622f34e8fdc313c90d4a8659aa4afe993e32dcb03ae6ec9ccdc6fcbe18" ;
247+ const ELIP0100_TOKEN_ID : & str =
248+ "d739234098f77172cb22f0de8affd6826d6b9d23d97e04575764786a5b0056e1" ;
249+ const ELIP0100_ISSUANCE_BLINDED : bool = true ;
158250
159251 const ELIP0100_CONTRACT : & str = r#"{"entity":{"domain":"example.com"},"issuer_pubkey":"03455ee7cedc97b0ba435b80066fc92c963a34c600317981d135330c4ee43ac7a3","name":"Testcoin","precision":2,"ticker":"TEST","version":0}"# ;
160252 const ELIP0100_PREVOUT_TXID : & str =
@@ -163,9 +255,12 @@ mod test {
163255 const ELIP0100_PREVOUT_VOUT : u32 = 1 ;
164256 const ELIP0100_ASSET_METADATA_RECORD_KEY : & str =
165257 "fc08707365745f6877770018befcc6cd9cece63ab0dc323e99fe4aaa59864a0dc913c3fde8342f6235f848" ;
258+ const ELIP0100_TOKEN_METADATA_RECORD_KEY : & str =
259+ "fc08707365745f68777701e156005b6a78645757047ed9239d6b6d82d6ff8adef022cb7271f798402339d7" ;
166260 const ELIP0100_ASSET_METADATA_RECORD_VALUE_WRONG : & str = "b47b22656e74697479223a7b22646f6d61696e223a226578616d706c652e636f6d227d2c226973737565725f7075626b6579223a22303334353565653763656463393762306261343335623830303636666339326339363361333463363030333137393831643133353333306334656534336163376133222c226e616d65223a2254657374636f696e222c22707265636973696f6e223a322c227469636b6572223a2254455354222c2276657273696f6e223a307d3514a07cf4812272c24a898c482f587a51126beef8c9b76a9e30bf41b0cbe53c01000000" ;
167261
168262 const ELIP0100_ASSET_METADATA_RECORD_VALUE : & str = "b47b22656e74697479223a7b22646f6d61696e223a226578616d706c652e636f6d227d2c226973737565725f7075626b6579223a22303334353565653763656463393762306261343335623830303636666339326339363361333463363030333137393831643133353333306334656534336163376133222c226e616d65223a2254657374636f696e222c22707265636973696f6e223a322c227469636b6572223a2254455354222c2276657273696f6e223a307d3ce5cbb041bf309e6ab7c9f8ee6b12517a582f488c894ac2722281f47ca0143501000000" ;
263+ const ELIP0100_TOKEN_METADATA_RECORD_VALUE : & str = "0118befcc6cd9cece63ab0dc323e99fe4aaa59864a0dc913c3fde8342f6235f848" ;
169264 fn mockup_asset_metadata ( ) -> ( AssetId , AssetMetadata ) {
170265 (
171266 AssetId :: from_str ( ASSET_ID ) . unwrap ( ) ,
@@ -197,7 +292,7 @@ mod test {
197292 fn prop_key_serialize ( ) {
198293 let asset_id = AssetId :: from_str ( ASSET_ID ) . unwrap ( ) ;
199294
200- let key = prop_key ( & asset_id) ;
295+ let key = prop_key ( & asset_id, PSBT_ELEMENTS_HWW_GLOBAL_ASSET_METADATA ) ;
201296 let mut vec = vec ! [ ] ;
202297 key. consensus_encode ( & mut vec) . unwrap ( ) ;
203298
@@ -265,6 +360,27 @@ mod test {
265360 . replace( ELIP0100_PREVOUT_TXID , & txid_hex_non_convention) ,
266361 "only change in the value is the txid"
267362 ) ;
363+
364+ let token_id = AssetId :: from_str ( ELIP0100_TOKEN_ID ) . unwrap ( ) ;
365+ let token_meta = TokenMetadata {
366+ asset_id,
367+ issuance_blinded : ELIP0100_ISSUANCE_BLINDED ,
368+ } ;
369+
370+ pset. add_token_metadata ( token_id, & token_meta) ;
371+
372+ let expected_key = Vec :: < u8 > :: from_hex ( ELIP0100_TOKEN_METADATA_RECORD_KEY ) . unwrap ( ) ;
373+
374+ let values: Vec < Vec < u8 > > = pset
375+ . global
376+ . get_pairs ( )
377+ . unwrap ( )
378+ . into_iter ( )
379+ . filter ( |p| serialize ( & p. key ) [ 1 ..] == expected_key[ ..] ) // NOTE key serialization contains an initial varint with the length of the key which is not present in the test vector
380+ . map ( |p| p. value )
381+ . collect ( ) ;
382+ assert_eq ! ( values. len( ) , 1 ) ;
383+ assert_eq ! ( values[ 0 ] . to_hex( ) , ELIP0100_TOKEN_METADATA_RECORD_VALUE ) ;
268384 }
269385
270386 #[ cfg( feature = "json-contract" ) ]
0 commit comments