1
1
use light_compressed_account:: Pubkey ;
2
2
use light_hasher:: { errors:: HasherError , sha256:: Sha256BE , Hasher , Poseidon } ;
3
3
use light_zero_copy:: { traits:: ZeroCopyAt , ZeroCopy , ZeroCopyMut } ;
4
- use solana_msg:: msg;
5
4
use zerocopy:: IntoBytes ;
6
5
7
6
use crate :: {
8
- hash_cache:: HashCache ,
9
- instructions:: mint_action:: CompressedMintInstructionData ,
10
- state:: { ExtensionStruct , ZExtensionStructMut } ,
11
- AnchorDeserialize , AnchorSerialize , CTokenError ,
7
+ hash_cache:: HashCache , instructions:: mint_action:: CompressedMintInstructionData ,
8
+ state:: ExtensionStruct , AnchorDeserialize , AnchorSerialize , CTokenError ,
12
9
} ;
13
10
14
11
// Order is optimized for hashing.
@@ -38,19 +35,16 @@ pub struct CompressedMint {
38
35
pub extensions : Option < Vec < ExtensionStruct > > ,
39
36
}
40
37
41
- // TODO: unify code if possible
42
- // use nested token metadata layout for data extension
43
- impl CompressedMint {
44
- #[ allow( dead_code) ]
45
- pub fn hash ( & self ) -> Result < [ u8 ; 32 ] , CTokenError > {
46
- let mut hash_cache = HashCache :: new ( ) ;
47
- self . hash_with_cache ( & mut hash_cache)
48
- }
49
-
50
- pub fn hash_with_cache ( & self , hash_cache : & mut HashCache ) -> Result < [ u8 ; 32 ] , CTokenError > {
51
- let hashed_spl_mint = hash_cache. get_or_hash_mint ( & self . spl_mint . into ( ) ) ?;
38
+ macro_rules! impl_compressed_mint_hash {
39
+ (
40
+ $self: ident,
41
+ $hash_cache: ident,
42
+ $is_decompressed: expr
43
+ $( , $deref_op: tt) ?
44
+ ) => { {
45
+ let hashed_spl_mint = $hash_cache. get_or_hash_mint( & $self. spl_mint. into( ) ) ?;
52
46
let mut supply_bytes = [ 0u8 ; 32 ] ;
53
- self . supply
47
+ $ self. supply
54
48
. as_bytes( )
55
49
. iter( )
56
50
. rev( )
@@ -59,18 +53,18 @@ impl CompressedMint {
59
53
60
54
let hashed_mint_authority;
61
55
let hashed_mint_authority_option =
62
- if let Some ( mint_authority) = self . mint_authority . as_ref ( ) {
63
- hashed_mint_authority = hash_cache. get_or_hash_pubkey ( & mint_authority. to_bytes ( ) ) ;
56
+ if let Some ( mint_authority) = $ self. mint_authority. as_ref( ) {
57
+ hashed_mint_authority = $ hash_cache. get_or_hash_pubkey( & ( $ ( $deref_op ) ? mint_authority) . to_bytes( ) ) ;
64
58
Some ( & hashed_mint_authority)
65
59
} else {
66
60
None
67
61
} ;
68
62
69
63
let hashed_freeze_authority;
70
64
let hashed_freeze_authority_option = if let Some ( freeze_authority) =
71
- self . freeze_authority . as_ref ( )
65
+ $ self. freeze_authority. as_ref( )
72
66
{
73
- hashed_freeze_authority = hash_cache. get_or_hash_pubkey ( & freeze_authority. to_bytes ( ) ) ;
67
+ hashed_freeze_authority = $ hash_cache. get_or_hash_pubkey( & ( $ ( $deref_op ) ? freeze_authority) . to_bytes( ) ) ;
74
68
Some ( & hashed_freeze_authority)
75
69
} else {
76
70
None
@@ -79,36 +73,44 @@ impl CompressedMint {
79
73
let mint_hash = CompressedMint :: hash_with_hashed_values(
80
74
& hashed_spl_mint,
81
75
& supply_bytes,
82
- self . decimals ,
83
- self . is_decompressed ,
76
+ $ self. decimals,
77
+ $ is_decompressed,
84
78
& hashed_mint_authority_option,
85
79
& hashed_freeze_authority_option,
86
- self . version ,
80
+ $ self. version,
87
81
) ?;
88
82
89
- if let Some ( extensions) = self . extensions . as_ref ( ) {
83
+ if let Some ( extensions) = $ self. extensions. as_ref( ) {
90
84
let mut extension_hashchain = [ 0u8 ; 32 ] ;
91
85
for extension in extensions. as_slice( ) {
92
- if self . version == 0 {
86
+ let extension_hash = if $self. version == 0 {
87
+ extension. hash:: <Poseidon >( ) ?
88
+ } else if $self. version == 1 {
89
+ extension. hash:: <Sha256BE >( ) ?
90
+ } else {
91
+ return Err ( CTokenError :: InvalidTokenDataVersion ) ;
92
+ } ;
93
+
94
+ if $self. version == 0 {
93
95
extension_hashchain = Poseidon :: hashv( & [
94
96
extension_hashchain. as_slice( ) ,
95
- extension . hash :: < Poseidon > ( ) ? . as_slice ( ) ,
97
+ extension_hash . as_slice( ) ,
96
98
] ) ?;
97
- } else if self . version == 1 {
99
+ } else if $ self. version == 1 {
98
100
extension_hashchain = Sha256BE :: hashv( & [
99
101
extension_hashchain. as_slice( ) ,
100
- extension . hash :: < Sha256BE > ( ) ? . as_slice ( ) ,
102
+ extension_hash . as_slice( ) ,
101
103
] ) ?;
102
104
} else {
103
105
return Err ( CTokenError :: InvalidTokenDataVersion ) ;
104
106
}
105
107
}
106
- if self . version == 0 {
108
+ if $ self. version == 0 {
107
109
Ok ( Poseidon :: hashv( & [
108
110
mint_hash. as_slice( ) ,
109
111
extension_hashchain. as_slice( ) ,
110
112
] ) ?)
111
- } else if self . version == 1 {
113
+ } else if $ self. version == 1 {
112
114
Ok ( Sha256BE :: hashv( & [
113
115
mint_hash. as_slice( ) ,
114
116
extension_hashchain. as_slice( ) ,
@@ -119,6 +121,20 @@ impl CompressedMint {
119
121
} else {
120
122
Ok ( mint_hash)
121
123
}
124
+ } } ;
125
+ }
126
+
127
+ // TODO: unify code if possible
128
+ // use nested token metadata layout for data extension
129
+ impl CompressedMint {
130
+ #[ allow( dead_code) ]
131
+ pub fn hash ( & self ) -> Result < [ u8 ; 32 ] , CTokenError > {
132
+ let mut hash_cache = HashCache :: new ( ) ;
133
+ self . hash_with_cache ( & mut hash_cache)
134
+ }
135
+
136
+ pub fn hash_with_cache ( & self , hash_cache : & mut HashCache ) -> Result < [ u8 ; 32 ] , CTokenError > {
137
+ impl_compressed_mint_hash ! ( self , hash_cache, self . is_decompressed)
122
138
}
123
139
124
140
pub fn hash_with_hashed_values (
@@ -163,147 +179,72 @@ impl CompressedMint {
163
179
hashed_freeze_authority : & Option < & [ u8 ; 32 ] > ,
164
180
version : u8 ,
165
181
) -> Result < [ u8 ; 32 ] , HasherError > {
166
- let mut hash_inputs = vec ! [ hashed_spl_mint. as_slice( ) , supply_bytes. as_slice( ) ] ;
182
+ // Note: ArrayVec causes lifetime issues.
183
+ let mut hash_inputs: [ & [ u8 ] ; 8 ] = [ & [ ] ; 8 ] ;
184
+
185
+ hash_inputs[ 0 ] = hashed_spl_mint. as_slice ( ) ;
186
+ hash_inputs[ 1 ] = supply_bytes. as_slice ( ) ;
187
+ let mut input_count = 2 ;
167
188
168
189
// Add decimals with prefix if not 0
169
190
let mut decimals_bytes = [ 0u8 ; 32 ] ;
170
191
if decimals != 0 {
171
192
decimals_bytes[ 30 ] = 1 ; // decimals prefix
172
193
decimals_bytes[ 31 ] = decimals;
173
- hash_inputs. push ( & decimals_bytes[ ..] ) ;
194
+ hash_inputs[ input_count] = & decimals_bytes[ ..] ;
195
+ input_count += 1 ;
174
196
}
175
197
176
198
// Add is_decompressed with prefix if true
177
199
let mut is_decompressed_bytes = [ 0u8 ; 32 ] ;
178
200
if is_decompressed {
179
201
is_decompressed_bytes[ 30 ] = 2 ; // is_decompressed prefix
180
202
is_decompressed_bytes[ 31 ] = 1 ; // true as 1
181
- hash_inputs. push ( & is_decompressed_bytes[ ..] ) ;
203
+ hash_inputs[ input_count] = & is_decompressed_bytes[ ..] ;
204
+ input_count += 1 ;
182
205
}
183
206
184
207
// Add mint authority if present
185
208
if let Some ( hashed_mint_authority) = hashed_mint_authority {
186
- hash_inputs. push ( hashed_mint_authority. as_slice ( ) ) ;
209
+ hash_inputs[ input_count] = hashed_mint_authority. as_slice ( ) ;
210
+ input_count += 1 ;
187
211
}
188
212
189
213
// Add freeze authority if present
190
214
let empty_authority = [ 0u8 ; 32 ] ;
191
215
if let Some ( hashed_freeze_authority) = hashed_freeze_authority {
192
216
// If there is freeze authority but no mint authority, add empty mint authority
193
217
if hashed_mint_authority. is_none ( ) {
194
- hash_inputs. push ( & empty_authority[ ..] ) ;
218
+ hash_inputs[ input_count] = & empty_authority[ ..] ;
219
+ input_count += 1 ;
195
220
}
196
- hash_inputs. push ( hashed_freeze_authority. as_slice ( ) ) ;
221
+ hash_inputs[ input_count] = hashed_freeze_authority. as_slice ( ) ;
222
+ input_count += 1 ;
197
223
}
198
224
199
225
// Add version with prefix if not 0
200
226
let mut num_extensions_bytes = [ 0u8 ; 32 ] ;
201
227
if version != 0 {
202
228
num_extensions_bytes[ 30 ] = 3 ; // version prefix
203
229
num_extensions_bytes[ 31 ] = version;
204
- hash_inputs. push ( & num_extensions_bytes[ ..] ) ;
230
+ hash_inputs[ input_count] = & num_extensions_bytes[ ..] ;
231
+ input_count += 1 ;
205
232
}
206
233
207
- let hash = H :: hashv ( hash_inputs. as_slice ( ) ) ?;
234
+ let hash = H :: hashv ( & hash_inputs[ ..input_count ] ) ?;
208
235
209
236
Ok ( hash)
210
237
}
211
238
}
212
239
213
240
impl ZCompressedMintMut < ' _ > {
214
241
pub fn hash ( & self , hash_cache : & mut HashCache ) -> Result < [ u8 ; 32 ] , CTokenError > {
215
- let hashed_spl_mint = hash_cache. get_or_hash_mint ( & self . spl_mint . into ( ) ) ?;
216
- let mut supply_bytes = [ 0u8 ; 32 ] ;
217
- self . supply
218
- . as_bytes ( )
219
- . iter ( )
220
- . rev ( )
221
- . zip ( supply_bytes[ 24 ..] . iter_mut ( ) )
222
- . for_each ( |( x, y) | * y = * x) ;
223
-
224
- let hashed_mint_authority;
225
- let hashed_mint_authority_option = if let Some ( mint_authority) =
226
- self . mint_authority . as_ref ( )
227
- {
228
- hashed_mint_authority = hash_cache. get_or_hash_pubkey ( & ( * mint_authority) . to_bytes ( ) ) ;
229
- Some ( & hashed_mint_authority)
230
- } else {
231
- None
232
- } ;
233
-
234
- let hashed_freeze_authority;
235
- let hashed_freeze_authority_option =
236
- if let Some ( freeze_authority) = self . freeze_authority . as_ref ( ) {
237
- hashed_freeze_authority =
238
- hash_cache. get_or_hash_pubkey ( & ( * freeze_authority) . to_bytes ( ) ) ;
239
- Some ( & hashed_freeze_authority)
240
- } else {
241
- None
242
- } ;
243
-
244
- let mint_hash = CompressedMint :: hash_with_hashed_values (
245
- & hashed_spl_mint,
246
- & supply_bytes,
247
- self . decimals ,
242
+ impl_compressed_mint_hash ! (
243
+ self ,
244
+ hash_cache,
248
245
self . is_decompressed( ) ,
249
- & hashed_mint_authority_option,
250
- & hashed_freeze_authority_option,
251
- self . version ,
252
- ) ?;
253
-
254
- if let Some ( extensions) = self . extensions . as_ref ( ) {
255
- let mut extension_hashchain = [ 0u8 ; 32 ] ;
256
- for extension in extensions. as_slice ( ) {
257
- let extension_hash = match extension {
258
- ZExtensionStructMut :: TokenMetadata ( token_metadata) => {
259
- if * token_metadata. version == 0 {
260
- extension. hash :: < Poseidon > ( ) ?
261
- } else if * token_metadata. version == 1 {
262
- extension. hash :: < Sha256BE > ( ) ?
263
- } else {
264
- return Err ( CTokenError :: InvalidTokenDataVersion ) ;
265
- }
266
- }
267
- _ => return Err ( CTokenError :: UnsupportedExtension ) ,
268
- } ;
269
-
270
- if self . version == 0 {
271
- extension_hashchain = Poseidon :: hashv ( & [
272
- extension_hashchain. as_slice ( ) ,
273
- extension_hash. as_slice ( ) ,
274
- ] ) ?;
275
- } else if self . version == 1 {
276
- extension_hashchain = Sha256BE :: hashv ( & [
277
- extension_hashchain. as_slice ( ) ,
278
- extension_hash. as_slice ( ) ,
279
- ] ) ?;
280
- } else {
281
- msg ! ( "invalid version " ) ;
282
- return Err ( CTokenError :: InvalidTokenDataVersion ) ;
283
- }
284
- }
285
-
286
- msg ! (
287
- "ZCompressedMintMut extension_hashchain: {:?} " ,
288
- extension_hashchain
289
- ) ;
290
-
291
- if self . version == 0 {
292
- Ok ( Poseidon :: hashv ( & [
293
- mint_hash. as_slice ( ) ,
294
- extension_hashchain. as_slice ( ) ,
295
- ] ) ?)
296
- } else if self . version == 1 {
297
- Ok ( Sha256BE :: hashv ( & [
298
- mint_hash. as_slice ( ) ,
299
- extension_hashchain. as_slice ( ) ,
300
- ] ) ?)
301
- } else {
302
- Err ( CTokenError :: InvalidTokenDataVersion )
303
- }
304
- } else {
305
- Ok ( mint_hash)
306
- }
246
+ *
247
+ )
307
248
}
308
249
}
309
250
0 commit comments