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

Commit

Permalink
feat(prover): introduce ProofContester
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaikocha committed Oct 11, 2023
1 parent d0952d4 commit c7cd1e9
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 42 deletions.
18 changes: 18 additions & 0 deletions pkg/rpc/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@ func SubscribeTransitionProved(
})
}

// SubscribeTransitionContested subscribes the protocol's TransitionContested events.
func SubscribeTransitionContested(
taikoL1 *bindings.TaikoL1Client,
ch chan *bindings.TaikoL1ClientTransitionContested,
) event.Subscription {
return SubscribeEvent("TransitionContested", func(ctx context.Context) (event.Subscription, error) {
sub, err := taikoL1.WatchTransitionContested(nil, ch, nil)
if err != nil {
log.Error("Create TaikoL1.TransitionContested subscription error", "error", err)
return nil, err
}

defer sub.Unsubscribe()

return waitSubErr(ctx, sub)
})
}

// SubscribeChainHead subscribes the new chain heads.
func SubscribeChainHead(
client *EthClient,
Expand Down
1 change: 1 addition & 0 deletions prover/proof_producer/guardian_producer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
type GuardianProofProducer struct{ *DummyProofProducer }

// RequestProof implements the ProofProducer interface.
// TODO: support returning `keccak256("RETURN_LIVENESS_BOND")` as proof.
func (g *GuardianProofProducer) RequestProof(
ctx context.Context,
opts *ProofRequestOptions,
Expand Down
42 changes: 36 additions & 6 deletions prover/proof_submitter/evidence/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import (
"encoding/binary"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"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"
"github.com/taikoxyz/taiko-client/pkg/rpc"
anchorTxValidator "github.com/taikoxyz/taiko-client/prover/anchor_tx_validator"
Expand All @@ -28,8 +32,8 @@ func NewBuilder(cli *rpc.Client, anchorTxValidator *anchorTxValidator.AnchorTxVa
}
}

// Build creates the evidence for the given L2 block proof.
func (a *Builder) Build(
// ForSubmission creates the evidence for the given L2 block proof.
func (b *Builder) ForSubmission(
ctx context.Context,
proofWithHeader *proofProducer.ProofWithHeader,
) (*encoding.BlockEvidence, error) {
Expand All @@ -49,7 +53,7 @@ func (a *Builder) Build(
)

// Get the corresponding L2 block.
block, err := a.rpc.L2.BlockByHash(ctx, header.Hash())
block, err := b.rpc.L2.BlockByHash(ctx, header.Hash())
if err != nil {
return nil, fmt.Errorf("failed to get L2 block with given hash %s: %w", header.Hash(), err)
}
Expand All @@ -60,12 +64,12 @@ func (a *Builder) Build(

// Validate TaikoL2.anchor transaction inside the L2 block.
anchorTx := block.Transactions()[0]
if err := a.anchorTxValidator.ValidateAnchorTx(ctx, anchorTx); err != nil {
if err := b.anchorTxValidator.ValidateAnchorTx(ctx, anchorTx); err != nil {
return nil, fmt.Errorf("invalid anchor transaction: %w", err)
}

// Get and validate this anchor transaction's receipt.
if _, err = a.anchorTxValidator.GetAndValidateAnchorTxReceipt(ctx, anchorTx); err != nil {
if _, err = b.anchorTxValidator.GetAndValidateAnchorTxReceipt(ctx, anchorTx); err != nil {
return nil, fmt.Errorf("failed to fetch anchor transaction receipt: %w", err)
}

Expand All @@ -74,7 +78,7 @@ func (a *Builder) Build(
ParentHash: proofWithHeader.Opts.ParentHash,
BlockHash: proofWithHeader.Opts.BlockHash,
SignalRoot: proofWithHeader.Opts.SignalRoot,
Graffiti: a.graffiti,
Graffiti: b.graffiti,
Tier: proofWithHeader.Tier,
Proof: proof,
}
Expand All @@ -90,6 +94,32 @@ func (a *Builder) Build(
return evidence, nil
}

// ForContest creates the evidence for contesting a L2 transition.
func (b *Builder) ForContest(
ctx context.Context,
header *types.Header,
l2SignalService common.Address,
event *bindings.TaikoL1ClientTransitionProved,
) (*encoding.BlockEvidence, error) {
signalRoot, err := b.rpc.GetStorageRoot(ctx, b.rpc.L2GethClient, l2SignalService, event.BlockId)
if err != nil {
return nil, fmt.Errorf("failed to get L2 signal service storage root: %w", err)
}
blockInfo, err := b.rpc.TaikoL1.GetBlock(&bind.CallOpts{Context: ctx}, event.BlockId.Uint64())
if err != nil {
return nil, err
}

return &encoding.BlockEvidence{
MetaHash: blockInfo.MetaHash,
ParentHash: event.ParentHash,
BlockHash: header.Hash(),
SignalRoot: signalRoot,
Tier: event.Tier,
Proof: []byte{},
}, nil
}

// uint16ToBytes converts an uint16 to bytes.
func uint16ToBytes(i uint16) []byte {
b := make([]byte, 2)
Expand Down
8 changes: 8 additions & 0 deletions prover/proof_submitter/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,11 @@ type Submitter interface {
Producer() proofProducer.ProofProducer
Tier() uint16
}

type Contester interface {
SubmitContest(
ctx context.Context,
blockProposedEvent *bindings.TaikoL1ClientBlockProposed,
transitionProvedEvent *bindings.TaikoL1ClientTransitionProved,
) error
}
141 changes: 141 additions & 0 deletions prover/proof_submitter/proof_contester.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package submitter

import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"math/big"
"strings"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/taikoxyz/taiko-client/bindings"
"github.com/taikoxyz/taiko-client/bindings/encoding"
"github.com/taikoxyz/taiko-client/pkg/rpc"
proofProducer "github.com/taikoxyz/taiko-client/prover/proof_producer"
"github.com/taikoxyz/taiko-client/prover/proof_submitter/evidence"
"github.com/taikoxyz/taiko-client/prover/proof_submitter/transaction"
)

var _ Contester = (*ProofContester)(nil)

// ProofContester is responsible for contesting wrong L2 transitions.
type ProofContester struct {
rpc *rpc.Client
evidenceBuilder *evidence.Builder
txBuilder *transaction.ProveBlockTxBuilder
txSender *transaction.Sender
l2SignalService common.Address
graffiti [32]byte
}

// NewProofContester creates a new ProofContester instance.
func NewProofContester(
rpcClient *rpc.Client,
proverPrivKey *ecdsa.PrivateKey,
proveBlockTxGasLimit *uint64,
txReplacementTipMultiplier uint64,
proveBlockMaxTxGasTipCap *big.Int,
submissionMaxRetry uint64,
retryInterval time.Duration,
waitReceiptTimeout time.Duration,
graffiti string,
) (*ProofContester, error) {
l2SignalService, err := rpcClient.TaikoL2.Resolve0(nil, rpc.StringToBytes32("signal_service"), false)
if err != nil {
return nil, err
}

var txGasLimit *big.Int
if proveBlockTxGasLimit != nil {
txGasLimit = new(big.Int).SetUint64(*proveBlockTxGasLimit)
}

return &ProofContester{
rpc: rpcClient,
evidenceBuilder: evidence.NewBuilder(rpcClient, nil, graffiti),
txBuilder: transaction.NewProveBlockTxBuilder(
rpcClient,
proverPrivKey,
txGasLimit,
proveBlockMaxTxGasTipCap,
new(big.Int).SetUint64(txReplacementTipMultiplier),
),
txSender: transaction.NewSender(rpcClient, retryInterval, &submissionMaxRetry, waitReceiptTimeout),
l2SignalService: l2SignalService,
graffiti: rpc.StringToBytes32(graffiti),
}, nil
}

// SubmitContest submits a taikoL1.proveBlock transaction to contest a L2 block transition.
func (c *ProofContester) SubmitContest(
ctx context.Context,
blockProposedEvent *bindings.TaikoL1ClientBlockProposed,
transitionProvedEvent *bindings.TaikoL1ClientTransitionProved,
) error {
// Ensure the transition has not been contested yet.
transition, err := c.rpc.TaikoL1.GetTransition(
&bind.CallOpts{Context: ctx},
transitionProvedEvent.BlockId.Uint64(),
transitionProvedEvent.ParentHash,
)
if err != nil {
if !strings.Contains(encoding.TryParsingCustomError(err).Error(), "L1_") {
log.Warn(
"Failed to get transition",
"blockID", transitionProvedEvent.BlockId,
"parentHash", transitionProvedEvent.ParentHash,
"error", encoding.TryParsingCustomError(err),
)
return nil
}
return err
}
if transition.Contester != (common.Address{}) {
log.Info(
"Transaction has already been contested",
"blockID", transitionProvedEvent.BlockId,
"parentHash", transitionProvedEvent.ParentHash,
"contester", transition.Contester,
)
return nil
}

header, err := c.rpc.L2.HeaderByNumber(ctx, transitionProvedEvent.BlockId)
if err != nil {
return err
}

// Genreate an evidence for contest.
evidence, err := c.evidenceBuilder.ForContest(ctx, header, c.l2SignalService, transitionProvedEvent)
if err != nil {
return err
}
input, err := encoding.EncodeEvidence(evidence)
if err != nil {
return fmt.Errorf("failed to encode TaikoL1.proveBlock inputs: %w", err)
}

if err := c.txSender.Send(
ctx,
&proofProducer.ProofWithHeader{
BlockID: transitionProvedEvent.BlockId,
Meta: &blockProposedEvent.Meta,
Header: header,
Proof: []byte{},
Opts: &proofProducer.ProofRequestOptions{EventL1Hash: blockProposedEvent.Raw.BlockHash},
Tier: transitionProvedEvent.Tier,
},
c.txBuilder.Build(ctx, transitionProvedEvent.BlockId, input),
); err != nil {
if errors.Is(err, transaction.ErrUnretryable) {
return nil
}

return err
}
return nil
}
2 changes: 1 addition & 1 deletion prover/proof_submitter/proof_submitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func (s *ProofSubmitter) SubmitProof(

metrics.ProverReceivedProofCounter.Inc(1)

evidence, err := s.evidenceBuilder.Build(ctx, proofWithHeader)
evidence, err := s.evidenceBuilder.ForSubmission(ctx, proofWithHeader)
if err != nil {
return fmt.Errorf("failed to create evidence: %w", err)
}
Expand Down
Loading

0 comments on commit c7cd1e9

Please sign in to comment.