@@ -21,6 +21,9 @@ use crate::{
21
21
/// keytype as defined in ELIP0100
22
22
pub const PSBT_ELEMENTS_HWW_GLOBAL_ASSET_METADATA : u8 = 0x00u8 ;
23
23
24
+ /// Token keytype as defined in ELIP0100
25
+ pub const PSBT_ELEMENTS_HWW_GLOBAL_REISSUANCE_TOKEN : u8 = 0x01u8 ;
26
+
24
27
/// Prefix for PSET hardware wallet extension as defined in ELIP0100
25
28
pub const PSET_HWW_PREFIX : & [ u8 ] = b"pset_hww" ;
26
29
@@ -33,7 +36,7 @@ impl PartiallySignedTransaction {
33
36
asset_id : AssetId ,
34
37
asset_meta : & AssetMetadata ,
35
38
) -> 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 ) ;
37
40
self . global
38
41
. proprietary
39
42
. insert ( key, asset_meta. serialize ( ) )
@@ -46,13 +49,41 @@ impl PartiallySignedTransaction {
46
49
& self ,
47
50
asset_id : AssetId ,
48
51
) -> 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 ) ;
50
53
51
54
self . global
52
55
. proprietary
53
56
. get ( & key)
54
57
. map ( |data| AssetMetadata :: deserialize ( data) )
55
58
}
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
+ }
56
87
}
57
88
58
89
/// Asset metadata, the contract and the outpoint used to issue the asset
@@ -62,15 +93,22 @@ pub struct AssetMetadata {
62
93
issuance_prevout : OutPoint ,
63
94
}
64
95
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 {
66
104
let mut key = Vec :: with_capacity ( 32 ) ;
67
105
asset_id
68
106
. consensus_encode ( & mut key)
69
107
. expect ( "vec doesn't err" ) ; // equivalent to asset_tag
70
108
71
109
ProprietaryKey {
72
110
prefix : PSET_HWW_PREFIX . to_vec ( ) ,
73
- subtype : 0x00 ,
111
+ subtype : keytype ,
74
112
key,
75
113
}
76
114
}
@@ -126,6 +164,57 @@ impl AssetMetadata {
126
164
}
127
165
}
128
166
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
+
129
218
#[ cfg( test) ]
130
219
mod test {
131
220
use std:: str:: FromStr ;
@@ -142,7 +231,7 @@ mod test {
142
231
AssetId ,
143
232
} ;
144
233
145
- use super :: { prop_key, AssetMetadata } ;
234
+ use super :: { prop_key, AssetMetadata , PSBT_ELEMENTS_HWW_GLOBAL_ASSET_METADATA , TokenMetadata } ;
146
235
147
236
#[ cfg( feature = "json-contract" ) ]
148
237
const CONTRACT_HASH : & str = "3c7f0a53c2ff5b99590620d7f6604a7a3a7bfbaaa6aa61f7bfc7833ca03cde82" ;
@@ -155,6 +244,9 @@ mod test {
155
244
const ELIP0100_IDENTIFIER : & str = "fc08707365745f68777700" ;
156
245
const ELIP0100_ASSET_TAG : & str =
157
246
"48f835622f34e8fdc313c90d4a8659aa4afe993e32dcb03ae6ec9ccdc6fcbe18" ;
247
+ const ELIP0100_TOKEN_ID : & str =
248
+ "d739234098f77172cb22f0de8affd6826d6b9d23d97e04575764786a5b0056e1" ;
249
+ const ELIP0100_ISSUANCE_BLINDED : bool = true ;
158
250
159
251
const ELIP0100_CONTRACT : & str = r#"{"entity":{"domain":"example.com"},"issuer_pubkey":"03455ee7cedc97b0ba435b80066fc92c963a34c600317981d135330c4ee43ac7a3","name":"Testcoin","precision":2,"ticker":"TEST","version":0}"# ;
160
252
const ELIP0100_PREVOUT_TXID : & str =
@@ -163,9 +255,12 @@ mod test {
163
255
const ELIP0100_PREVOUT_VOUT : u32 = 1 ;
164
256
const ELIP0100_ASSET_METADATA_RECORD_KEY : & str =
165
257
"fc08707365745f6877770018befcc6cd9cece63ab0dc323e99fe4aaa59864a0dc913c3fde8342f6235f848" ;
258
+ const ELIP0100_TOKEN_METADATA_RECORD_KEY : & str =
259
+ "fc08707365745f68777701e156005b6a78645757047ed9239d6b6d82d6ff8adef022cb7271f798402339d7" ;
166
260
const ELIP0100_ASSET_METADATA_RECORD_VALUE_WRONG : & str = "b47b22656e74697479223a7b22646f6d61696e223a226578616d706c652e636f6d227d2c226973737565725f7075626b6579223a22303334353565653763656463393762306261343335623830303636666339326339363361333463363030333137393831643133353333306334656534336163376133222c226e616d65223a2254657374636f696e222c22707265636973696f6e223a322c227469636b6572223a2254455354222c2276657273696f6e223a307d3514a07cf4812272c24a898c482f587a51126beef8c9b76a9e30bf41b0cbe53c01000000" ;
167
261
168
262
const ELIP0100_ASSET_METADATA_RECORD_VALUE : & str = "b47b22656e74697479223a7b22646f6d61696e223a226578616d706c652e636f6d227d2c226973737565725f7075626b6579223a22303334353565653763656463393762306261343335623830303636666339326339363361333463363030333137393831643133353333306334656534336163376133222c226e616d65223a2254657374636f696e222c22707265636973696f6e223a322c227469636b6572223a2254455354222c2276657273696f6e223a307d3ce5cbb041bf309e6ab7c9f8ee6b12517a582f488c894ac2722281f47ca0143501000000" ;
263
+ const ELIP0100_TOKEN_METADATA_RECORD_VALUE : & str = "0118befcc6cd9cece63ab0dc323e99fe4aaa59864a0dc913c3fde8342f6235f848" ;
169
264
fn mockup_asset_metadata ( ) -> ( AssetId , AssetMetadata ) {
170
265
(
171
266
AssetId :: from_str ( ASSET_ID ) . unwrap ( ) ,
@@ -197,7 +292,7 @@ mod test {
197
292
fn prop_key_serialize ( ) {
198
293
let asset_id = AssetId :: from_str ( ASSET_ID ) . unwrap ( ) ;
199
294
200
- let key = prop_key ( & asset_id) ;
295
+ let key = prop_key ( & asset_id, PSBT_ELEMENTS_HWW_GLOBAL_ASSET_METADATA ) ;
201
296
let mut vec = vec ! [ ] ;
202
297
key. consensus_encode ( & mut vec) . unwrap ( ) ;
203
298
@@ -265,6 +360,27 @@ mod test {
265
360
. replace( ELIP0100_PREVOUT_TXID , & txid_hex_non_convention) ,
266
361
"only change in the value is the txid"
267
362
) ;
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 ) ;
268
384
}
269
385
270
386
#[ cfg( feature = "json-contract" ) ]
0 commit comments