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

feat(prover): oracle prover #194

Merged
merged 20 commits into from
Apr 27, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions bindings/encoding/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,6 @@ func EncodeProposeBlockInput(metadataInput *TaikoL1BlockMetadataInput) ([]byte,
// EncodeProveBlockInput encodes the input params for TaikoL1.proveBlock.
func EncodeProveBlockInput(
evidence *TaikoL1Evidence,
anchorTx *types.Transaction,
anchorReceipt *types.Receipt,
) ([]byte, error) {
evidenceBytes, err := EncodeEvidence(evidence)
if err != nil {
Expand Down
9 changes: 0 additions & 9 deletions bindings/encoding/input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,6 @@ func TestEncodeProveBlockInput(t *testing.T) {
VerifierId: 1024,
Proof: randomHash().Big().Bytes(),
},
types.NewTransaction(
0,
common.BytesToAddress(randomHash().Bytes()),
common.Big0,
0,
common.Big0,
randomHash().Bytes(),
),
types.NewReceipt(randomHash().Bytes(), false, 1024),
)

require.Nil(t, err)
Expand Down
7 changes: 7 additions & 0 deletions cmd/flags/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ var (
"`lowerBound-upperBound` (e.g. `30m-1h`), testing purposes only",
Category: proverCategory,
}
OracleProver = &cli.BoolFlag{
Name: "oracleProver",
Usage: "Set whether prover should use oracle prover or not",
Category: proverCategory,
Value: false,
}
)

// All prover flags.
Expand All @@ -67,4 +73,5 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{
MaxConcurrentProvingJobs,
Dummy,
RandomDummyProofDelay,
OracleProver,
})
4 changes: 2 additions & 2 deletions driver/anchor_tx_constructor/anchor_tx_constructor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (s *AnchorTxConstructorTestSuite) TestAssembleAnchorTx() {
}

func (s *AnchorTxConstructorTestSuite) TestNewAnchorTransactor() {
godlenTouchAddress, err := s.RpcClient.TaikoL2.GOLDENTOUCHADDRESS(nil)
goldenTouchAddress, err := s.RpcClient.TaikoL2.GOLDENTOUCHADDRESS(nil)
s.Nil(err)

c, err := New(
Expand All @@ -58,7 +58,7 @@ func (s *AnchorTxConstructorTestSuite) TestNewAnchorTransactor() {
s.Nil(err)
s.Equal(true, opts.NoSend)
s.Equal(common.Big0, opts.Nonce)
s.Equal(godlenTouchAddress, opts.From)
s.Equal(goldenTouchAddress, opts.From)
s.Equal(common.Big256, opts.GasFeeCap)
s.Equal(common.Big0, opts.GasTipCap)
}
Expand Down
4 changes: 2 additions & 2 deletions prover/anchor_tx_validator/anchor_tx_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ func (s *AnchorTxValidatorTestSuite) TestValidateAnchorTx() {
wrongPrivKey, err := crypto.HexToECDSA("2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200")
s.Nil(err)

godlenTouchPrivKey, err := s.RpcClient.TaikoL2.GOLDENTOUCHPRIVATEKEY(nil)
goldenTouchPrivKey, err := s.RpcClient.TaikoL2.GOLDENTOUCHPRIVATEKEY(nil)
s.Nil(err)

// 0x92954368afd3caa1f3ce3ead0069c1af414054aefe1ef9aeacc1bf426222ce38
goldenTouchPriKey, err := crypto.HexToECDSA(common.Bytes2Hex(godlenTouchPrivKey.Bytes()))
goldenTouchPriKey, err := crypto.HexToECDSA(common.Bytes2Hex(goldenTouchPrivKey.Bytes()))
s.Nil(err)

// invalid To
Expand Down
2 changes: 2 additions & 0 deletions prover/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Config struct {
StartingBlockID *big.Int
MaxConcurrentProvingJobs uint
Dummy bool
OracleProver bool
RandomDummyProofDelayLowerBound *time.Duration
RandomDummyProofDelayUpperBound *time.Duration
}
Expand Down Expand Up @@ -87,6 +88,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
StartingBlockID: startingBlockID,
MaxConcurrentProvingJobs: c.Uint(flags.MaxConcurrentProvingJobs.Name),
Dummy: c.Bool(flags.Dummy.Name),
OracleProver: c.Bool(flags.OracleProver.Name),
RandomDummyProofDelayLowerBound: randomDummyProofDelayLowerBound,
RandomDummyProofDelayUpperBound: randomDummyProofDelayUpperBound,
}, nil
Expand Down
3 changes: 3 additions & 0 deletions prover/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func (s *ProverTestSuite) TestNewConfigFromCliContext() {
&cli.StringFlag{Name: flags.L1ProverPrivKey.Name},
&cli.BoolFlag{Name: flags.Dummy.Name},
&cli.StringFlag{Name: flags.RandomDummyProofDelay.Name},
&cli.BoolFlag{Name: flags.OracleProver.Name},
}
app.Action = func(ctx *cli.Context) error {
c, err := NewConfigFromCliContext(ctx)
Expand All @@ -46,6 +47,7 @@ func (s *ProverTestSuite) TestNewConfigFromCliContext() {
s.Equal(30*time.Minute, *c.RandomDummyProofDelayLowerBound)
s.Equal(time.Hour, *c.RandomDummyProofDelayUpperBound)
s.True(c.Dummy)
s.True(c.OracleProver)
s.Nil(new(Prover).InitFromCli(context.Background(), ctx))

return err
Expand All @@ -62,5 +64,6 @@ func (s *ProverTestSuite) TestNewConfigFromCliContext() {
"-" + flags.L1ProverPrivKey.Name, os.Getenv("L1_PROVER_PRIVATE_KEY"),
"-" + flags.Dummy.Name,
"-" + flags.RandomDummyProofDelay.Name, "30m-1h",
"-" + flags.OracleProver.Name,
}))
}
135 changes: 135 additions & 0 deletions prover/proof_producer/oracle_proof_producer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package producer

import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"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"
)

