@@ -11,6 +11,7 @@ use mesh_apis::local_staking_api::{
11
11
LocalStakingApiHelper , LocalStakingApiQueryMsg , MaxSlashResponse ,
12
12
} ;
13
13
use mesh_apis:: vault_api:: { self , VaultApi } ;
14
+ use mesh_sync:: Lockable ;
14
15
use sylvia:: types:: { ExecCtx , InstantiateCtx , QueryCtx , ReplyCtx } ;
15
16
use sylvia:: { contract, schemars} ;
16
17
@@ -48,7 +49,7 @@ pub struct VaultContract<'a> {
48
49
/// All liens in the protocol
49
50
///
50
51
/// Liens are indexed with (user, creditor), as this pair has to be unique
51
- pub liens : Map < ' a , ( & ' a Addr , & ' a Addr ) , Lien > ,
52
+ pub liens : Map < ' a , ( & ' a Addr , & ' a Addr ) , Lockable < Lien > > ,
52
53
/// Per-user information
53
54
pub users : Map < ' a , & ' a Addr , UserInfo > ,
54
55
/// Pending txs information
@@ -180,7 +181,7 @@ impl VaultContract<'_> {
180
181
let contract = CrossStakingApiHelper ( contract) ;
181
182
let slashable = contract. max_slash ( ctx. deps . as_ref ( ) ) ?;
182
183
183
- let tx_id = self . maybe_stake (
184
+ let tx_id = self . stake_tx (
184
185
& mut ctx,
185
186
& config,
186
187
& contract. 0 ,
@@ -288,8 +289,10 @@ impl VaultContract<'_> {
288
289
let lienholder = ctx. deps . api . addr_validate ( & lienholder) ?;
289
290
290
291
self . liens
291
- . load ( ctx. deps . storage , ( & account, & lienholder) )
292
+ . load ( ctx. deps . storage , ( & account, & lienholder) ) ?
293
+ . read ( )
292
294
. map_err ( Into :: into)
295
+ . cloned ( )
293
296
}
294
297
295
298
/// Returns paginated claims list for an user
@@ -312,8 +315,10 @@ impl VaultContract<'_> {
312
315
. liens
313
316
. prefix ( & account)
314
317
. range ( ctx. deps . storage , bound, None , Order :: Ascending )
315
- . map ( |lien| {
316
- lien. map ( |( lienholder, lien) | LienInfo {
318
+ . map ( |item| {
319
+ let ( lienholder, lien) = item?;
320
+ let lien = lien. read ( ) ?;
321
+ Ok :: < LienInfo , ContractError > ( LienInfo {
317
322
lienholder : lienholder. into ( ) ,
318
323
amount : lien. amount ,
319
324
} )
@@ -428,10 +433,11 @@ impl VaultContract<'_> {
428
433
let mut lien = self
429
434
. liens
430
435
. may_load ( ctx. deps . storage , ( & ctx. info . sender , lienholder) ) ?
431
- . unwrap_or ( Lien {
436
+ . unwrap_or ( Lockable :: new ( Lien {
432
437
amount : Uint128 :: zero ( ) ,
433
438
slashable,
434
- } ) ;
439
+ } ) ) ;
440
+ let lien = lien. write ( ) ?;
435
441
lien. amount += amount;
436
442
437
443
let mut user = self
@@ -443,8 +449,11 @@ impl VaultContract<'_> {
443
449
444
450
ensure ! ( user. verify_collateral( ) , ContractError :: InsufficentBalance ) ;
445
451
446
- self . liens
447
- . save ( ctx. deps . storage , ( & ctx. info . sender , lienholder) , & lien) ?;
452
+ self . liens . save (
453
+ ctx. deps . storage ,
454
+ ( & ctx. info . sender , lienholder) ,
455
+ & Lockable :: new ( lien. clone ( ) ) ,
456
+ ) ?;
448
457
449
458
self . users . save ( ctx. deps . storage , & ctx. info . sender , & user) ?;
450
459
@@ -458,7 +467,7 @@ impl VaultContract<'_> {
458
467
///
459
468
/// Config is taken in argument as it sometimes is used outside of this function, so
460
469
/// we want to avoid double-fetching it
461
- fn maybe_stake (
470
+ fn stake_tx (
462
471
& self ,
463
472
ctx : & mut ExecCtx ,
464
473
config : & Config ,
@@ -471,24 +480,21 @@ impl VaultContract<'_> {
471
480
ContractError :: UnexpectedDenom ( config. denom. clone( ) )
472
481
) ;
473
482
474
- // Check that there are no pending txs for this user
475
- let txs = self
476
- . pending
477
- . txs_by_user ( ctx. deps . storage , & ctx. info . sender ) ?;
478
- ensure ! ( txs. is_empty( ) , ContractError :: PendingTx ( txs[ 0 ] . id) ) ;
479
-
480
483
// Tx starts here
481
484
let amount = amount. amount ;
482
- // Load user and update (but do not save) max lien and total slashable
483
- let mut lien = self
485
+ // Load user and update max lien and total slashable
486
+ // Write lock lien
487
+ let mut lien_lock = self
484
488
. liens
485
489
. may_load ( ctx. deps . storage , ( & ctx. info . sender , lienholder) ) ?
486
- . unwrap_or ( Lien {
490
+ . unwrap_or ( Lockable :: new ( Lien {
487
491
amount : Uint128 :: zero ( ) ,
488
492
slashable,
489
- } ) ;
493
+ } ) ) ;
494
+ let lien = lien_lock. write ( ) ?;
490
495
lien. amount += amount;
491
496
497
+ // TODO?: Lockable
492
498
let mut user = self
493
499
. users
494
500
. may_load ( ctx. deps . storage , & ctx. info . sender ) ?
@@ -498,7 +504,14 @@ impl VaultContract<'_> {
498
504
499
505
ensure ! ( user. verify_collateral( ) , ContractError :: InsufficentBalance ) ;
500
506
501
- // Passed. Create new tx
507
+ // Write lock it
508
+ lien_lock. lock_write ( ) ?;
509
+ self . liens
510
+ . save ( ctx. deps . storage , ( & ctx. info . sender , lienholder) , & lien_lock) ?;
511
+
512
+ self . users . save ( ctx. deps . storage , & ctx. info . sender , & user) ?;
513
+
514
+ // Create new tx
502
515
let tx_id = self . next_tx_id ( ctx. deps . storage ) ?;
503
516
504
517
let new_tx = Tx {
@@ -529,28 +542,19 @@ impl VaultContract<'_> {
529
542
) ;
530
543
531
544
// Load lien
532
- let mut lien = self
545
+ let mut lien_lock = self
533
546
. liens
534
- . may_load ( ctx. deps . storage , ( & tx. user , & tx. lienholder ) ) ?
535
- . unwrap_or ( Lien {
536
- amount : Uint128 :: zero ( ) ,
537
- slashable : tx. slashable ,
538
- } ) ;
539
- lien. amount += tx. amount ;
540
-
541
- let mut user = self
542
- . users
543
- . may_load ( ctx. deps . storage , & tx. user ) ?
544
- . unwrap_or_default ( ) ;
545
- user. max_lien = user. max_lien . max ( lien. amount ) ;
546
- user. total_slashable += tx. amount * lien. slashable ;
547
-
548
- self . liens
549
- . save ( ctx. deps . storage , ( & ctx. info . sender , & tx. lienholder ) , & lien) ?;
550
-
551
- self . users . save ( ctx. deps . storage , & ctx. info . sender , & user) ?;
547
+ . load ( ctx. deps . storage , ( & tx. user , & tx. lienholder ) ) ?;
548
+ // Unlock it
549
+ lien_lock. unlock_write ( ) ?;
550
+ // Save it
551
+ self . liens . save (
552
+ ctx. deps . storage ,
553
+ ( & ctx. info . sender , & tx. lienholder ) ,
554
+ & lien_lock,
555
+ ) ?;
552
556
553
- // And remove tx
557
+ // Remove tx
554
558
self . pending . txs . remove ( ctx. deps . storage , tx_id) ?;
555
559
556
560
Ok ( ( ) )
@@ -570,7 +574,43 @@ impl VaultContract<'_> {
570
574
ContractError :: WrongContractTx ( tx_id, ctx. info. sender. clone( ) )
571
575
) ;
572
576
573
- // Just remove tx
577
+ // Load lien
578
+ let mut lien_lock = self
579
+ . liens
580
+ . load ( ctx. deps . storage , ( & tx. user , & tx. lienholder ) ) ?;
581
+ // Rollback amount (need to unlock it first)
582
+ lien_lock. unlock_write ( ) ?;
583
+ let lien = lien_lock. write ( ) ?;
584
+ lien. amount -= tx. amount ;
585
+ // Unlock lien
586
+ lien_lock. unlock_write ( ) ?;
587
+ // Save it unlocked
588
+ self . liens . save (
589
+ ctx. deps . storage ,
590
+ ( & ctx. info . sender , & tx. lienholder ) ,
591
+ & lien_lock,
592
+ ) ?;
593
+
594
+ // Rollback user's max_lien
595
+ let mut user = self . users . load ( ctx. deps . storage , & tx. user ) ?;
596
+
597
+ // Max lien has to be recalculated from scratch; the just rolled back lien
598
+ // is already written to storage
599
+ user. max_lien = self
600
+ . liens
601
+ . prefix ( & tx. user )
602
+ . range ( ctx. deps . storage , None , None , Order :: Ascending )
603
+ . try_fold ( Uint128 :: zero ( ) , |max_lien, item| {
604
+ let ( _, lien_lock) = item?;
605
+ // Shouldn't fail, because unlocked already
606
+ let lien = lien_lock. read ( ) . map_err ( Into :: < ContractError > :: into) ;
607
+ lien. map ( |lien| max_lien. max ( lien. amount ) )
608
+ } ) ?;
609
+
610
+ user. total_slashable -= tx. amount * tx. slashable ;
611
+ self . users . save ( ctx. deps . storage , & tx. user , & user) ?;
612
+
613
+ // Remove tx
574
614
self . pending . txs . remove ( ctx. deps . storage , tx_id) ?;
575
615
576
616
Ok ( ( ) )
@@ -586,30 +626,36 @@ impl VaultContract<'_> {
586
626
let amount = amount. amount ;
587
627
588
628
let owner = Addr :: unchecked ( owner) ;
589
- let mut lien = self
629
+ // TODO: Txs
630
+ let mut lien_lock = self
590
631
. liens
591
632
. may_load ( ctx. deps . storage , ( & owner, & ctx. info . sender ) ) ?
592
633
. ok_or ( ContractError :: UnknownLienholder ) ?;
634
+ let lien = lien_lock. write ( ) ?;
593
635
594
636
ensure ! ( lien. amount >= amount, ContractError :: InsufficientLien ) ;
637
+ let slashable = lien. slashable ;
595
638
lien. amount -= amount;
596
639
597
640
self . liens
598
- . save ( ctx. deps . storage , ( & owner, & ctx. info . sender ) , & lien ) ?;
641
+ . save ( ctx. deps . storage , ( & owner, & ctx. info . sender ) , & lien_lock ) ?;
599
642
600
643
let mut user = self . users . load ( ctx. deps . storage , & owner) ?;
601
644
602
- // Max lien has to be recalculated from scratch; the just released lien
645
+ // Max lien has to be recalculated from scratch; the just saved lien
603
646
// is already written to storage
604
647
user. max_lien = self
605
648
. liens
606
649
. prefix ( & owner)
607
650
. range ( ctx. deps . storage , None , None , Order :: Ascending )
608
- . try_fold ( Uint128 :: zero ( ) , |max_lien, lien| {
609
- lien. map ( |( _, lien) | max_lien. max ( lien. amount ) )
651
+ . try_fold ( Uint128 :: zero ( ) , |max_lien, item| {
652
+ let ( _, lien_lock) = item?;
653
+ // FIXME: Fails as write locked
654
+ let lien = lien_lock. read ( ) . map_err ( Into :: < ContractError > :: into) ;
655
+ lien. map ( |lien| max_lien. max ( lien. amount ) )
610
656
} ) ?;
611
657
612
- user. total_slashable -= amount * lien . slashable ;
658
+ user. total_slashable -= amount * slashable;
613
659
self . users . save ( ctx. deps . storage , & owner, & user) ?;
614
660
615
661
Ok ( ( ) )
0 commit comments