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): support tier_zkvm_any #18721

Closed
wants to merge 1 commit into from
Closed
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
3 changes: 3 additions & 0 deletions packages/taiko-client/bindings/encoding/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ var (
TierSgxID uint16 = 200
TierZkVMRisc0ID uint16 = 250
TierZkVMSp1ID uint16 = 251
TierZkAnyID uint16 = 252
TierSgxAndZkVMID uint16 = 300
TierGuardianMinorityID uint16 = 900
TierGuardianMajorityID uint16 = 1000
TierZKVMRisc0 = "tier_zkvm_risc0"
TierZKVMSP1 = "tier_zkvm_sp1"
GoldenTouchPrivKey = "92954368afd3caa1f3ce3ead0069c1af414054aefe1ef9aeacc1bf426222ce38"
)

Expand Down
9 changes: 9 additions & 0 deletions packages/taiko-client/pkg/rpc/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,3 +953,12 @@ func (c *Client) calculateBaseFeeOntake(

return baseFeeInfo.Basefee, nil
}

// Resolve resolves the address corresponding to the name.
func (c *Client) Resolve(ctx context.Context, name string) (common.Address, error) {
address, err := c.TaikoL1.Resolve0(&bind.CallOpts{Context: ctx}, StringToBytes32(name), false)
if err != nil {
return common.Address{}, err
}
return address, nil
}
19 changes: 19 additions & 0 deletions packages/taiko-client/prover/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func (p *Prover) setApprovalAmount(ctx context.Context, contract common.Address)

// initProofSubmitters initializes the proof submitters from the given tiers in protocol.
func (p *Prover) initProofSubmitters(
ctx context.Context,
txBuilder *transaction.ProveBlockTxBuilder,
tiers []*rpc.TierProviderTierWithID,
) error {
Expand Down Expand Up @@ -132,6 +133,24 @@ func (p *Prover) initProofSubmitters(
RaikoRequestTimeout: p.cfg.RaikoRequestTimeout,
}
bufferSize = p.cfg.ZKVMProofBufferSize
case encoding.TierZkAnyID:
risc0Verifier, err := p.rpc.Resolve(ctx, encoding.TierZKVMRisc0)
if err != nil {
return err
}
sp1Verifier, err := p.rpc.Resolve(ctx, encoding.TierZKVMSP1)
if err != nil {
return err
}
producer = &proofProducer.ZKAnyProofProducer{
RaikoHostEndpoint: p.cfg.RaikoZKVMHostEndpoint,
JWT: p.cfg.RaikoJWT,
Dummy: p.cfg.Dummy,
RaikoRequestTimeout: p.cfg.RaikoRequestTimeout,
Risc0Verifier: risc0Verifier,
SP1Verifier: sp1Verifier,
}
bufferSize = 0
case encoding.TierGuardianMinorityID:
producer = proofProducer.NewGuardianProofProducer(encoding.TierGuardianMinorityID, p.cfg.EnableLivenessBondProof)
bufferSize = 0
Expand Down
326 changes: 326 additions & 0 deletions packages/taiko-client/prover/proof_producer/zk_any_producer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
package producer

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

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

"github.com/taikoxyz/taiko-mono/packages/taiko-client/bindings/encoding"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/bindings/metadata"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/internal/metrics"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc"
)

var (
ErrNotSupported = errors.New("currently not supported")
)

// ZKAnyProofProducer generates a ZK proof for the given block.
type ZKAnyProofProducer struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure but can we try to make something like this to reduce the code redundancy:

type ZKAnyProofProducer struct {
  spProducer *ZKvmProofProducer
  r0Producer *ZKvmProofProducer
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or make zkAny another option of the current ZKvmProofProducer, if its just API request body changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what raiko will design their api.
Currently code change is based on adding a new api without breaking change.

RaikoHostEndpoint string
RaikoRequestTimeout time.Duration
Risc0Verifier common.Address
SP1Verifier common.Address
JWT string // JWT provided by Raiko
Dummy bool
DummyProofProducer
}