// OracleProducer is responsible for generating a fake "zkproof" consisting
// of a signature of the evidence.
type OracleProducer struct {
rpc *rpc.Client
proverPrivKey *ecdsa.PrivateKey
anchorTxValidator *anchorTxValidator.AnchorTxValidator
}

// NewOracleProducer creates a new NewOracleProducer instance.
func NewOracleProducer(
rpc *rpc.Client,
proverPrivKey *ecdsa.PrivateKey,
taikoL2Address common.Address,
) (*OracleProducer, error) {
anchorValidator, err := anchorTxValidator.New(taikoL2Address, rpc.L2ChainID, rpc)
if err != nil {
return nil, err
}

return &OracleProducer{rpc, proverPrivKey, anchorValidator}, nil
}

// RequestProof implements the ProofProducer interface.
func (d *OracleProducer) RequestProof(
ctx context.Context,
opts *ProofRequestOptions,
blockID *big.Int,
meta *bindings.TaikoDataBlockMetadata,
header *types.Header,
resultCh chan *ProofWithHeader,
) error {
log.Info(
"Request oracle proof",
"blockID", blockID,
"beneficiary", meta.Beneficiary,
"height", header.Number,
"hash", header.Hash(),
)

block, err := d.rpc.L2.BlockByHash(ctx, header.Hash())
if err != nil {
return fmt.Errorf("failed to get L2 block with given hash %s: %w", header.Hash(), err)
}

anchorTx := block.Transactions()[0]
if err := d.anchorTxValidator.ValidateAnchorTx(ctx, anchorTx); err != nil {
return fmt.Errorf("invalid anchor transaction: %w", err)
}

signalRoot, err := d.anchorTxValidator.GetAnchoredSignalRoot(ctx, anchorTx)
if err != nil {
return err
}

parent, err := d.rpc.L2.BlockByHash(ctx, block.ParentHash())
if err != nil {
return err
}

blockInfo, err := d.rpc.TaikoL1.GetBlock(nil, blockID)
if err != nil {
return err
}

// signature should be done with proof set to nil, verifierID set to 0,
// and prover set to 0 address.
evidence := &encoding.TaikoL1Evidence{
MetaHash: blockInfo.MetaHash,
ParentHash: block.ParentHash(),
BlockHash: block.Hash(),
SignalRoot: signalRoot,
Graffiti: [32]byte{},
Prover: common.HexToAddress("0x0000000000000000000000000000000000000000"),
ParentGasUsed: uint32(parent.GasUsed()),
GasUsed: uint32(block.GasUsed()),
VerifierId: 0,
Proof: nil,
}

proof, _, err := hashAndSignForOracleProof(evidence, d.proverPrivKey)
if err != nil {
return fmt.Errorf("failed to sign evidence: %w", err)
}

resultCh <- &ProofWithHeader{
BlockID: blockID,
Header: header,
Meta: meta,
ZkProof: proof,
}

return nil
}

