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

feat(prover): support SGX prover with raiko-host #473

Merged
merged 5 commits into from
Dec 14, 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
7 changes: 7 additions & 0 deletions cmd/flags/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ var (
Required: true,
Category: proverCategory,
}
RaikoHostEndpoint = &cli.StringFlag{
Name: "raiko.hostEndpoint",
Usage: "RPC endpoint of a Raiko host service",
Required: true,
Category: proverCategory,
}
)

// Optional flags used by prover.
Expand Down Expand Up @@ -187,6 +193,7 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{
L2HTTPEndpoint,
ZkEvmRpcdEndpoint,
ZkEvmRpcdParamsPath,
RaikoHostEndpoint,
L1ProverPrivKey,
MinOptimisticTierFee,
MinSgxTierFee,
Expand Down
2 changes: 2 additions & 0 deletions prover/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type Config struct {
DatabaseCacheSize uint64
Allowance *big.Int
GuardianProverHealthCheckServerEndpoint *url.URL
RaikoHostEndpoint string
}

// NewConfigFromCliContext creates a new config instance from command line flags.
Expand Down Expand Up @@ -125,6 +126,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
L1ProverPrivKey: l1ProverPrivKey,
ZKEvmRpcdEndpoint: c.String(flags.ZkEvmRpcdEndpoint.Name),
ZkEvmRpcdParamsPath: c.String(flags.ZkEvmRpcdParamsPath.Name),
RaikoHostEndpoint: c.String(flags.RaikoHostEndpoint.Name),
StartingBlockID: startingBlockID,
Dummy: c.Bool(flags.Dummy.Name),
GuardianProverAddress: common.HexToAddress(c.String(flags.GuardianProver.Name)),
Expand Down
175 changes: 170 additions & 5 deletions prover/proof_producer/sgx_producer.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,201 @@
package producer

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
"net/http"
"time"

"github.com/cenkalti/backoff/v4"
"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/metrics"
)

// SGXProofProducer generates a SGX proof for the given block.
type SGXProofProducer struct{ *DummyProofProducer }
type SGXProofProducer struct {
RaikoHostEndpoint string // a proverd RPC endpoint
L1Endpoint string // a L1 node RPC endpoint
L2Endpoint string // a L2 execution engine's RPC endpoint
*DummyProofProducer
}

// SGXRequestProofBody represents the JSON body for requesting the proof.
type SGXRequestProofBody struct {
JsonRPC string `json:"jsonrpc"`
ID *big.Int `json:"id"`
Method string `json:"method"`
Params []*SGXRequestProofBodyParam `json:"params"`
}

// SGXRequestProofBody represents the JSON body of RequestProofBody's `param` field.
type SGXRequestProofBodyParam struct {
Type string `json:"type"`
Block *big.Int `json:"block"`
L2RPC string `json:"l2Rpc"`
L1RPC string `json:"l1Rpc"`
Prover string `json:"prover"`
Graffiti string `json:"graffiti"`
}

// SGXRequestProofBodyResponse represents the JSON body of the response of the proof requests.
type SGXRequestProofBodyResponse struct {
JsonRPC string `json:"jsonrpc"`
ID *big.Int `json:"id"`
Result *RaikoHostOutput `json:"result"`
Error *struct {
Code *big.Int `json:"code"`
Message string `json:"message"`
} `json:"error,omitempty"`
}

// RaikoHostOutput represents the JSON body of SGXRequestProofBodyResponse's `result` field.
type RaikoHostOutput struct {
Type string `json:"type"`
Proof string `json:"proof"`
}

// NewSGXProducer creates a new `SGXProofProducer` instance.
func NewSGXProducer(
raikoHostEndpoint string,
l1Endpoint string,
l2Endpoint string,
) (*SGXProofProducer, error) {
return &SGXProofProducer{
RaikoHostEndpoint: raikoHostEndpoint,
L1Endpoint: l1Endpoint,
L2Endpoint: l2Endpoint,
}, nil
}