// RequestProof implements the ProofProducer interface.
func (s *ZKAnyProofProducer) RequestProof(
ctx context.Context,
opts *ProofRequestOptions,
blockID *big.Int,
meta metadata.TaikoBlockMetaData,
header *types.Header,
requestAt time.Time,
) (*ProofWithHeader, error) {
log.Info(
"Request zk proof from raiko-host service",
"blockID", blockID,
"coinbase", meta.GetCoinbase(),
"hash", header.Hash(),
"time", time.Since(requestAt),
)

if s.Dummy {
return s.DummyProofProducer.RequestProof(opts, blockID, meta, header, s.Tier(), requestAt)
}

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

// TODO: count according to response
if s.ZKProofType == ZKProofTypeR0 {
metrics.ProverR0ProofGeneratedCounter.Add(1)
} else if s.ZKProofType == ZKProofTypeSP1 {
metrics.ProverSp1ProofGeneratedCounter.Add(1)
}

return &ProofWithHeader{
BlockID: blockID,
Header: header,
Meta: meta,
Proof: proof,
Opts: opts,
Tier: s.Tier(),
}, nil
}

// RequestCancel implements the ProofProducer interface to cancel the proof generating progress.
func (s *ZKAnyProofProducer) RequestCancel(
ctx context.Context,
opts *ProofRequestOptions,
) error {
return s.requestCancel(ctx, opts)
}

// Aggregate implements the ProofProducer interface to aggregate a batch of proofs.
func (s *ZKAnyProofProducer) Aggregate(
_ context.Context,
_ []*ProofWithHeader,
_ time.Time,
) (*BatchProofs, error) {
return nil, ErrNotSupported
}

// callProverDaemon keeps polling the proverd service to get the requested proof.
func (s *ZKAnyProofProducer) callProverDaemon(
ctx context.Context,
opts *ProofRequestOptions,
requestAt time.Time,
) ([]byte, error) {
var (
proof []byte
)

zkCtx, zkCancel := rpc.CtxWithTimeoutOrDefault(ctx, s.RaikoRequestTimeout)
defer zkCancel()

output, err := s.requestProof(zkCtx, opts)
if err != nil {
log.Error("Failed to request proof", "blockID", opts.BlockID, "error", err, "endpoint", s.RaikoHostEndpoint)
return nil, err
}

if output.Data.Status == ErrProofInProgress.Error() {
return nil, ErrProofInProgress
}
if output.Data.Status == StatusRegistered {
return nil, ErrRetry
}

if !opts.Compressed {
if len(output.Data.Proof.Proof) == 0 {
return nil, errEmptyProof
}
proof = common.Hex2Bytes(output.Data.Proof.Proof[2:])
}
log.Info(
"Proof generated",
"blockID", opts.BlockID,
"time", time.Since(requestAt),
"producer", "ZKAnyProofProducer",
)

// TODO: record generation time according to response
if s.ZKProofType == ZKProofTypeR0 {
metrics.ProverR0ProofGenerationTime.Set(float64(time.Since(requestAt).Seconds()))
} else if s.ZKProofType == ZKProofTypeSP1 {
metrics.ProverSP1ProofGenerationTime.Set(float64(time.Since(requestAt).Seconds()))
}

return proof, nil
}

