diff --git a/bindings/encoding/struct.go b/bindings/encoding/struct.go index 7bf9aa659..86c52f730 100644 --- a/bindings/encoding/struct.go +++ b/bindings/encoding/struct.go @@ -9,11 +9,12 @@ import ( ) // Tier IDs defined in protocol. -const ( - TierOptimisticID uint16 = 100 - TierSgxID uint16 = 200 - TierPseZkevmID uint16 = 300 - TierGuardianID uint16 = 1000 +var ( + TierOptimisticID uint16 = 100 + TierSgxID uint16 = 200 + TierPseZkevmID uint16 = 300 + TierGuardianID uint16 = 1000 + ProtocolTiers []uint16 = []uint16{TierOptimisticID, TierSgxID, TierPseZkevmID, TierGuardianID} ) // BlockHeader represents an Ethereum block header. diff --git a/cmd/flags/prover.go b/cmd/flags/prover.go index 19c964051..df8fe0d4e 100644 --- a/cmd/flags/prover.go +++ b/cmd/flags/prover.go @@ -96,6 +96,12 @@ var ( Category: proverCategory, Value: false, } + ContestControversialProofs = &cli.BoolFlag{ + Name: "prover.contestControversialProofs", + Usage: "Whether you want to contest proofs with different L2 hashes with higher tier proofs", + Category: proverCategory, + Value: false, + } ProveBlockTxGasLimit = &cli.Uint64Flag{ Name: "prover.proveBlockTxGasLimit", Usage: "Gas limit will be used for TaikoL1.proveBlock transactions", @@ -161,6 +167,7 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{ ProveBlockMaxTxGasTipCap, Graffiti, ProveUnassignedBlocks, + ContestControversialProofs, ProveBlockTxGasLimit, ProverHTTPServerPort, ProverCapacity, diff --git a/prover/config.go b/prover/config.go index c686305dc..bf9733ea7 100644 --- a/prover/config.go +++ b/prover/config.go @@ -35,6 +35,7 @@ type Config struct { BackOffMaxRetrys uint64 BackOffRetryInterval time.Duration ProveUnassignedBlocks bool + ContestControversialProofs bool RPCTimeout *time.Duration WaitReceiptTimeout time.Duration ProveBlockGasLimit *uint64 @@ -122,6 +123,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { BackOffMaxRetrys: c.Uint64(flags.BackOffMaxRetrys.Name), BackOffRetryInterval: c.Duration(flags.BackOffRetryInterval.Name), ProveUnassignedBlocks: c.Bool(flags.ProveUnassignedBlocks.Name), + ContestControversialProofs: c.Bool(flags.ContestControversialProofs.Name), RPCTimeout: timeout, WaitReceiptTimeout: c.Duration(flags.WaitReceiptTimeout.Name), ProveBlockGasLimit: proveBlockTxGasLimit, diff --git a/prover/proof_producer/dummy_producer.go b/prover/proof_producer/dummy_producer.go new file mode 100644 index 000000000..59de6be52 --- /dev/null +++ b/prover/proof_producer/dummy_producer.go @@ -0,0 +1,35 @@ +package producer + +import ( + "bytes" + "context" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/taikoxyz/taiko-client/bindings" +) + +// OptimisticProofProducer always returns a dummy proof. +type DummyProofProducer struct{} + +// RequestProof returns a dummy proof to the result channel. +func (o *DummyProofProducer) RequestProof( + ctx context.Context, + opts *ProofRequestOptions, + blockID *big.Int, + meta *bindings.TaikoDataBlockMetadata, + header *types.Header, + tier uint16, + resultCh chan *ProofWithHeader, +) error { + resultCh <- &ProofWithHeader{ + BlockID: blockID, + Meta: meta, + Header: header, + Proof: bytes.Repeat([]byte{0xff}, 100), + Degree: CircuitsIdx, + Opts: opts, + } + + return nil +} diff --git a/prover/proof_producer/guardian_producer.go b/prover/proof_producer/guardian_producer.go new file mode 100644 index 000000000..451d0681e --- /dev/null +++ b/prover/proof_producer/guardian_producer.go @@ -0,0 +1,44 @@ +package producer + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/bindings/encoding" +) + +// GuardianProofProducer always returns an optimistic (dummy) proof. +type GuardianProofProducer struct{ *DummyProofProducer } + +// RequestProof implements the ProofProducer interface. +func (g *GuardianProofProducer) RequestProof( + ctx context.Context, + opts *ProofRequestOptions, + blockID *big.Int, + meta *bindings.TaikoDataBlockMetadata, + header *types.Header, + resultCh chan *ProofWithHeader, +) error { + log.Info( + "Request guardian proof", + "blockID", blockID, + "coinbase", meta.Coinbase, + "height", header.Number, + "hash", header.Hash(), + ) + + return g.DummyProofProducer.RequestProof(ctx, opts, blockID, meta, header, g.Tier(), resultCh) +} + +// Tier implements the ProofProducer interface. +func (g *GuardianProofProducer) Tier() uint16 { + return encoding.TierGuardianID +} + +// Cancel cancels an existing proof generation. +func (g *GuardianProofProducer) Cancel(ctx context.Context, blockID *big.Int) error { + return nil +} diff --git a/prover/proof_producer/optimistic_producer.go b/prover/proof_producer/optimistic_producer.go index 0fc4d16ae..9ab5dc21d 100644 --- a/prover/proof_producer/optimistic_producer.go +++ b/prover/proof_producer/optimistic_producer.go @@ -1,7 +1,6 @@ package producer import ( - "bytes" "context" "math/big" @@ -12,7 +11,7 @@ import ( ) // OptimisticProofProducer always returns an optimistic (dummy) proof. -type OptimisticProofProducer struct{} +type OptimisticProofProducer struct{ *DummyProofProducer } // RequestProof implements the ProofProducer interface. func (o *OptimisticProofProducer) RequestProof( @@ -31,16 +30,7 @@ func (o *OptimisticProofProducer) RequestProof( "hash", header.Hash(), ) - resultCh <- &ProofWithHeader{ - BlockID: blockID, - Meta: meta, - Header: header, - Proof: bytes.Repeat([]byte{0xff}, 100), - Degree: CircuitsIdx, - Opts: opts, - } - - return nil + return o.DummyProofProducer.RequestProof(ctx, opts, blockID, meta, header, o.Tier(), resultCh) } // Tier implements the ProofProducer interface. diff --git a/prover/proof_producer/proof_producer.go b/prover/proof_producer/proof_producer.go index 63fe522f4..dc58c7a8c 100644 --- a/prover/proof_producer/proof_producer.go +++ b/prover/proof_producer/proof_producer.go @@ -15,7 +15,7 @@ const ( // ProofRequestOptions contains all options that need to be passed to zkEVM rpcd service. type ProofRequestOptions struct { - Height *big.Int // the block number + BlockID *big.Int ProverAddress common.Address ProposeBlockTxHash common.Hash L1SignalService common.Address @@ -38,6 +38,7 @@ type ProofWithHeader struct { Proof []byte Degree uint64 Opts *ProofRequestOptions + Tier uint16 } type ProofProducer interface { diff --git a/prover/proof_producer/sgx_producer.go b/prover/proof_producer/sgx_producer.go new file mode 100644 index 000000000..e70680bba --- /dev/null +++ b/prover/proof_producer/sgx_producer.go @@ -0,0 +1,44 @@ +package producer + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/bindings/encoding" +) + +// SGXProofProducer generates a SGX proof for the given block. +type SGXProofProducer struct{ *DummyProofProducer } + +// RequestProof implements the ProofProducer interface. +func (s *SGXProofProducer) RequestProof( + ctx context.Context, + opts *ProofRequestOptions, + blockID *big.Int, + meta *bindings.TaikoDataBlockMetadata, + header *types.Header, + resultCh chan *ProofWithHeader, +) error { + log.Warn( + "SGX proof producer is unimplemented, will return a dummy proof instead", + "blockID", blockID, + "coinbase", meta.Coinbase, + "height", header.Number, + "hash", header.Hash(), + ) + + return s.DummyProofProducer.RequestProof(ctx, opts, blockID, meta, header, s.Tier(), resultCh) +} + +// Tier implements the ProofProducer interface. +func (s *SGXProofProducer) Tier() uint16 { + return encoding.TierSgxID +} + +// Cancel cancels an existing proof generation. +func (s *SGXProofProducer) Cancel(ctx context.Context, blockID *big.Int) error { + return nil +} diff --git a/prover/proof_producer/zkevm_rpcd_producer.go b/prover/proof_producer/zkevm_rpcd_producer.go index ce6d6b0ec..96ac1f448 100644 --- a/prover/proof_producer/zkevm_rpcd_producer.go +++ b/prover/proof_producer/zkevm_rpcd_producer.go @@ -26,14 +26,15 @@ var ( // ZkevmRpcdProducer is responsible for requesting zk proofs from the given proverd endpoint. type ZkevmRpcdProducer struct { - RpcdEndpoint string // a proverd RPC endpoint - Param string // parameter file to use - L1Endpoint string // a L1 node RPC endpoint - L2Endpoint string // a L2 execution engine's RPC endpoint - Retry bool // retry proof computation if error - CustomProofHook func() ([]byte, uint64, error) // only for testing purposes - ProofTimeTarget uint64 // used for calculating proof delay - ProtocolConfig *bindings.TaikoDataConfig // protocol configurations + RpcdEndpoint string // a proverd RPC endpoint + Param string // parameter file to use + L1Endpoint string // a L1 node RPC endpoint + L2Endpoint string // a L2 execution engine's RPC endpoint + Retry bool // retry proof computation if error + ProofTimeTarget uint64 // used for calculating proof delay + ProtocolConfig *bindings.TaikoDataConfig // protocol configurations + CustomProofHook func() ([]byte, uint64, error) // only for testing purposes + *DummyProofProducer // only for testing purposes } // RequestProofBody represents the JSON body for requesting the proof. @@ -137,6 +138,10 @@ func (p *ZkevmRpcdProducer) RequestProof( "hash", header.Hash(), ) + if p.DummyProofProducer != nil { + return p.DummyProofProducer.RequestProof(ctx, opts, blockID, meta, header, p.Tier(), resultCh) + } + var ( proof []byte degree uint64 @@ -158,6 +163,7 @@ func (p *ZkevmRpcdProducer) RequestProof( Proof: proof, Degree: degree, Opts: opts, + Tier: p.Tier(), } return nil @@ -176,12 +182,12 @@ func (p *ZkevmRpcdProducer) callProverDaemon(ctx context.Context, opts *ProofReq } output, err := p.requestProof(opts) if err != nil { - log.Error("Failed to request proof", "height", opts.Height, "err", err, "endpoint", p.RpcdEndpoint) + log.Error("Failed to request proof", "height", opts.BlockID, "err", err, "endpoint", p.RpcdEndpoint) return err } if output == nil { - log.Info("Proof generating", "height", opts.Height, "time", time.Since(start)) + log.Info("Proof generating", "height", opts.BlockID, "time", time.Since(start)) return errProofGenerating } @@ -195,7 +201,7 @@ func (p *ZkevmRpcdProducer) callProverDaemon(ctx context.Context, opts *ProofReq proof = common.Hex2Bytes(proofOutput) degree = output.Aggregation.Degree - log.Info("Proof generated", "height", opts.Height, "degree", degree, "time", time.Since(start)) + log.Info("Proof generated", "height", opts.BlockID, "degree", degree, "time", time.Since(start)) return nil }, backoff.NewConstantBackOff(proofPollingInterval)); err != nil { return nil, 0, err @@ -211,7 +217,7 @@ func (p *ZkevmRpcdProducer) requestProof(opts *ProofRequestOptions) (*RpcdOutput Method: "proof", Params: []*RequestProofBodyParam{{ Circuit: "super", - Block: opts.Height, + Block: opts.BlockID, L2RPC: p.L2Endpoint, Retry: true, Param: p.Param, @@ -249,7 +255,7 @@ func (p *ZkevmRpcdProducer) requestProof(opts *ProofRequestOptions) (*RpcdOutput defer res.Body.Close() if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to request proof, id: %d, statusCode: %d", opts.Height, res.StatusCode) + return nil, fmt.Errorf("failed to request proof, id: %d, statusCode: %d", opts.BlockID, res.StatusCode) } resBytes, err := io.ReadAll(res.Body) diff --git a/prover/proof_submitter/interface.go b/prover/proof_submitter/interface.go index 13c3a6fcb..f72e83f00 100644 --- a/prover/proof_submitter/interface.go +++ b/prover/proof_submitter/interface.go @@ -12,4 +12,5 @@ type Submitter interface { RequestProof(ctx context.Context, event *bindings.TaikoL1ClientBlockProposed) error SubmitProof(ctx context.Context, proofWithHeader *proofProducer.ProofWithHeader) error CancelProof(ctx context.Context, blockID *big.Int) error + Tier() uint16 } diff --git a/prover/proof_submitter/proof_submitter.go b/prover/proof_submitter/proof_submitter.go index d394c233e..8fb5cfc54 100644 --- a/prover/proof_submitter/proof_submitter.go +++ b/prover/proof_submitter/proof_submitter.go @@ -47,8 +47,8 @@ type ProofSubmitter struct { mutex *sync.Mutex } -// NewValidProofSubmitter creates a new Submitter instance. -func NewValidProofSubmitter( +// New creates a new ProofSubmitter instance. +func New( rpcClient *rpc.Client, proofProducer proofProducer.ProofProducer, resultCh chan *proofProducer.ProofWithHeader, @@ -132,7 +132,7 @@ func (s *ProofSubmitter) RequestProof(ctx context.Context, event *bindings.Taiko // Request proof. opts := &proofProducer.ProofRequestOptions{ - Height: block.Number(), + BlockID: block.Number(), ProverAddress: s.proverAddress, ProposeBlockTxHash: event.Raw.TxHash, L1SignalService: s.l1SignalService, @@ -194,7 +194,7 @@ func (s *ProofSubmitter) SubmitProof( } log.Debug( - "Get the L2 block to prove", + "L2 block to prove", "blockID", blockID, "hash", block.Hash(), "root", header.Root.String(), @@ -308,6 +308,11 @@ func (s *ProofSubmitter) CancelProof(ctx context.Context, blockID *big.Int) erro return s.proofProducer.Cancel(ctx, blockID) } +// Tier returns the proof tier of the current proof submitter. +func (s *ProofSubmitter) Tier() uint16 { + return s.proofProducer.Tier() +} + // uint16ToBytes converts an uint16 to bytes. func uint16ToBytes(i uint16) []byte { b := make([]byte, 2) diff --git a/prover/proof_submitter/proof_submitter_test.go b/prover/proof_submitter/proof_submitter_test.go index 2c4a4074a..b896cc618 100644 --- a/prover/proof_submitter/proof_submitter_test.go +++ b/prover/proof_submitter/proof_submitter_test.go @@ -38,7 +38,7 @@ func (s *ProofSubmitterTestSuite) SetupTest() { s.validProofCh = make(chan *proofProducer.ProofWithHeader, 1024) s.invalidProofCh = make(chan *proofProducer.ProofWithHeader, 1024) - s.validProofSubmitter, err = NewValidProofSubmitter( + s.validProofSubmitter, err = New( s.RpcClient, &proofProducer.OptimisticProofProducer{}, s.validProofCh, diff --git a/prover/prover.go b/prover/prover.go index f6b1c5df6..cd6ca900b 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -18,6 +18,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/bindings/encoding" "github.com/taikoxyz/taiko-client/metrics" eventIterator "github.com/taikoxyz/taiko-client/pkg/chain_iterator/event_iterator" "github.com/taikoxyz/taiko-client/pkg/rpc" @@ -57,7 +58,7 @@ type Prover struct { tiers []*rpc.TierProviderTierWithID // Proof submitters - proofSubmitter proofSubmitter.Submitter + proofSubmitters []proofSubmitter.Submitter // Subscriptions blockProposedCh chan *bindings.TaikoL1ClientBlockProposed @@ -139,42 +140,60 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { p.proposeConcurrencyGuard = make(chan struct{}, cfg.MaxConcurrentProvingJobs) p.submitProofConcurrencyGuard = make(chan struct{}, cfg.MaxConcurrentProvingJobs) - var producer proofProducer.ProofProducer - if cfg.Dummy { - producer = new(proofProducer.OptimisticProofProducer) - } else { - if producer, err = proofProducer.NewZkevmRpcdProducer( - cfg.ZKEvmRpcdEndpoint, - cfg.ZkEvmRpcdParamsPath, - cfg.L1HttpEndpoint, - cfg.L2HttpEndpoint, - true, - p.protocolConfigs, + // Protocol proof tiers + if p.tiers, err = p.rpc.GetTiers(ctx); err != nil { + return err + } + + // Proof submitters + for _, tier := range p.tiers { + var ( + producer proofProducer.ProofProducer + submitter proofSubmitter.Submitter + ) + switch tier.ID { + case encoding.TierOptimisticID: + producer = &proofProducer.OptimisticProofProducer{DummyProofProducer: new(proofProducer.DummyProofProducer)} + case encoding.TierSgxID: + producer = &proofProducer.SGXProofProducer{DummyProofProducer: new(proofProducer.DummyProofProducer)} + case encoding.TierPseZkevmID: + zkEvmRpcdProducer, err := proofProducer.NewZkevmRpcdProducer( + cfg.ZKEvmRpcdEndpoint, + cfg.ZkEvmRpcdParamsPath, + cfg.L1HttpEndpoint, + cfg.L2HttpEndpoint, + true, + p.protocolConfigs, + ) + if err != nil { + return err + } + if p.cfg.Dummy { + zkEvmRpcdProducer.DummyProofProducer = new(proofProducer.DummyProofProducer) + } + producer = zkEvmRpcdProducer + case encoding.TierGuardianID: + producer = &proofProducer.GuardianProofProducer{DummyProofProducer: new(proofProducer.DummyProofProducer)} + } + + if submitter, err = proofSubmitter.New( + p.rpc, + producer, + p.proofGenerationCh, + p.cfg.TaikoL2Address, + p.cfg.L1ProverPrivKey, + p.cfg.Graffiti, + p.cfg.ProofSubmissionMaxRetry, + p.cfg.BackOffRetryInterval, + p.cfg.WaitReceiptTimeout, + p.cfg.ProveBlockGasLimit, + p.cfg.ProveBlockTxReplacementMultiplier, + p.cfg.ProveBlockMaxTxGasTipCap, ); err != nil { return err } - } - // Proof submitter - if p.proofSubmitter, err = proofSubmitter.NewValidProofSubmitter( - p.rpc, - producer, - p.proofGenerationCh, - p.cfg.TaikoL2Address, - p.cfg.L1ProverPrivKey, - p.cfg.Graffiti, - p.cfg.ProofSubmissionMaxRetry, - p.cfg.BackOffRetryInterval, - p.cfg.WaitReceiptTimeout, - p.cfg.ProveBlockGasLimit, - p.cfg.ProveBlockTxReplacementMultiplier, - p.cfg.ProveBlockMaxTxGasTipCap, - ); err != nil { - return err - } - - if p.tiers, err = p.rpc.GetTiers(ctx); err != nil { - return err + p.proofSubmitters = append(p.proofSubmitters, submitter) } // Prover server @@ -485,7 +504,11 @@ func (p *Prover) onBlockProposed( } } - return p.proofSubmitter.RequestProof(ctx, event) + if proofSubmitter := p.selectSubmitter(event.MinTier); proofSubmitter != nil { + return proofSubmitter.RequestProof(ctx, event) + } + + return nil } p.proposeConcurrencyGuard <- struct{}{} @@ -526,8 +549,12 @@ func (p *Prover) submitProofOp(ctx context.Context, proofWithHeader *proofProduc if err := backoff.Retry( func() error { - err := p.proofSubmitter.SubmitProof(p.ctx, proofWithHeader) - if err != nil { + proofSubmitter := p.getSubmitterByTier(proofWithHeader.Tier) + if proofSubmitter == nil { + return nil + } + + if err := proofSubmitter.SubmitProof(p.ctx, proofWithHeader); err != nil { log.Error("Submit proof error", "error", err) return err } @@ -663,7 +690,7 @@ func (p *Prover) isValidProof( func (p *Prover) onTransitionProved(ctx context.Context, event *bindings.TaikoL1ClientTransitionProved) error { metrics.ProverReceivedProvenBlockGauge.Update(event.BlockId.Int64()) - if !p.cfg.GuardianProver { + if !p.cfg.ContestControversialProofs { return nil } @@ -686,11 +713,11 @@ func (p *Prover) onTransitionProved(ctx context.Context, event *bindings.TaikoL1 return err } - return p.requestProofByBlockID(event.BlockId, new(big.Int).SetUint64(L1Height)) + return p.requestProofByBlockID(event.BlockId, new(big.Int).SetUint64(L1Height), true) } // requestProofByBlockID performs a proving operation for the given block. -func (p *Prover) requestProofByBlockID(blockId *big.Int, l1Height *big.Int) error { +func (p *Prover) requestProofByBlockID(blockId *big.Int, l1Height *big.Int, contestOldProof bool) error { onBlockProposed := func( ctx context.Context, event *bindings.TaikoL1ClientBlockProposed, @@ -721,7 +748,11 @@ func (p *Prover) requestProofByBlockID(blockId *big.Int, l1Height *big.Int) erro p.proposeConcurrencyGuard <- struct{}{} - return p.proofSubmitter.RequestProof(ctx, event) + if proofSubmitter := p.selectSubmitter(event.MinTier); proofSubmitter != nil { + return proofSubmitter.RequestProof(ctx, event) + } + + return nil } handleBlockProposedEvent := func() error { @@ -767,7 +798,7 @@ func (p *Prover) onProvingWindowExpired(ctx context.Context, e *bindings.TaikoL1 return nil } - return p.requestProofByBlockID(e.BlockId, new(big.Int).SetUint64(e.Raw.BlockNumber)) + return p.requestProofByBlockID(e.BlockId, new(big.Int).SetUint64(e.Raw.BlockNumber), false) } // getProvingWindow returns the provingWindow of the given proposed block. @@ -780,3 +811,29 @@ func (p *Prover) getProvingWindow(e *bindings.TaikoL1ClientBlockProposed) (time. return 0, errTierNotFound } + +// selectSubmitter returns the proof submitter with the given minTier. +func (p *Prover) selectSubmitter(minTier uint16) proofSubmitter.Submitter { + for _, s := range p.proofSubmitters { + if s.Tier() >= minTier { + return s + } + } + + log.Warn("No proof producer / submitter found for the given minTier", "minTier", minTier) + + return nil +} + +// getSubmitterByTier returns the proof submitter with the given tier. +func (p *Prover) getSubmitterByTier(tier uint16) proofSubmitter.Submitter { + for _, s := range p.proofSubmitters { + if s.Tier() == tier { + return s + } + } + + log.Warn("No proof producer / submitter found for the given tier", "tier", tier) + + return nil +} diff --git a/prover/prover_test.go b/prover/prover_test.go index f1f434995..e63d1dee5 100644 --- a/prover/prover_test.go +++ b/prover/prover_test.go @@ -151,7 +151,7 @@ func (s *ProverTestSuite) TestOnBlockProposed() { // Valid block e := testutils.ProposeAndInsertValidBlock(&s.ClientTestSuite, s.proposer, s.d.ChainSyncer().CalldataSyncer()) s.Nil(s.p.onBlockProposed(context.Background(), e, func() {})) - s.Nil(s.p.proofSubmitter.SubmitProof(context.Background(), <-s.p.proofGenerationCh)) + s.Nil(s.p.selectSubmitter(e.MinTier).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) // Empty blocks for _, e = range testutils.ProposeAndInsertEmptyBlocks( @@ -161,7 +161,7 @@ func (s *ProverTestSuite) TestOnBlockProposed() { ) { s.Nil(s.p.onBlockProposed(context.Background(), e, func() {})) - s.Nil(s.p.proofSubmitter.SubmitProof(context.Background(), <-s.p.proofGenerationCh)) + s.Nil(s.p.selectSubmitter(e.MinTier).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) } }