// RequestProof implements the ProofProducer interface.
func (s *SGXProofProducer) RequestProof(
func (p *SGXProofProducer) RequestProof(
ctx context.Context,
opts *ProofRequestOptions,
blockID *big.Int,
meta *bindings.TaikoDataBlockMetadata,
header *types.Header,
resultCh chan *ProofWithHeader,
) error {
log.Warn(
"SGX proof producer is unimplemented, will return a dummy proof instead",
log.Info(
"Request proof from raiko-host service",
"blockID", blockID,
"coinbase", meta.Coinbase,
"height", header.Number,
"hash", header.Hash(),
)

return s.DummyProofProducer.RequestProof(ctx, opts, blockID, meta, header, s.Tier(), resultCh)
if p.DummyProofProducer != nil {
return p.DummyProofProducer.RequestProof(ctx, opts, blockID, meta, header, p.Tier(), resultCh)
}

proof, err := p.callProverDaemon(ctx, opts)
if err != nil {
return err
}

resultCh <- &ProofWithHeader{
BlockID: blockID,
Header: header,
Meta: meta,
Proof: proof,
Degree: 0,
Opts: opts,
Tier: p.Tier(),
}

return nil
}

// callProverDaemon keeps polling the proverd service to get the requested proof.
func (p *SGXProofProducer) callProverDaemon(ctx context.Context, opts *ProofRequestOptions) ([]byte, error) {
var (
proof []byte
start = time.Now()
)
if err := backoff.Retry(func() error {
if ctx.Err() != nil {
return nil
}
output, err := p.requestProof(opts)
if err != nil {
log.Error("Failed to request proof", "height", opts.BlockID, "err", err, "endpoint", p.RaikoHostEndpoint)
return err
}

if output == nil {
log.Info("Proof generating", "height", opts.BlockID, "time", time.Since(start))
return errProofGenerating
}

log.Debug("Proof generation output", "output", output)

proof = common.Hex2Bytes(output.Proof)
log.Info("Proof generated", "height", opts.BlockID, "time", time.Since(start))
return nil
}, backoff.NewConstantBackOff(proofPollingInterval)); err != nil {
return nil, err
}

metrics.ProverPseProofGenerationTime.Update(int64(time.Since(start).Seconds()))

return proof, nil
}

// requestProof sends a RPC request to proverd to try to get the requested proof.
func (p *SGXProofProducer) requestProof(opts *ProofRequestOptions) (*RaikoHostOutput, error) {
reqBody := SGXRequestProofBody{
JsonRPC: "2.0",
ID: common.Big1,
Method: "proof",
Params: []*SGXRequestProofBodyParam{{
Type: "Sgx",
Block: opts.BlockID,
L2RPC: p.L2Endpoint,
L1RPC: p.L1Endpoint,
Prover: opts.ProverAddress.Hex()[2:],
Graffiti: opts.Graffiti,
}},
}

jsonValue, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}

res, err := http.Post(p.RaikoHostEndpoint, "application/json", bytes.NewBuffer(jsonValue))
if err != nil {
return nil, err
}

defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to request proof, id: %d, statusCode: %d", opts.BlockID, res.StatusCode)
}

resBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}

var output SGXRequestProofBodyResponse
if err := json.Unmarshal(resBytes, &output); err != nil {
return nil, err
}

if output.Error != nil {
return nil, errors.New(output.Error.Message)
}

return output.Result, nil
}

// Tier implements the ProofProducer interface.
Expand Down
4 changes: 3 additions & 1 deletion prover/proof_producer/sgx_producer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import (
)

func TestSGXProducerRequestProof(t *testing.T) {
producer := &SGXProofProducer{}
producer := &SGXProofProducer{
DummyProofProducer: &DummyProofProducer{},
}

resCh := make(chan *ProofWithHeader, 1)

Expand Down
9 changes: 8 additions & 1 deletion prover/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,14 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) {
case encoding.TierOptimisticID:
producer = &proofProducer.OptimisticProofProducer{DummyProofProducer: new(proofProducer.DummyProofProducer)}
case encoding.TierSgxID:
producer = &proofProducer.SGXProofProducer{DummyProofProducer: new(proofProducer.DummyProofProducer)}
sgxProducer, err := proofProducer.NewSGXProducer(cfg.RaikoHostEndpoint, cfg.L1HttpEndpoint, cfg.L2HttpEndpoint)
if err != nil {
return err
}
if p.cfg.Dummy {
sgxProducer.DummyProofProducer = new(proofProducer.DummyProofProducer)
}
producer = sgxProducer
case encoding.TierSgxAndPseZkevmID:
zkEvmRpcdProducer, err := proofProducer.NewZkevmRpcdProducer(
cfg.ZKEvmRpcdEndpoint,
Expand Down