// requestProof sends a RPC request to proverd to try to get the requested proof.
func (s *ZKAnyProofProducer) requestProof(
ctx context.Context,
opts *ProofRequestOptions,
) (*RaikoRequestProofBodyResponseV2, error) {
var (
reqBody RaikoRequestProofBody
recursion string
)
if opts.Compressed {
recursion = RecursionCompressed
} else {
recursion = RecursionPlonk
}
switch s.ZKProofType {
case ZKProofTypeSP1:
reqBody = RaikoRequestProofBody{
Type: s.ZKProofType,
Block: opts.BlockID,
Prover: opts.ProverAddress.Hex()[2:],
Graffiti: opts.Graffiti,
SP1: &SP1RequestProofBodyParam{
Recursion: recursion,
Prover: "network",
Verify: true,
},
}
default:
reqBody = RaikoRequestProofBody{
Type: s.ZKProofType,
Block: opts.BlockID,
Prover: opts.ProverAddress.Hex()[2:],
Graffiti: opts.Graffiti,
RISC0: &RISC0RequestProofBodyParam{
Bonsai: true,
Snark: true,
Profile: false,
ExecutionPo2: big.NewInt(20),
},
}
}

client := &http.Client{}

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

req, err := http.NewRequestWithContext(ctx, "POST", s.RaikoHostEndpoint+"/v2/proof", bytes.NewBuffer(jsonValue))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
if len(s.JWT) > 0 {
req.Header.Set("Authorization", "Bearer "+base64.StdEncoding.EncodeToString([]byte(s.JWT)))
}

log.Debug(
"Send zk any proof generation request",
"blockID", opts.BlockID,
"input", string(jsonValue),
)

res, err := client.Do(req)
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
}

log.Debug(
"Proof zk any generation output",
"blockID", opts.BlockID,
"output", string(resBytes),
)
var output RaikoRequestProofBodyResponseV2
if err := json.Unmarshal(resBytes, &output); err != nil {
return nil, err
}

if len(output.ErrorMessage) > 0 || len(output.Error) > 0 {
return nil, fmt.Errorf("failed to get zk any proof, err: %s, msg: %s, zkType: %s",
output.Error,
output.ErrorMessage,
)
}

return &output, nil
}

func (s *ZKAnyProofProducer) requestCancel(
ctx context.Context,
opts *ProofRequestOptions,
) error {
var (
reqBody RaikoRequestProofBody
recursion string
)
if opts.Compressed {
recursion = RecursionCompressed
} else {
recursion = RecursionPlonk
}
switch s.ZKProofType {
case ZKProofTypeSP1:
reqBody = RaikoRequestProofBody{
Type: s.ZKProofType,
Block: opts.BlockID,
Prover: opts.ProverAddress.Hex()[2:],
Graffiti: opts.Graffiti,
SP1: &SP1RequestProofBodyParam{
Recursion: recursion,
Prover: "network",
Verify: true,
},
}
default:
reqBody = RaikoRequestProofBody{
Type: s.ZKProofType,
Block: opts.BlockID,
Prover: opts.ProverAddress.Hex()[2:],
Graffiti: opts.Graffiti,
RISC0: &RISC0RequestProofBodyParam{
Bonsai: true,
Snark: true,
Profile: false,
ExecutionPo2: big.NewInt(20),
},
}
}

client := &http.Client{}

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

req, err := http.NewRequestWithContext(
ctx,
"POST",
s.RaikoHostEndpoint+"/v2/proof/cancel",
bytes.NewBuffer(jsonValue),
)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
if len(s.JWT) > 0 {
req.Header.Set("Authorization", "Bearer "+base64.StdEncoding.EncodeToString([]byte(s.JWT)))
}

res, err := client.Do(req)
if err != nil {
return err
}

defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return fmt.Errorf("failed to cancel requesting proof, statusCode: %d", res.StatusCode)
}

return nil
}

// Tier implements the ProofProducer interface.
func (s *ZKAnyProofProducer) Tier() uint16 {
return encoding.TierZkAnyID
}
2 changes: 1 addition & 1 deletion packages/taiko-client/prover/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func InitFromConfig(
}

// Proof submitters
if err := p.initProofSubmitters(txBuilder, tiers); err != nil {
if err := p.initProofSubmitters(ctx, txBuilder, tiers); err != nil {
return err
}

Expand Down
Loading