Skip to content
This repository has been archived by the owner on May 11, 2024. It is now read-only.

Commit

Permalink
fix(prover): ensure L2 reorg finished before generating proofs && add…
Browse files Browse the repository at this point in the history
… `verificationCheckTicker` (#277)
  • Loading branch information
davidtaikocha authored Jun 8, 2023
1 parent cd4e40d commit 6fa24ea
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 1 deletion.
67 changes: 66 additions & 1 deletion prover/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"sync"
"time"

"github.com/cenkalti/backoff/v4"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -235,19 +236,35 @@ func (p *Prover) eventLoop() {
}
}

lastLatestVerifiedL1Height := p.latestVerifiedL1Height

// If there is too many (TaikoData.Config.maxNumBlocks) pending blocks in TaikoL1 contract, there will be no new
// BlockProposed temporarily, so except the BlockProposed subscription, we need another trigger to start
// fetching the proposed blocks.
forceProvingTicker := time.NewTicker(15 * time.Second)
defer forceProvingTicker.Stop()

// If there is no new block verification in `proofCooldownPeriod * 2` seconeds, and the current prover is
// a special prover, we will go back to try proving the block whose id is `lastVerifiedBlockId + 1`.
verificationCheckTicker := time.NewTicker(
time.Duration(p.protocolConfigs.ProofCooldownPeriod.Uint64()*2) * time.Second,
)
defer verificationCheckTicker.Stop()

// Call reqProving() right away to catch up with the latest state.
reqProving()

for {
select {
case <-p.ctx.Done():
return
case <-verificationCheckTicker.C:
if err := backoff.Retry(
func() error { return p.checkChainVerification(lastLatestVerifiedL1Height) },
backoff.NewExponentialBackOff(),
); err != nil {
log.Error("Check chain verification error", "error", err)
}
case proofWithHeader := <-p.proofGenerationCh:
p.submitProofOp(p.ctx, proofWithHeader)
case <-p.proveNotify:
Expand Down Expand Up @@ -345,6 +362,27 @@ func (p *Prover) onBlockProposed(
if event.Id.Uint64() <= p.lastHandledBlockID {
return nil
}

currentL1OriginHeader, err := p.rpc.L1.HeaderByNumber(ctx, new(big.Int).SetUint64(event.Meta.L1Height))
if err != nil {
return err
}

if currentL1OriginHeader.Hash() != event.Meta.L1Hash {
log.Warn(
"L1 block hash mismatch due to L1 reorg",
"height", event.Meta.L1Height,
"currentL1OriginHeader", currentL1OriginHeader.Hash(),
"L1HashInEvent", event.Meta.L1Hash,
)

return fmt.Errorf(
"L1 block hash mismatch due to L1 reorg: %s != %s",
currentL1OriginHeader.Hash(),
event.Meta.L1Hash,
)
}

log.Info(
"Proposed block",
"L1Height", event.Raw.BlockNumber,
Expand Down Expand Up @@ -535,7 +573,7 @@ func (p *Prover) initL1Current(startingBlockID *big.Int) error {
latestVerifiedHeaderL1Origin, err := p.rpc.L2.L1OriginByID(p.ctx, startingBlockID)
if err != nil {
if err.Error() == ethereum.NotFound.Error() {
log.Warn("Failed to find L1Origin for blockID: %d, use latest L1 head instead", startingBlockID)
log.Warn("Failed to find L1Origin for blockID, use latest L1 head instead", "blockID", startingBlockID)
l1Head, err := p.rpc.L1.BlockNumber(p.ctx)
if err != nil {
return err
Expand Down Expand Up @@ -574,6 +612,33 @@ func (p *Prover) closeSubscription() {
p.blockProposedSub.Unsubscribe()
}

// checkChainVerification checks if there is no new block verification in protocol, if so,
// it will let current sepecial prover to go back to try proving the block whose id is `lastVerifiedBlockId + 1`.
func (p *Prover) checkChainVerification(lastLatestVerifiedL1Height uint64) error {
if (!p.cfg.SystemProver && !p.cfg.OracleProver) || lastLatestVerifiedL1Height != p.latestVerifiedL1Height {
return nil
}

log.Warn(
"No new block verification in `proofCooldownPeriod * 2` seconeds",
"latestVerifiedL1Height", p.latestVerifiedL1Height,
"proofCooldownPeriod", p.protocolConfigs.ProofCooldownPeriod,
)

stateVar, err := p.rpc.TaikoL1.GetStateVariables(nil)
if err != nil {
log.Error("Failed to get protocol state variables", "error", err)
return err
}

if err := p.initL1Current(new(big.Int).SetUint64(stateVar.LastVerifiedBlockId)); err != nil {
return err
}
p.lastHandledBlockID = stateVar.LastVerifiedBlockId

return nil
}

// cancelProofIfValid cancels proof only if the parentGasUsed and parentHash in the proof match what
// is expected
func (p *Prover) cancelProofIfValid(
Expand Down
7 changes: 7 additions & 0 deletions prover/prover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ func (s *ProverTestSuite) TestStartSubscription() {
s.NotPanics(s.p.closeSubscription)
}

func (s *ProverTestSuite) TestCheckChainVerification() {
s.Nil(s.p.checkChainVerification(0))
s.p.latestVerifiedL1Height = 1024
s.p.cfg.SystemProver = true
s.Nil(s.p.checkChainVerification(1024))
}

func (s *ProverTestSuite) TestStartClose() {
s.Nil(s.p.Start())
s.cancel()
Expand Down

0 comments on commit 6fa24ea

Please sign in to comment.