Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(taiko-client): change guardian provers to directly submit proofs instead of contests #17069

Merged
merged 15 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type AssignmentExpiredEventHandler struct {
proofSubmissionCh chan<- *proofProducer.ProofRequestBody
proofContestCh chan<- *proofProducer.ContestRequestBody
contesterMode bool
// Guardian prover related.
isGuardian bool
}

// NewAssignmentExpiredEventHandler creates a new AssignmentExpiredEventHandler instance.
Expand All @@ -28,8 +30,9 @@ func NewAssignmentExpiredEventHandler(
proofSubmissionCh chan *proofProducer.ProofRequestBody,
proofContestCh chan *proofProducer.ContestRequestBody,
contesterMode bool,
isGuardian bool,
) *AssignmentExpiredEventHandler {
return &AssignmentExpiredEventHandler{rpc, proverAddress, proofSubmissionCh, proofContestCh, contesterMode}
return &AssignmentExpiredEventHandler{rpc, proverAddress, proofSubmissionCh, proofContestCh, contesterMode, isGuardian}
}

// Handle implements the AssignmentExpiredHandler interface.
Expand Down Expand Up @@ -63,7 +66,7 @@ func (h *AssignmentExpiredEventHandler) Handle(

// If there is no contester, we submit a contest to protocol.
go func() {
if proofStatus.CurrentTransitionState.Contester == rpc.ZeroAddress {
if proofStatus.CurrentTransitionState.Contester == rpc.ZeroAddress && !h.isGuardian {
h.proofContestCh <- &proofProducer.ContestRequestBody{
BlockID: e.BlockId,
ProposedIn: new(big.Int).SetUint64(e.Raw.BlockNumber),
Expand Down
82 changes: 25 additions & 57 deletions packages/taiko-client/prover/event_handler/block_proposed.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package handler

import (
"context"
"crypto/rand"
"errors"
"fmt"
"math/big"
Expand All @@ -24,9 +23,8 @@ import (
)

var (
errL1Reorged = errors.New("L1 reorged")
proofExpirationDelay = 6 * 12 * time.Second // 6 ethereum blocks
submissionDelayRandomBumpRange float64 = 20
errL1Reorged = errors.New("L1 reorged")
proofExpirationDelay = 6 * 12 * time.Second // 6 ethereum blocks
)

// BlockProposedEventHandler is responsible for handling the BlockProposed event as a prover.
Expand All @@ -44,8 +42,7 @@ type BlockProposedEventHandler struct {
contesterMode bool
proveUnassignedBlocks bool
// Guardian prover related.
isGuardian bool
submissionDelay time.Duration
isGuardian bool
}

// NewBlockProposedEventHandlerOps is the options for creating a new BlockProposedEventHandler.
Expand All @@ -62,7 +59,6 @@ type NewBlockProposedEventHandlerOps struct {
BackOffMaxRetrys uint64
ContesterMode bool
ProveUnassignedBlocks bool
SubmissionDelay time.Duration
}

// NewBlockProposedEventHandler creates a new BlockProposedEventHandler instance.
Expand All @@ -81,7 +77,6 @@ func NewBlockProposedEventHandler(opts *NewBlockProposedEventHandlerOps) *BlockP
opts.ContesterMode,
opts.ProveUnassignedBlocks,
false,
opts.SubmissionDelay,
}
}

Expand Down Expand Up @@ -223,29 +218,6 @@ func (h *BlockProposedEventHandler) checkL1Reorg(
return nil
}

// getRandomBumpedSubmissionDelay returns a random bumped submission delay.
func (h *BlockProposedEventHandler) getRandomBumpedSubmissionDelay(expiredAt time.Time) (time.Duration, error) {
if h.submissionDelay == 0 {
return h.submissionDelay, nil
}

randomBump, err := rand.Int(
rand.Reader,
new(big.Int).SetUint64(uint64(h.submissionDelay.Seconds()*submissionDelayRandomBumpRange/100)),
)
if err != nil {
return 0, err
}

delay := time.Duration(h.submissionDelay.Seconds()+float64(randomBump.Uint64())) * time.Second

if time.Since(expiredAt) >= delay {
return 0, nil
}

return delay - time.Since(expiredAt), nil
}

// checkExpirationAndSubmitProof checks whether the proposed block's proving window is expired,
// and submits a new proof if necessary.
func (h *BlockProposedEventHandler) checkExpirationAndSubmitProof(
Expand Down Expand Up @@ -295,28 +267,33 @@ func (h *BlockProposedEventHandler) checkExpirationAndSubmitProof(
return nil
}

// If the current proof has not been contested, we should contest it at first.
if proofStatus.CurrentTransitionState.Contester == rpc.ZeroAddress {
h.proofContestCh <- &proofProducer.ContestRequestBody{
BlockID: e.BlockId,
ProposedIn: new(big.Int).SetUint64(e.Raw.BlockNumber),
ParentHash: proofStatus.ParentHeader.Hash(),
Meta: &e.Meta,
Tier: e.Meta.MinTier,
}
if h.isGuardian {
// In guardian prover, we submit a proof directly.
h.proofSubmissionCh <- &proofProducer.ProofRequestBody{Tier: encoding.TierGuardianMinorityID, Event: e}
} else {
// The invalid proof submitted to protocol is contested by another prover,
// we need to submit a proof with a higher tier.
h.proofSubmissionCh <- &proofProducer.ProofRequestBody{
Tier: proofStatus.CurrentTransitionState.Tier + 1,
Event: e,
// If the current proof has not been contested, we should contest it at first.
if proofStatus.CurrentTransitionState.Contester == rpc.ZeroAddress {
h.proofContestCh <- &proofProducer.ContestRequestBody{
BlockID: e.BlockId,
ProposedIn: new(big.Int).SetUint64(e.Raw.BlockNumber),
ParentHash: proofStatus.ParentHeader.Hash(),
Meta: &e.Meta,
Tier: e.Meta.MinTier,
}
} else {
// The invalid proof submitted to protocol is contested by another prover,
// we need to submit a proof with a higher tier.
h.proofSubmissionCh <- &proofProducer.ProofRequestBody{
Tier: proofStatus.CurrentTransitionState.Tier + 1,
Event: e,
}
}
}

return nil
}

windowExpired, expiredAt, timeToExpire, err := isProvingWindowExpired(e, h.sharedState.GetTiers())
windowExpired, _, timeToExpire, err := IsProvingWindowExpired(&e.Meta, h.sharedState.GetTiers())
if err != nil {
return fmt.Errorf("failed to check if the proving window is expired: %w", err)
}
Expand All @@ -339,7 +316,7 @@ func (h *BlockProposedEventHandler) checkExpirationAndSubmitProof(
"timeToExpire", timeToExpire,
)
time.AfterFunc(
// Add another 60 seconds, to ensure one more L1 block will be mined before the proof submission
// Add another 72 seconds, to ensure one more L1 block will be mined before the proof submission
timeToExpire+proofExpirationDelay,
func() { h.assignmentExpiredCh <- e },
)
Expand All @@ -352,12 +329,6 @@ func (h *BlockProposedEventHandler) checkExpirationAndSubmitProof(
// try to submit a proof for this proposed block.
tier := e.Meta.MinTier

// Get a random bumped submission delay, if necessary.
submissionDelay, err := h.getRandomBumpedSubmissionDelay(expiredAt)
if err != nil {
return err
}

if h.isGuardian {
tier = encoding.TierGuardianMinorityID
}
Expand All @@ -367,15 +338,12 @@ func (h *BlockProposedEventHandler) checkExpirationAndSubmitProof(
"blockID", e.BlockId,
"assignProver", e.AssignedProver,
"minTier", e.Meta.MinTier,
"submissionDelay", submissionDelay,
"tier", tier,
)

metrics.ProverProofsAssigned.Add(1)

time.AfterFunc(submissionDelay, func() {
h.proofSubmissionCh <- &proofProducer.ProofRequestBody{Tier: tier, Event: e}
})
h.proofSubmissionCh <- &proofProducer.ProofRequestBody{Tier: tier, Event: e}

return nil
}
Expand Down
33 changes: 0 additions & 33 deletions packages/taiko-client/prover/event_handler/block_proposed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,3 @@ func (s *EventHandlerTestSuite) TestBlockProposedHandle() {
err := handler.Handle(context.Background(), e, func() {})
s.Nil(err)
}

func (s *EventHandlerTestSuite) TestGetRandomBumpedSubmissionDelay() {
opts := &NewBlockProposedEventHandlerOps{
SharedState: &state.SharedState{},
ProverAddress: common.Address{},
GenesisHeightL1: 0,
RPC: s.RPCClient,
ProofGenerationCh: make(chan *proofProducer.ProofWithHeader),
AssignmentExpiredCh: make(chan *bindings.TaikoL1ClientBlockProposed),
ProofSubmissionCh: make(chan *proofProducer.ProofRequestBody),
ProofContestCh: make(chan *proofProducer.ContestRequestBody),
BackOffRetryInterval: 1 * time.Minute,
BackOffMaxRetrys: 5,
ContesterMode: true,
ProveUnassignedBlocks: true,
}
handler1 := NewBlockProposedEventHandler(opts)

delay, err := handler1.getRandomBumpedSubmissionDelay(time.Now())
s.Nil(err)
s.Zero(delay)

opts.SubmissionDelay = 1 * time.Hour
handler2 := NewBlockProposedEventHandler(opts)
delay, err = handler2.getRandomBumpedSubmissionDelay(time.Now())
s.Nil(err)
s.NotZero(delay)
s.Greater(delay.Seconds(), opts.SubmissionDelay.Seconds())
s.Less(
delay.Seconds(),
opts.SubmissionDelay.Seconds()*(1+(submissionDelayRandomBumpRange/100)),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (h *TransitionContestedEventHandler) Handle(
return err
}

blockProposedEvent, err := getBlockProposedEventFromBlockID(
blockProposedEvent, err := GetBlockProposedEventFromBlockID(
ctx,
h.rpc,
e.BlockId,
Expand Down
55 changes: 42 additions & 13 deletions packages/taiko-client/prover/event_handler/transition_proved.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"math/big"

"github.com/taikoxyz/taiko-mono/packages/taiko-client/bindings/encoding"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"

Expand All @@ -15,18 +17,28 @@ import (

// TransitionProvedEventHandler is responsible for handling the TransitionProved event.
type TransitionProvedEventHandler struct {
rpc *rpc.Client
proofContestCh chan<- *proofProducer.ContestRequestBody
contesterMode bool
rpc *rpc.Client
proofContestCh chan<- *proofProducer.ContestRequestBody
proofSubmissionCh chan<- *proofProducer.ProofRequestBody
contesterMode bool
isGuardian bool
}

// NewTransitionProvedEventHandler creates a new TransitionProvedEventHandler instance.
func NewTransitionProvedEventHandler(
rpc *rpc.Client,
proofContestCh chan *proofProducer.ContestRequestBody,
proofSubmissionCh chan *proofProducer.ProofRequestBody,
contesterMode bool,
isGuardian bool,
) *TransitionProvedEventHandler {
return &TransitionProvedEventHandler{rpc, proofContestCh, contesterMode}
return &TransitionProvedEventHandler{
rpc,
proofContestCh,
proofSubmissionCh,
contesterMode,
isGuardian,
}
}

// Handle implements the TransitionProvedHandler interface.
Expand Down Expand Up @@ -78,15 +90,32 @@ func (h *TransitionProvedEventHandler) Handle(
"blockHash", common.Bytes2Hex(e.Tran.BlockHash[:]),
"stateRoot", common.Bytes2Hex(e.Tran.StateRoot[:]),
)

go func() {
h.proofContestCh <- &proofProducer.ContestRequestBody{
BlockID: e.BlockId,
ProposedIn: new(big.Int).SetUint64(blockInfo.ProposedIn),
ParentHash: e.Tran.ParentHash,
Meta: meta,
Tier: e.Tier,
if h.isGuardian {
blockProposedEvent, err := GetBlockProposedEventFromBlockID(
ctx,
h.rpc,
e.BlockId,
new(big.Int).SetUint64(blockInfo.ProposedIn),
)
if err != nil {
return err
}
}()
go func() {
h.proofSubmissionCh <- &proofProducer.ProofRequestBody{
Tier: encoding.TierGuardianMinorityID,
Event: blockProposedEvent,
}
}()
} else {
go func() {
h.proofContestCh <- &proofProducer.ContestRequestBody{
BlockID: e.BlockId,
ProposedIn: new(big.Int).SetUint64(blockInfo.ProposedIn),
ParentHash: e.Tran.ParentHash,
Meta: meta,
Tier: e.Tier,
}
}()
}
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ func (s *EventHandlerTestSuite) TestTransitionProvedHandle() {
handler := NewTransitionProvedEventHandler(
s.RPCClient,
make(chan *proofProducer.ContestRequestBody),
make(chan *proofProducer.ProofRequestBody),
true,
false,
)
e := s.ProposeAndInsertValidBlock(s.proposer, s.blobSyncer)
err := handler.Handle(context.Background(), &bindings.TaikoL1ClientTransitionProved{
Expand Down
22 changes: 11 additions & 11 deletions packages/taiko-client/prover/event_handler/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,22 @@ func isValidProof(
l2Header.Root == stateRoot, nil
}

// getProvingWindow returns the provingWindow of the given proposed block.
// getProvingWindow returns the provingWindow of the given tier.
func getProvingWindow(
e *bindings.TaikoL1ClientBlockProposed,
tier uint16,
tiers []*rpc.TierProviderTierWithID,
) (time.Duration, error) {
for _, t := range tiers {
if e.Meta.MinTier == t.ID {
if tier == t.ID {
return time.Duration(t.ProvingWindow) * time.Minute, nil
}
}

return 0, errTierNotFound
}

// getBlockProposedEventFromBlockID fetches the BlockProposed event by the given block id.
func getBlockProposedEventFromBlockID(
// GetBlockProposedEventFromBlockID fetches the BlockProposed event by the given block id.
func GetBlockProposedEventFromBlockID(
ctx context.Context,
rpc *rpc.Client,
id *big.Int,
Expand Down Expand Up @@ -120,28 +120,28 @@ func getMetadataFromBlockID(
id *big.Int,
proposedIn *big.Int,
) (*bindings.TaikoDataBlockMetadata, error) {
e, err := getBlockProposedEventFromBlockID(ctx, rpc, id, proposedIn)
e, err := GetBlockProposedEventFromBlockID(ctx, rpc, id, proposedIn)
if err != nil {
return nil, err
}
return &e.Meta, nil
}

// isProvingWindowExpired returns true as the first return parameter if the assigned prover
// IsProvingWindowExpired returns true as the first return parameter if the assigned prover
// proving window of the given proposed block is expired, and the second return parameter is the time
// remaining til proving window is expired.
func isProvingWindowExpired(
e *bindings.TaikoL1ClientBlockProposed,
func IsProvingWindowExpired(
metadata *bindings.TaikoDataBlockMetadata,
tiers []*rpc.TierProviderTierWithID,
) (bool, time.Time, time.Duration, error) {
provingWindow, err := getProvingWindow(e, tiers)
provingWindow, err := getProvingWindow(metadata.MinTier, tiers)
if err != nil {
return false, time.Time{}, 0, fmt.Errorf("failed to get proving window: %w", err)
}

var (
now = uint64(time.Now().Unix())
expiredAt = e.Meta.Timestamp + uint64(provingWindow.Seconds())
expiredAt = metadata.Timestamp + uint64(provingWindow.Seconds())
)

return now > expiredAt, time.Unix(int64(expiredAt), 0), time.Duration(expiredAt-now) * time.Second, nil
Expand Down