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

Commit

Permalink
feat(prover): add oracle prover flag (#194)
Browse files Browse the repository at this point in the history
Co-authored-by: David <david@taiko.xyz>
  • Loading branch information
cyberhorsey and davidtaikocha authored Apr 27, 2023
1 parent 80620f8 commit ebbc725
Show file tree
Hide file tree
Showing 14 changed files with 253 additions and 31 deletions.
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_") {
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

0 comments on commit ebbc725

Please sign in to comment.