@@ -38,6 +38,8 @@ pub fn spend_cat_coins(
38
38
) -> Result < Vec < CoinSpend > , ToClvmError > {
39
39
let mut total_delta = 0 ;
40
40
41
+ let len = cat_spends. len ( ) ;
42
+
41
43
cat_spends
42
44
. iter ( )
43
45
. enumerate ( )
@@ -61,8 +63,8 @@ pub fn spend_cat_coins(
61
63
total_delta += delta;
62
64
63
65
// Find information of neighboring coins on the ring.
64
- let prev_cat = & cat_spends[ index. wrapping_sub ( 1 ) % cat_spends . len ( ) ] ;
65
- let next_cat = & cat_spends[ index. wrapping_add ( 1 ) % cat_spends . len ( ) ] ;
66
+ let prev_cat = & cat_spends[ if index == 0 { len - 1 } else { index - 1 } ] ;
67
+ let next_cat = & cat_spends[ if index == len - 1 { 0 } else { index + 1 } ] ;
66
68
67
69
// Construct the puzzle.
68
70
let puzzle = CurriedProgram {
@@ -112,6 +114,10 @@ pub fn spend_cat_coins(
112
114
113
115
#[ cfg( test) ]
114
116
mod tests {
117
+ use chia:: gen:: {
118
+ conditions:: EmptyVisitor , run_block_generator:: run_block_generator,
119
+ solution_generator:: solution_generator,
120
+ } ;
115
121
use chia_bls:: { derive_keys:: master_to_wallet_unhardened, SecretKey } ;
116
122
use chia_protocol:: Bytes32 ;
117
123
use chia_wallet:: {
@@ -197,4 +203,174 @@ mod tests {
197
203
) ;
198
204
assert_eq ! ( hex:: encode( actual) , hex:: encode( expected) ) ;
199
205
}
206
+
207
+ #[ test]
208
+ fn test_cat_spend_multi ( ) {
209
+ let synthetic_key =
210
+ master_to_wallet_unhardened ( & SecretKey :: from_seed ( SEED . as_ref ( ) ) . public_key ( ) , 0 )
211
+ . derive_synthetic ( & DEFAULT_HIDDEN_PUZZLE_HASH ) ;
212
+
213
+ let mut a = Allocator :: new ( ) ;
214
+ let standard_puzzle_ptr = node_from_bytes ( & mut a, & STANDARD_PUZZLE ) . unwrap ( ) ;
215
+ let cat_puzzle_ptr = node_from_bytes ( & mut a, & CAT_PUZZLE ) . unwrap ( ) ;
216
+
217
+ let asset_id = [ 42 ; 32 ] ;
218
+
219
+ let p2_puzzle_hash = standard_puzzle_hash ( & synthetic_key) ;
220
+ let cat_puzzle_hash = cat_puzzle_hash ( asset_id, p2_puzzle_hash) ;
221
+
222
+ let parent_coin_1 = Coin :: new ( Bytes32 :: new ( [ 0 ; 32 ] ) , Bytes32 :: new ( cat_puzzle_hash) , 69 ) ;
223
+ let coin_1 = Coin :: new (
224
+ Bytes32 :: from ( parent_coin_1. coin_id ( ) ) ,
225
+ Bytes32 :: new ( cat_puzzle_hash) ,
226
+ 42 ,
227
+ ) ;
228
+
229
+ let parent_coin_2 = Coin :: new ( Bytes32 :: new ( [ 0 ; 32 ] ) , Bytes32 :: new ( cat_puzzle_hash) , 69 ) ;
230
+ let coin_2 = Coin :: new (
231
+ Bytes32 :: from ( parent_coin_2. coin_id ( ) ) ,
232
+ Bytes32 :: new ( cat_puzzle_hash) ,
233
+ 34 ,
234
+ ) ;
235
+
236
+ let parent_coin_3 = Coin :: new ( Bytes32 :: new ( [ 0 ; 32 ] ) , Bytes32 :: new ( cat_puzzle_hash) , 69 ) ;
237
+ let coin_3 = Coin :: new (
238
+ Bytes32 :: from ( parent_coin_3. coin_id ( ) ) ,
239
+ Bytes32 :: new ( cat_puzzle_hash) ,
240
+ 69 ,
241
+ ) ;
242
+
243
+ let conditions = vec ! [ CatCondition :: Normal ( Condition :: CreateCoin (
244
+ CreateCoin :: Normal {
245
+ puzzle_hash: coin_1. puzzle_hash,
246
+ amount: coin_1. amount + coin_2. amount + coin_3. amount,
247
+ } ,
248
+ ) ) ] ;
249
+
250
+ let coin_spends = spend_cat_coins (
251
+ & mut a,
252
+ standard_puzzle_ptr,
253
+ cat_puzzle_ptr,
254
+ asset_id,
255
+ & [
256
+ CatSpend {
257
+ coin : coin_1,
258
+ synthetic_key : synthetic_key. clone ( ) ,
259
+ conditions,
260
+ extra_delta : 0 ,
261
+ lineage_proof : LineageProof {
262
+ parent_coin_info : parent_coin_1. parent_coin_info ,
263
+ inner_puzzle_hash : p2_puzzle_hash. into ( ) ,
264
+ amount : parent_coin_1. amount ,
265
+ } ,
266
+ p2_puzzle_hash,
267
+ } ,
268
+ CatSpend {
269
+ coin : coin_2,
270
+ synthetic_key : synthetic_key. clone ( ) ,
271
+ conditions : Vec :: new ( ) ,
272
+ extra_delta : 0 ,
273
+ lineage_proof : LineageProof {
274
+ parent_coin_info : parent_coin_2. parent_coin_info ,
275
+ inner_puzzle_hash : p2_puzzle_hash. into ( ) ,
276
+ amount : parent_coin_2. amount ,
277
+ } ,
278
+ p2_puzzle_hash,
279
+ } ,
280
+ CatSpend {
281
+ coin : coin_3,
282
+ synthetic_key,
283
+ conditions : Vec :: new ( ) ,
284
+ extra_delta : 0 ,
285
+ lineage_proof : LineageProof {
286
+ parent_coin_info : parent_coin_3. parent_coin_info ,
287
+ inner_puzzle_hash : p2_puzzle_hash. into ( ) ,
288
+ amount : parent_coin_3. amount ,
289
+ } ,
290
+ p2_puzzle_hash,
291
+ } ,
292
+ ] ,
293
+ )
294
+ . unwrap ( ) ;
295
+
296
+ let spend_vec = coin_spends
297
+ . clone ( )
298
+ . into_iter ( )
299
+ . map ( |coin_spend| {
300
+ (
301
+ coin_spend. coin ,
302
+ coin_spend. puzzle_reveal ,
303
+ coin_spend. solution ,
304
+ )
305
+ } )
306
+ . collect :: < Vec < _ > > ( ) ;
307
+ let gen = solution_generator ( spend_vec) . unwrap ( ) ;
308
+ let block =
309
+ run_block_generator :: < Program , EmptyVisitor > ( & mut a, & gen, & [ ] , u64:: MAX , 0 ) . unwrap ( ) ;
310
+
311
+ assert_eq ! ( block. cost, 101289468 ) ;
312
+
313
+ assert_eq ! ( coin_spends. len( ) , 3 ) ;
314
+
315
+ let output_ptr_1 = coin_spends[ 0 ]
316
+ . puzzle_reveal
317
+ . run ( & mut a, 0 , u64:: MAX , & coin_spends[ 0 ] . solution )
318
+ . unwrap ( )
319
+ . 1 ;
320
+ let actual = node_to_bytes ( & a, output_ptr_1) . unwrap ( ) ;
321
+
322
+ let expected = hex ! (
323
+ "
324
+ ffff46ffa06438c882c2db9f5c2a8b4cbda9258c40a6583b2d7c6becc1678607
325
+ 4d558c834980ffff3cffa1cb1cb6597fe61e67a6cbbcd4e8f0bda5e9fc56cd84
326
+ c9e9502772b410dc8a03207680ffff3dffa0742ddb368882193072ea013bde24
327
+ 4a5c9d40ab4454c09666e84777a79307e17a80ffff32ffb08584adae5630842a
328
+ 1766bc444d2b872dd3080f4e5daaecf6f762a4be7dc148f37868149d4217f3dc
329
+ c9183fe61e48d8bfffa004c476adfcffeacfef7c979bdd03b4641f1870d3f81b
330
+ 20636eefbcf879bb64ec80ffff33ffa0f9f2d59294f2aae8f9833db876d1bf43
331
+ 95d46af18c17312041c6f4a4d73fa041ff8200918080
332
+ "
333
+ ) ;
334
+ assert_eq ! ( hex:: encode( actual) , hex:: encode( expected) ) ;
335
+
336
+ let output_ptr_2 = coin_spends[ 1 ]
337
+ . puzzle_reveal
338
+ . run ( & mut a, 0 , u64:: MAX , & coin_spends[ 1 ] . solution )
339
+ . unwrap ( )
340
+ . 1 ;
341
+ let actual = node_to_bytes ( & a, output_ptr_2) . unwrap ( ) ;
342
+
343
+ let expected = hex ! (
344
+ "
345
+ ffff46ffa0ae60b8db0664959078a1c6e51ca6a8fc55207c63a8ac74d026f1d9
346
+ 15c406bac480ffff3cffa1cb9a41843ab318a8336f61a6bf9e8b0b1d555b9f07
347
+ cd19582e0bc52a961c65dc9e80ffff3dffa0294cda8d35164e01c4e3b7c07c36
348
+ a5bb2f38a23e93ef49c882ee74349a0df8bd80ffff32ffb08584adae5630842a
349
+ 1766bc444d2b872dd3080f4e5daaecf6f762a4be7dc148f37868149d4217f3dc
350
+ c9183fe61e48d8bfffa0ba4484b961b7a2369d948d06c55b64bdbfaffb326bc1
351
+ 3b490ab1215dd33d8d468080
352
+ "
353
+ ) ;
354
+ assert_eq ! ( hex:: encode( actual) , hex:: encode( expected) ) ;
355
+
356
+ let output_ptr_3 = coin_spends[ 2 ]
357
+ . puzzle_reveal
358
+ . run ( & mut a, 0 , u64:: MAX , & coin_spends[ 2 ] . solution )
359
+ . unwrap ( )
360
+ . 1 ;
361
+ let actual = node_to_bytes ( & a, output_ptr_3) . unwrap ( ) ;
362
+
363
+ let expected = hex ! (
364
+ "
365
+ ffff46ffa0f8eacbef2bad0c7b27b638a90a37244e75013e977f250230856d05
366
+ a2784e1d0980ffff3cffa1cb17c47c5fa8d795efa0d9227d2066cde36dd4e845
367
+ 7e8f4e507d2015a1c7f3d94b80ffff3dffa0629abc502829339c7880ee003c4e
368
+ 68a8181d71206e50e7b36c29301ef60128f580ffff32ffb08584adae5630842a
369
+ 1766bc444d2b872dd3080f4e5daaecf6f762a4be7dc148f37868149d4217f3dc
370
+ c9183fe61e48d8bfffa0ba4484b961b7a2369d948d06c55b64bdbfaffb326bc1
371
+ 3b490ab1215dd33d8d468080
372
+ "
373
+ ) ;
374
+ assert_eq ! ( hex:: encode( actual) , hex:: encode( expected) ) ;
375
+ }
200
376
}
0 commit comments