From fd1a1463eb5cbac950858b124509aaf96e86619f Mon Sep 17 00:00:00 2001 From: David Date: Thu, 12 Oct 2023 14:48:54 +0800 Subject: [PATCH] feat(prover): more listeners --- driver/chain_syncer/calldata/syncer_test.go | 2 +- driver/chain_syncer/chain_syncer_test.go | 2 +- driver/driver_test.go | 2 +- driver/state/state.go | 18 ++- proposer/proposer_test.go | 2 +- prover/proof_submitter/proof_contester.go | 7 +- .../proof_submitter/proof_submitter_test.go | 2 +- prover/proof_submitter/transaction/sender.go | 4 + .../transaction/sender_test.go | 2 + prover/prover.go | 106 ++++++++++++++---- prover/prover_test.go | 13 ++- testutils/suite.go | 7 +- 12 files changed, 133 insertions(+), 34 deletions(-) diff --git a/driver/chain_syncer/calldata/syncer_test.go b/driver/chain_syncer/calldata/syncer_test.go index 05718a902..8db3b45fa 100644 --- a/driver/chain_syncer/calldata/syncer_test.go +++ b/driver/chain_syncer/calldata/syncer_test.go @@ -56,7 +56,7 @@ func (s *CalldataSyncerTestSuite) SetupTest() { L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), ProposeInterval: &proposeInterval, MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, + WaitReceiptTimeout: 12 * time.Second, ProverEndpoints: s.ProverEndpoints, OptimisticTierFee: common.Big256, SgxTierFee: common.Big256, diff --git a/driver/chain_syncer/chain_syncer_test.go b/driver/chain_syncer/chain_syncer_test.go index d31fdb348..a4af01544 100644 --- a/driver/chain_syncer/chain_syncer_test.go +++ b/driver/chain_syncer/chain_syncer_test.go @@ -58,7 +58,7 @@ func (s *ChainSyncerTestSuite) SetupTest() { L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), ProposeInterval: &proposeInterval, MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, + WaitReceiptTimeout: 12 * time.Second, ProverEndpoints: s.ProverEndpoints, OptimisticTierFee: common.Big256, SgxTierFee: common.Big256, diff --git a/driver/driver_test.go b/driver/driver_test.go index d3fdbcde2..40df0e74d 100644 --- a/driver/driver_test.go +++ b/driver/driver_test.go @@ -62,7 +62,7 @@ func (s *DriverTestSuite) SetupTest() { L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), ProposeInterval: &proposeInterval, MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, + WaitReceiptTimeout: 12 * time.Second, ProverEndpoints: s.ProverEndpoints, OptimisticTierFee: common.Big256, SgxTierFee: common.Big256, diff --git a/driver/state/state.go b/driver/state/state.go index b76c8f752..e15b69b69 100644 --- a/driver/state/state.go +++ b/driver/state/state.go @@ -167,9 +167,23 @@ func (s *State) startSubscriptions(ctx context.Context) { case e := <-s.blockProposedCh: s.setHeadBlockID(e.BlockId) case e := <-s.transitionProvedCh: - log.Info("✅ Block proven", "blockID", e.BlockId, "hash", common.Hash(e.BlockHash), "prover", e.Prover) + log.Info( + "✅ Transition proven", + "blockID", e.BlockId, + "parentHash", common.Hash(e.ParentHash), + "hash", common.Hash(e.BlockHash), + "signalRoot", common.Hash(e.SignalRoot), + "prover", e.Prover, + ) case e := <-s.blockVerifiedCh: - log.Info("📈 Block verified", "blockID", e.BlockId, "hash", common.Hash(e.BlockHash), "prover", e.Prover) + log.Info( + "📈 Block verified", + "blockID", e.BlockId, + "hash", common.Hash(e.BlockHash), + "signalRoot", common.Hash(e.SignalRoot), + "assignedProver", e.AssignedProver, + "prover", e.Prover, + ) case e := <-s.crossChainSynced: // Verify the protocol synced block, check if it exists in // L2 execution engine. diff --git a/proposer/proposer_test.go b/proposer/proposer_test.go index f841d0992..5b1552e18 100644 --- a/proposer/proposer_test.go +++ b/proposer/proposer_test.go @@ -44,7 +44,7 @@ func (s *ProposerTestSuite) SetupTest() { ProposeInterval: &proposeInterval, MaxProposedTxListsPerEpoch: 1, ProposeBlockTxReplacementMultiplier: 2, - WaitReceiptTimeout: 10 * time.Second, + WaitReceiptTimeout: 12 * time.Second, ProverEndpoints: s.ProverEndpoints, OptimisticTierFee: common.Big256, SgxTierFee: common.Big256, diff --git a/prover/proof_submitter/proof_contester.go b/prover/proof_submitter/proof_contester.go index 2e976d100..ce93d4be2 100644 --- a/prover/proof_submitter/proof_contester.go +++ b/prover/proof_submitter/proof_contester.go @@ -126,8 +126,11 @@ func (c *ProofContester) SubmitContest( Meta: &blockProposedEvent.Meta, Header: header, Proof: []byte{}, - Opts: &proofProducer.ProofRequestOptions{EventL1Hash: blockProposedEvent.Raw.BlockHash}, - Tier: transitionProvedEvent.Tier, + Opts: &proofProducer.ProofRequestOptions{ + EventL1Hash: blockProposedEvent.Raw.BlockHash, + SignalRoot: evidence.SignalRoot, + }, + Tier: transitionProvedEvent.Tier, }, c.txBuilder.Build(ctx, transitionProvedEvent.BlockId, input), ); err != nil { diff --git a/prover/proof_submitter/proof_submitter_test.go b/prover/proof_submitter/proof_submitter_test.go index 3fa6828f1..bbd5e0003 100644 --- a/prover/proof_submitter/proof_submitter_test.go +++ b/prover/proof_submitter/proof_submitter_test.go @@ -83,7 +83,7 @@ func (s *ProofSubmitterTestSuite) SetupTest() { L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), ProposeInterval: &proposeInterval, MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, + WaitReceiptTimeout: 12 * time.Second, ProverEndpoints: s.ProverEndpoints, OptimisticTierFee: common.Big256, SgxTierFee: common.Big256, diff --git a/prover/proof_submitter/transaction/sender.go b/prover/proof_submitter/transaction/sender.go index dba02ad83..3ae2fcab3 100644 --- a/prover/proof_submitter/transaction/sender.go +++ b/prover/proof_submitter/transaction/sender.go @@ -110,8 +110,12 @@ func (s *Sender) Send( log.Info( "💰 Your block proof was accepted", "blockID", proofWithHeader.BlockID, + "parentHash", proofWithHeader.Header.ParentHash, + "hash", proofWithHeader.Header.Hash(), + "signalRoot", proofWithHeader.Opts.SignalRoot, "txHash", tx.Hash(), "tier", proofWithHeader.Tier, + "isContest", len(proofWithHeader.Proof) == 0, ) return nil diff --git a/prover/proof_submitter/transaction/sender_test.go b/prover/proof_submitter/transaction/sender_test.go index 1ca00de66..80a07e2aa 100644 --- a/prover/proof_submitter/transaction/sender_test.go +++ b/prover/proof_submitter/transaction/sender_test.go @@ -49,6 +49,7 @@ func (s *TransactionTestSuite) TestSendTxWithBackoff() { &proofProducer.ProofWithHeader{ Meta: meta, BlockID: common.Big1, + Header: &types.Header{}, Opts: &proofProducer.ProofRequestOptions{EventL1Hash: l1Head.Hash()}, }, func(nonce *big.Int) (*types.Transaction, error) { return nil, errors.New("L1_TEST") }, @@ -59,6 +60,7 @@ func (s *TransactionTestSuite) TestSendTxWithBackoff() { &proofProducer.ProofWithHeader{ Meta: meta, BlockID: common.Big1, + Header: &types.Header{}, Opts: &proofProducer.ProofRequestOptions{EventL1Hash: l1Head.Hash()}, }, func(nonce *big.Int) (*types.Transaction, error) { diff --git a/prover/prover.go b/prover/prover.go index 187af6012..47c2dfb6e 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -296,8 +296,9 @@ func (p *Prover) eventLoop() { log.Error("Handle TransitionProved event error", "error", err) } case e := <-p.transitionContestedCh: - // TODO: add a listener. - log.Info("Transaction contested", "event", e) + if err := p.onTransitionContested(p.ctx, e); err != nil { + log.Error("Handle TransitionContested event error", "error", err) + } case e := <-p.proofWindowExpiredCh: if err := p.onProvingWindowExpired(p.ctx, e); err != nil { log.Error("Handle provingWindow expired event error", "error", err) @@ -625,18 +626,69 @@ func (p *Prover) submitProofOp(ctx context.Context, proofWithHeader *proofProduc }() } +// onTransitionContested tries to submit a higher tier proof for the contested transition. +func (p *Prover) onTransitionContested(ctx context.Context, e *bindings.TaikoL1ClientTransitionContested) error { + log.Info( + "🗡 Transition contested", + "blockID", e.BlockId, + "parentHash", common.Bytes2Hex(e.ParentHash[:]), + "hash", common.Bytes2Hex(e.BlockHash[:]), + "signalRoot", common.BytesToHash(e.SignalRoot[:]), + "contester", e.Contester, + "bond", e.ContestBond, + ) + + // If this prover is not in contest mode, we simply output a log and return. + if !p.cfg.ContestControversialProofs { + return nil + } + + // Compare the contested transition to the block in local L2 canonical chain. + isValidProof, err := p.isValidProof( + ctx, + e.BlockId, + e.ParentHash, + e.BlockHash, + e.SignalRoot, + ) + if err != nil { + return err + } + if isValidProof { + log.Info( + "Contested transition is valid to local canonical chain, ignore the contest", + "blockID", e.BlockId, + "parentHash", common.Bytes2Hex(e.ParentHash[:]), + "hash", common.Bytes2Hex(e.BlockHash[:]), + "signalRoot", common.BytesToHash(e.SignalRoot[:]), + "contester", e.Contester, + "bond", e.ContestBond, + ) + return nil + } + + l1Height, err := p.rpc.TaikoL2.LatestSyncedL1Height(&bind.CallOpts{Context: ctx, BlockNumber: e.BlockId}) + if err != nil { + return err + } + + return p.requestProofByBlockID(e.BlockId, new(big.Int).SetUint64(l1Height+1), e.Tier+1, nil) +} + // onBlockVerified update the latestVerified block in current state, and cancels // the block being proven if it's verified. -func (p *Prover) onBlockVerified(ctx context.Context, event *bindings.TaikoL1ClientBlockVerified) error { - metrics.ProverLatestVerifiedIDGauge.Update(event.BlockId.Int64()) +func (p *Prover) onBlockVerified(ctx context.Context, e *bindings.TaikoL1ClientBlockVerified) error { + metrics.ProverLatestVerifiedIDGauge.Update(e.BlockId.Int64()) - p.latestVerifiedL1Height = event.Raw.BlockNumber + p.latestVerifiedL1Height = e.Raw.BlockNumber log.Info( "New verified block", - "blockID", event.BlockId, - "hash", common.BytesToHash(event.BlockHash[:]), - "prover", event.Prover, + "blockID", e.BlockId, + "hash", common.BytesToHash(e.BlockHash[:]), + "signalRoot", common.BytesToHash(e.SignalRoot[:]), + "assignedProver", e.AssignedProver, + "prover", e.Prover, ) return nil @@ -665,7 +717,10 @@ func (p *Prover) onTransitionProved(ctx context.Context, event *bindings.TaikoL1 isValidProof, err := p.isValidProof( ctx, - event, + event.BlockId, + event.ParentHash, + event.BlockHash, + event.SignalRoot, ) if err != nil { return err @@ -680,7 +735,7 @@ func (p *Prover) onTransitionProved(ctx context.Context, event *bindings.TaikoL1 } log.Info( - "Contest a proven block", + "Contest a proven transition", "blockID", event.BlockId, "l1Height", l1Height, "tier", event.Tier, @@ -689,7 +744,7 @@ func (p *Prover) onTransitionProved(ctx context.Context, event *bindings.TaikoL1 "signalRoot", common.Bytes2Hex(event.SignalRoot[:]), ) - return p.requestProofByBlockID(event.BlockId, new(big.Int).SetUint64(l1Height+1), event) + return p.requestProofByBlockID(event.BlockId, new(big.Int).SetUint64(l1Height+1), event.Tier, event) } // Name returns the application name. @@ -774,44 +829,51 @@ func (p *Prover) closeSubscription() { } // isValidProof checks if the given proof is a valid one, comparing to current L2 node canonical chain. -func (p *Prover) isValidProof(ctx context.Context, event *bindings.TaikoL1ClientTransitionProved) (bool, error) { - parent, err := p.rpc.L2ParentByBlockId(ctx, event.BlockId) +func (p *Prover) isValidProof( + ctx context.Context, + blockID *big.Int, + parentHash common.Hash, + blockHash common.Hash, + signalRoot common.Hash, +) (bool, error) { + parent, err := p.rpc.L2ParentByBlockId(ctx, blockID) if err != nil { return false, err } - block, err := p.rpc.L2.BlockByNumber(ctx, event.BlockId) + block, err := p.rpc.L2.BlockByNumber(ctx, blockID) if err != nil { return false, err } l2SignalService, err := p.rpc.TaikoL2.Resolve0( - &bind.CallOpts{Context: ctx, BlockNumber: event.BlockId}, + &bind.CallOpts{Context: ctx, BlockNumber: blockID}, rpc.StringToBytes32("signal_service"), false, ) if err != nil { return false, err } - signalRoot, err := p.rpc.GetStorageRoot( + root, err := p.rpc.GetStorageRoot( ctx, p.rpc.L2GethClient, l2SignalService, - event.BlockId, + blockID, ) if err != nil { return false, err } - return parent.Hash() == event.ParentHash && - block.Hash() == event.BlockHash && - signalRoot == event.SignalRoot, nil + return parent.Hash() == parentHash && + block.Hash() == blockHash && + root == signalRoot, nil } // requestProofByBlockID performs a proving operation for the given block. func (p *Prover) requestProofByBlockID( blockID *big.Int, l1Height *big.Int, + minTier uint16, // If this event is not nil, then the prover will try contesting the transition. transitionProvedEvent *bindings.TaikoL1ClientTransitionProved, ) error { @@ -847,7 +909,7 @@ func (p *Prover) requestProofByBlockID( } // If there is no proof submitter selected, skip proving it. - if proofSubmitter := p.selectSubmitter(event.MinTier); proofSubmitter != nil { + if proofSubmitter := p.selectSubmitter(minTier); proofSubmitter != nil { return proofSubmitter.RequestProof(ctx, event) } @@ -927,7 +989,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), nil) + return p.requestProofByBlockID(e.BlockId, new(big.Int).SetUint64(e.Raw.BlockNumber), e.MinTier, nil) } // getProvingWindow returns the provingWindow of the given proposed block. diff --git a/prover/prover_test.go b/prover/prover_test.go index 380df8c4f..b6c11624f 100644 --- a/prover/prover_test.go +++ b/prover/prover_test.go @@ -61,6 +61,7 @@ func (s *ProverTestSuite) SetupTest() { MinSgxTierFee: common.Big1, MinPseZkevmTierFee: common.Big1, HTTPServerPort: uint64(port), + WaitReceiptTimeout: 12 * time.Second, }))) p.srv = testutils.NewTestProverServer( &s.ClientTestSuite, @@ -104,7 +105,7 @@ func (s *ProverTestSuite) SetupTest() { L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), ProposeInterval: &proposeInterval, MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, + WaitReceiptTimeout: 12 * time.Second, ProverEndpoints: []*url.URL{proverServerUrl}, OptimisticTierFee: common.Big256, SgxTierFee: common.Big256, @@ -244,7 +245,6 @@ func (s *ProverTestSuite) TestContestWrongBlocks() { // Contest the transition. contestedSink := make(chan *bindings.TaikoL1ClientTransitionContested) - contestedSub, err := s.p.rpc.TaikoL1.WatchTransitionContested(nil, contestedSink, nil) s.Nil(err) defer func() { @@ -259,6 +259,15 @@ func (s *ProverTestSuite) TestContestWrongBlocks() { s.Equal(header.Number.Uint64(), contestedEvent.BlockId.Uint64()) s.Equal(common.BytesToHash(proofWithHeader.Opts.BlockHash[:]), common.BytesToHash(contestedEvent.BlockHash[:])) s.Equal(header.ParentHash, common.BytesToHash(contestedEvent.ParentHash[:])) + + s.Nil(s.p.onTransitionContested(context.Background(), contestedEvent)) + s.Nil(s.p.selectSubmitter(contestedEvent.Tier+1).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) + provenEvent := <-sink + + s.Equal(header.Number.Uint64(), provenEvent.BlockId.Uint64()) + s.Equal(header.Hash(), common.BytesToHash(provenEvent.BlockHash[:])) + s.Equal(header.ParentHash, common.BytesToHash(provenEvent.ParentHash[:])) + s.Greater(provenEvent.Tier, contestedEvent.Tier) } func (s *ProverTestSuite) TestProveExpiredUnassignedBlock() { diff --git a/testutils/suite.go b/testutils/suite.go index 49caa9857..8312ada83 100644 --- a/testutils/suite.go +++ b/testutils/suite.go @@ -82,7 +82,7 @@ func (s *ClientTestSuite) SetupTest() { s.Nil(err) if tokenBalance.Cmp(common.Big0) == 0 { - // Do not verify zk proofs in tests. + // Do not verify zk && sgx proofs in tests. addressManager, err := bindings.NewAddressManager( common.HexToAddress(os.Getenv("ADDRESS_MANAGER_CONTRACT_ADDRESS")), rpcCli.L1, @@ -103,6 +103,11 @@ func (s *ClientTestSuite) SetupTest() { _, err = rpc.WaitReceipt(context.Background(), rpcCli.L1, tx) s.Nil(err) + tx, err = addressManager.SetAddress(opts, chainID, rpc.StringToBytes32("tier_sgx"), common.Address{}) + s.Nil(err) + _, err = rpc.WaitReceipt(context.Background(), rpcCli.L1, tx) + s.Nil(err) + // Deposit taiko tokens for provers. opts, err = bind.NewKeyedTransactorWithChainID(l1ProverPrivKey, rpcCli.L1ChainID) s.Nil(err)