// HashSignAndSetEvidenceForOracleProof hashes and signs the TaikoL1Evidence according to the
// protocol spec to generate an "oracle proof" via the signature and v value.
func hashAndSignForOracleProof(
evidence *encoding.TaikoL1Evidence,
privateKey *ecdsa.PrivateKey,
) ([]byte, uint8, error) {
inputToSign, err := encoding.EncodeProveBlockInput(evidence)
if err != nil {
return nil, 0, fmt.Errorf("failed to encode TaikoL1.proveBlock inputs: %w", err)
}

hashed := crypto.Keccak256Hash(inputToSign)

sig, err := crypto.Sign(hashed.Bytes(), privateKey)
if err != nil {
return nil, 0, fmt.Errorf("failed to sign TaikoL1Evidence: %w", err)
}

// add 27 to be able to be ecrecover in solidity
v := uint8(int(sig[64])) + 27

return sig, v, nil
}
62 changes: 62 additions & 0 deletions prover/proof_producer/oracle_proof_producer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package producer

import (
"crypto/ecdsa"
"crypto/elliptic"
"math/big"
"math/rand"
"os"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
"github.com/taikoxyz/taiko-client/bindings/encoding"
)

// randomHash generates a random blob of data and returns it as a hash.
func randomHash() common.Hash {
var hash common.Hash
if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil {
panic(err)
}
return hash
}

func TestHashAndSignOracleProof(t *testing.T) {
evidence := &encoding.TaikoL1Evidence{
MetaHash: randomHash(),
BlockHash: randomHash(),
ParentHash: randomHash(),
SignalRoot: randomHash(),
Graffiti: randomHash(),
Prover: common.BigToAddress(new(big.Int).SetUint64(rand.Uint64())),
ParentGasUsed: 1024,
GasUsed: 1024,
VerifierId: 0,
Proof: nil,
}

privateKey, err := crypto.HexToECDSA(os.Getenv("L1_PROVER_PRIVATE_KEY"))
require.Nil(t, err)

publicKey := privateKey.Public()

publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
require.True(t, ok)

input, err := encoding.EncodeProveBlockInput(evidence)
require.Nil(t, err)

hash := crypto.Keccak256Hash(input)

sig, _, err := hashAndSignForOracleProof(evidence, privateKey)
require.Nil(t, err)

pubKey, err := crypto.Ecrecover(hash.Bytes(), sig)
require.Nil(t, err)
isValid := crypto.VerifySignature(pubKey, hash.Bytes(), sig[:64])
require.True(t, isValid)

require.Equal(t, elliptic.Marshal(publicKeyECDSA, publicKeyECDSA.X, publicKeyECDSA.Y), pubKey)
}
2 changes: 1 addition & 1 deletion prover/proof_submitter/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var (
// isSubmitProofTxErrorRetryable checks whether the error returned by a proof submission transaction
// is retryable.
func isSubmitProofTxErrorRetryable(err error, blockID *big.Int) bool {
if strings.HasPrefix(err.Error(), "L1_NOT_ORACLE_PROVEN") || !strings.HasPrefix(err.Error(), "L1_") {
if strings.HasPrefix(err.Error(), "L1_NOT_ORACLE_PROVER") || !strings.HasPrefix(err.Error(), "L1_") {
davidtaikocha marked this conversation as resolved.
Show resolved Hide resolved
return true
}

Expand Down
2 changes: 1 addition & 1 deletion prover/proof_submitter/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

func (s *ProofSubmitterTestSuite) TestIsSubmitProofTxErrorRetryable() {
s.True(isSubmitProofTxErrorRetryable(errors.New(testAddr.String()), common.Big0))
s.True(isSubmitProofTxErrorRetryable(errors.New("L1_NOT_ORACLE_PROVEN"), common.Big0))
s.True(isSubmitProofTxErrorRetryable(errors.New("L1_NOT_ORACLE_PROVER"), common.Big0))
s.False(isSubmitProofTxErrorRetryable(errors.New("L1_DUP_PROVERS"), common.Big0))
s.False(isSubmitProofTxErrorRetryable(errors.New("L1_"+testAddr.String()), common.Big0))
}
Expand Down
Loading