2
2
// SPDX-License-Identifier: Apache-2.0, MIT
3
3
4
4
use std:: cmp:: min;
5
- use std:: collections:: { BTreeMap , BTreeSet } ;
5
+ use std:: collections:: { BTreeMap , BTreeSet , HashSet } ;
6
6
7
7
use cid:: multihash:: { Code , MultihashDigest , MultihashGeneric } ;
8
8
use cid:: Cid ;
9
- use fil_actors_runtime:: { extract_send_result, FIRST_ACTOR_SPECIFIC_EXIT_CODE } ;
9
+ use fil_actors_runtime:: { extract_send_result, BatchReturnGen , FIRST_ACTOR_SPECIFIC_EXIT_CODE } ;
10
10
use frc46_token:: token:: types:: { BalanceReturn , TransferFromParams , TransferFromReturn } ;
11
11
use fvm_ipld_bitfield:: BitField ;
12
12
use fvm_ipld_blockstore:: Blockstore ;
@@ -74,7 +74,7 @@ pub enum Method {
74
74
WithdrawBalance = 3 ,
75
75
PublishStorageDeals = 4 ,
76
76
VerifyDealsForActivation = 5 ,
77
- ActivateDeals = 6 ,
77
+ BatchActivateDeals = 6 ,
78
78
OnMinerSectorsTerminate = 7 ,
79
79
ComputeDataCommitment = 8 ,
80
80
CronTick = 9 ,
@@ -530,93 +530,145 @@ impl Actor {
530
530
Ok ( VerifyDealsForActivationReturn { sectors : sectors_data } )
531
531
}
532
532
533
- /// Activate a set of deals, returning the combined deal space and extra info for verified deals.
534
- fn activate_deals (
533
+ /// Activate a set of deals, returning the deal space and extra info for sectors containing
534
+ /// verified deals. Sectors are activated in parameter-defined order and can fail independently of
535
+ /// each other with the responsible ExitCode recorded in a BatchReturn.
536
+ fn batch_activate_deals (
535
537
rt : & impl Runtime ,
536
- params : ActivateDealsParams ,
537
- ) -> Result < ActivateDealsResult , ActorError > {
538
+ params : BatchActivateDealsParams ,
539
+ ) -> Result < BatchActivateDealsResult , ActorError > {
538
540
rt. validate_immediate_caller_type ( std:: iter:: once ( & Type :: Miner ) ) ?;
539
541
let miner_addr = rt. message ( ) . caller ( ) ;
540
542
let curr_epoch = rt. curr_epoch ( ) ;
541
543
542
- let ( deal_spaces, verified_infos) = rt. transaction ( |st : & mut State , rt| {
543
- let proposal_array = st. get_proposal_array ( rt. store ( ) ) ?;
544
- let proposals = get_proposals ( & proposal_array, & params. deal_ids , st. next_id ) ?;
544
+ let ( activations, batch_ret) = rt. transaction ( |st : & mut State , rt| {
545
+ let mut deal_states: Vec < ( DealID , DealState ) > = vec ! [ ] ;
546
+ let mut batch_gen = BatchReturnGen :: new ( params. sectors . len ( ) ) ;
547
+ let mut activations: Vec < DealActivation > = vec ! [ ] ;
548
+ let mut activated_deals: HashSet < DealID > = HashSet :: new ( ) ;
549
+
550
+ for p in params. sectors {
551
+ let proposal_array = st. get_proposal_array ( rt. store ( ) ) ?;
552
+
553
+ if p. deal_ids . iter ( ) . any ( |id| activated_deals. contains ( id) ) {
554
+ log:: warn!(
555
+ "failed to activate sector containing duplicate deals {:?}" ,
556
+ p. deal_ids
557
+ ) ;
558
+ batch_gen. add_fail ( ExitCode :: USR_ILLEGAL_ARGUMENT ) ;
559
+ continue ;
560
+ }
545
561
546
- let deal_spaces = {
547
- validate_and_return_deal_space (
562
+ let proposals = match get_proposals ( & proposal_array, & p. deal_ids , st. next_id ) {
563
+ Ok ( proposals) => proposals,
564
+ Err ( e) => {
565
+ log:: warn!( "failed to get proposals for deals {:?}: {:?}" , p. deal_ids, e) ;
566
+ batch_gen. add_fail ( e. exit_code ( ) ) ;
567
+ continue ;
568
+ }
569
+ } ;
570
+
571
+ let deal_spaces = match validate_and_return_deal_space (
548
572
& proposals,
549
573
& miner_addr,
550
- params . sector_expiry ,
574
+ p . sector_expiry ,
551
575
curr_epoch,
552
576
None ,
553
- )
554
- . context ( "failed to validate deal proposals for activation" ) ?
555
- } ;
577
+ ) {
578
+ Ok ( ds) => ds,
579
+ Err ( e) => {
580
+ log:: warn!( "failed validate deals {:?}: {}" , p. deal_ids, e) ;
581
+ batch_gen. add_fail ( e. exit_code ( ) ) ;
582
+ continue ;
583
+ }
584
+ } ;
556
585
557
- // Update deal states
558
- let mut verified_infos = Vec :: new ( ) ;
559
- let mut deal_states: Vec < ( DealID , DealState ) > = vec ! [ ] ;
586
+ // Update deal states
587
+ let mut verified_infos = Vec :: new ( ) ;
560
588
561
- for ( deal_id, proposal) in proposals {
562
589
// This construction could be replaced with a single "update deal state"
563
590
// state method, possibly batched over all deal ids at once.
564
- let s = st. find_deal_state ( rt. store ( ) , deal_id) ?;
591
+ let update_result: Result < ( ) , ActorError > =
592
+ proposals. into_iter ( ) . try_for_each ( |( deal_id, proposal) | {
593
+ let s = st
594
+ . find_deal_state ( rt. store ( ) , deal_id)
595
+ . context ( format ! ( "error looking up deal state for {}" , deal_id) ) ?;
565
596
566
- if s. is_some ( ) {
567
- return Err ( actor_error ! (
568
- illegal_argument,
569
- "deal {} already activated" ,
570
- deal_id
571
- ) ) ;
572
- }
597
+ if s. is_some ( ) {
598
+ return Err ( actor_error ! (
599
+ illegal_argument,
600
+ "deal {} already activated" ,
601
+ deal_id
602
+ ) ) ;
603
+ }
573
604
574
- let propc = rt_deal_cid ( rt, & proposal) ?;
605
+ let propc = rt_deal_cid ( rt, & proposal) ?;
575
606
576
- // Confirm the deal is in the pending proposals queue.
577
- // It will be removed from this queue later, during cron.
578
- let has = st. has_pending_deal ( rt. store ( ) , propc) ?;
607
+ // Confirm the deal is in the pending proposals queue.
608
+ // It will be removed from this queue later, during cron.
609
+ let has = st. has_pending_deal ( rt. store ( ) , propc) ?;
579
610
580
- if !has {
581
- return Err ( actor_error ! (
582
- illegal_state,
583
- "tried to activate deal that was not in the pending set ({})" ,
584
- propc
585
- ) ) ;
586
- }
611
+ if !has {
612
+ return Err ( actor_error ! (
613
+ illegal_state,
614
+ "tried to activate deal that was not in the pending set ({})" ,
615
+ propc
616
+ ) ) ;
617
+ }
587
618
588
- // Extract and remove any verified allocation ID for the pending deal.
589
- let allocation = st
590
- . remove_pending_deal_allocation_id ( rt. store ( ) , & deal_id_key ( deal_id) ) ?
591
- . unwrap_or ( ( BytesKey ( vec ! [ ] ) , NO_ALLOCATION_ID ) )
592
- . 1 ;
593
-
594
- if allocation != NO_ALLOCATION_ID {
595
- verified_infos. push ( VerifiedDealInfo {
596
- client : proposal. client . id ( ) . unwrap ( ) ,
597
- allocation_id : allocation,
598
- data : proposal. piece_cid ,
599
- size : proposal. piece_size ,
600
- } )
601
- }
619
+ // Extract and remove any verified allocation ID for the pending deal.
620
+ let allocation = st
621
+ . remove_pending_deal_allocation_id ( rt. store ( ) , & deal_id_key ( deal_id) )
622
+ . context ( format ! (
623
+ "failed to remove pending deal allocation id {}" ,
624
+ deal_id
625
+ ) ) ?
626
+ . unwrap_or ( ( BytesKey ( vec ! [ ] ) , NO_ALLOCATION_ID ) )
627
+ . 1 ;
628
+
629
+ if allocation != NO_ALLOCATION_ID {
630
+ verified_infos. push ( VerifiedDealInfo {
631
+ client : proposal. client . id ( ) . unwrap ( ) ,
632
+ allocation_id : allocation,
633
+ data : proposal. piece_cid ,
634
+ size : proposal. piece_size ,
635
+ } )
636
+ }
602
637
603
- deal_states. push ( (
604
- deal_id,
605
- DealState {
606
- sector_start_epoch : curr_epoch,
607
- last_updated_epoch : EPOCH_UNDEFINED ,
608
- slash_epoch : EPOCH_UNDEFINED ,
609
- verified_claim : allocation,
610
- } ,
611
- ) ) ;
638
+ deal_states. push ( (
639
+ deal_id,
640
+ DealState {
641
+ sector_start_epoch : curr_epoch,
642
+ last_updated_epoch : EPOCH_UNDEFINED ,
643
+ slash_epoch : EPOCH_UNDEFINED ,
644
+ verified_claim : allocation,
645
+ } ,
646
+ ) ) ;
647
+ activated_deals. insert ( deal_id) ;
648
+ Ok ( ( ) )
649
+ } ) ;
650
+
651
+ match update_result {
652
+ Ok ( _) => {
653
+ activations. push ( DealActivation {
654
+ nonverified_deal_space : deal_spaces. deal_space ,
655
+ verified_infos,
656
+ } ) ;
657
+ batch_gen. add_success ( ) ;
658
+ }
659
+ Err ( e) => {
660
+ log:: warn!( "failed to activate deals {:?}: {}" , p. deal_ids, e) ;
661
+ batch_gen. add_fail ( e. exit_code ( ) ) ;
662
+ }
663
+ }
612
664
}
613
665
614
666
st. put_deal_states ( rt. store ( ) , & deal_states) ?;
615
667
616
- Ok ( ( deal_spaces , verified_infos ) )
668
+ Ok ( ( activations , batch_gen . gen ( ) ) )
617
669
} ) ?;
618
670
619
- Ok ( ActivateDealsResult { nonverified_deal_space : deal_spaces . deal_space , verified_infos } )
671
+ Ok ( BatchActivateDealsResult { activations , activation_results : batch_ret } )
620
672
}
621
673
622
674
/// Terminate a set of deals in response to their containing sector being terminated.
@@ -634,7 +686,6 @@ impl Actor {
634
686
635
687
for id in params. deal_ids {
636
688
let deal = st. find_proposal ( rt. store ( ) , id) ?;
637
-
638
689
// The deal may have expired and been deleted before the sector is terminated.
639
690
// Nothing to do, but continue execution for the other deals.
640
691
if deal. is_none ( ) {
@@ -1403,7 +1454,7 @@ impl ActorCode for Actor {
1403
1454
WithdrawBalance |WithdrawBalanceExported => withdraw_balance,
1404
1455
PublishStorageDeals |PublishStorageDealsExported => publish_storage_deals,
1405
1456
VerifyDealsForActivation => verify_deals_for_activation,
1406
- ActivateDeals => activate_deals ,
1457
+ BatchActivateDeals => batch_activate_deals ,
1407
1458
OnMinerSectorsTerminate => on_miner_sectors_terminate,
1408
1459
ComputeDataCommitment => compute_data_commitment,
1409
1460
CronTick => cron_tick,
0 commit comments