Skip to content

Commit f92b4f1

Browse files
YoGhurt111davidtaikochaclaudeDavid
authored
feat(taiko-client): add ZK-only RISC0 + SP1 proof mode (#21959)
Co-authored-by: David <david@taiko.xyz> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: David <cai@Davids-MacBook-Pro.local>
1 parent 4679d0d commit f92b4f1

17 files changed

Lines changed: 908 additions & 330 deletions

packages/taiko-client/cmd/flags/prover.go

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var (
1818
}
1919
RaikoHostEndpoint = &cli.StringFlag{
2020
Name: "raiko.host",
21-
Usage: "RPC endpoint of a Raiko host service for post Shasta fork",
21+
Usage: "RPC endpoint of the Raiko 2 host service for post Unzen fork",
2222
Required: true,
2323
Category: proverCategory,
2424
EnvVars: []string{"RAIKO_HOST"},
@@ -27,12 +27,6 @@ var (
2727

2828
// Optional flags used by prover.
2929
var (
30-
RaikoZKVMHostEndpoint = &cli.StringFlag{
31-
Name: "raiko.host.zkvm",
32-
Usage: "RPC endpoint of a Raiko ZKVM host service for post Shasta fork",
33-
Category: proverCategory,
34-
EnvVars: []string{"RAIKO_HOST_ZKVM"},
35-
}
3630
RaikoApiKeyPath = &cli.StringFlag{
3731
Name: "raiko.apiKeyPath",
3832
Usage: "Path to an Api key for the Raiko service",
@@ -85,12 +79,25 @@ var (
8579
ForceSP1Proof = &cli.BoolFlag{
8680
Name: "prover.forceSP1Proof",
8781
Usage: "Always request SP1 proofs from the ZKVM proof producer instead of trying RISC0 first. " +
88-
"If no ZKVM proof producer is configured, the prover keeps using the base proof producer. " +
89-
"Post Shasta fork only.",
82+
"Ignored when --prover.zkOnlyProofs is set. Post Shasta fork only.",
9083
Value: false,
9184
Category: proverCategory,
9285
EnvVars: []string{"PROVER_FORCE_SP1_PROOF"},
9386
}
87+
ZkOnlyProofs = &cli.BoolFlag{
88+
Name: "prover.zkOnlyProofs",
89+
Usage: "Prove every proposal with both RISC0 and SP1 proofs and submit the [RISC0, SP1] sub-proof pair, " +
90+
"instead of pairing a single ZK proof with an SGX_GETH proof. The " +
91+
"inbox's proof verifier must accept the [RISC0, SP1] pair — i.e. ZkRequiredVerifier, live with the " +
92+
"Unzen hardfork; on the pre-Unzen MainnetVerifier every submission reverts. " +
93+
"Intended for provers running without a TEE (sgx-geth) service or during SGX outages; " +
94+
"note that it generates proofs on both ZKVMs for every proposal. " +
95+
"When set, --prover.forceSP1Proof and --prover.maxRisc0ProofProposalDistance are ignored. " +
96+
"Post Shasta fork only.",
97+
Value: false,
98+
Category: proverCategory,
99+
EnvVars: []string{"PROVER_ZK_ONLY_PROOFS"},
100+
}
94101
// Special flags for testing.
95102
Dummy = &cli.BoolFlag{
96103
Name: "prover.dummy",
@@ -130,14 +137,6 @@ var (
130137
EnvVars: []string{"PROVER_FORCE_BATCH_PROVING_INTERVAL"},
131138
}
132139
// Batch proof related flag
133-
SGXBatchSize = &cli.Uint64Flag{
134-
Name: "prover.sgx.batchSize",
135-
Usage: "The default size of batch sgx proofs, when it arrives, submit a batch of proofs immediately, " +
136-
"this flag only works for proposal proof aggregation",
137-
Value: 1,
138-
Category: proverCategory,
139-
EnvVars: []string{"PROVER_SGX_BATCH_SIZE"},
140-
}
141140
ZKVMBatchSize = &cli.Uint64Flag{
142141
Name: "prover.zkvm.batchSize",
143142
Usage: "The size of batch ZKVM proof, when it arrives, submit a batch of proofs immediately, " +
@@ -164,11 +163,10 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{
164163
LocalProposerAddresses,
165164
BlockConfirmations,
166165
RaikoRequestTimeout,
167-
RaikoZKVMHostEndpoint,
168-
SGXBatchSize,
169166
ZKVMBatchSize,
170167
ForceBatchProvingInterval,
171168
ProposalWindowSize,
172169
MaxRisc0ProofProposalDistance,
173170
ForceSP1Proof,
171+
ZkOnlyProofs,
174172
}, opsigner.CLIFlags("PROVER", proverCategory), TxmgrFlags)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package flags
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestProverRaikoHostFlagRequired(t *testing.T) {
10+
require.True(t, RaikoHostEndpoint.Required)
11+
}

packages/taiko-client/prover/config.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,20 @@ type Config struct {
3636
RPCTimeout time.Duration
3737
ProveBatchesGasLimit uint64
3838
RaikoHostEndpoint string
39-
RaikoZKVMHostEndpoint string
4039
RaikoApiKey string
4140
RaikoRequestTimeout time.Duration
4241
LocalProposerAddresses []common.Address
4342
BlockConfirmations uint64
4443
TxmgrConfigs *txmgr.CLIConfig
4544
PrivateTxmgrConfigs *txmgr.CLIConfig
46-
SGXProofBufferSize uint64
4745
ZKVMProofBufferSize uint64
4846
ForceBatchProvingInterval time.Duration
4947
ProofPollingInterval time.Duration
5048
Dummy bool
5149
ProposalWindowSize uint64
5250
MaxRisc0ProofProposalDistance uint64
5351
ForceSP1Proof bool
52+
ZkOnlyProofs bool
5453
}
5554

5655
// NewConfigFromCliContext creates a new config instance from command line flags.
@@ -86,6 +85,15 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
8685
}
8786
}
8887

88+
// The Raiko endpoint is a required CLI flag. Keep explicit validation here for
89+
// callers that construct a CLI context without the application's flag validation.
90+
raikoHostEndpoint := strings.TrimSpace(c.String(flags.RaikoHostEndpoint.Name))
91+
if len(raikoHostEndpoint) == 0 {
92+
return nil, fmt.Errorf("--%s is required", flags.RaikoHostEndpoint.Name)
93+
}
94+
95+
zkOnlyProofs := c.Bool(flags.ZkOnlyProofs.Name)
96+
8997
var localProposerAddresses []common.Address
9098
for _, localProposerAddress := range c.StringSlice(flags.LocalProposerAddresses.Name) {
9199
if !common.IsHexAddress(localProposerAddress) {
@@ -106,8 +114,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
106114
InboxAddress: common.HexToAddress(c.String(flags.InboxAddress.Name)),
107115
TaikoAnchorAddress: common.HexToAddress(c.String(flags.TaikoAnchorAddress.Name)),
108116
L1ProverPrivKey: l1ProverPrivKey,
109-
RaikoHostEndpoint: c.String(flags.RaikoHostEndpoint.Name),
110-
RaikoZKVMHostEndpoint: c.String(flags.RaikoZKVMHostEndpoint.Name),
117+
RaikoHostEndpoint: raikoHostEndpoint,
111118
RaikoApiKey: strings.TrimSpace(string(raikoApiKey)),
112119
RaikoRequestTimeout: c.Duration(flags.RaikoRequestTimeout.Name),
113120
StartingProposalID: startingProposalID,
@@ -120,6 +127,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
120127
flags.MaxRisc0ProofProposalDistance.Name,
121128
),
122129
ForceSP1Proof: c.Bool(flags.ForceSP1Proof.Name),
130+
ZkOnlyProofs: zkOnlyProofs,
123131
RPCTimeout: c.Duration(flags.RPCTimeout.Name),
124132
ProveBatchesGasLimit: c.Uint64(flags.TxGasLimit.Name),
125133
LocalProposerAddresses: localProposerAddresses,
@@ -130,7 +138,6 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
130138
l1ProverPrivKey,
131139
c,
132140
),
133-
SGXProofBufferSize: c.Uint64(flags.SGXBatchSize.Name),
134141
ZKVMProofBufferSize: c.Uint64(flags.ZKVMBatchSize.Name),
135142
ForceBatchProvingInterval: c.Duration(flags.ForceBatchProvingInterval.Name),
136143
ProofPollingInterval: c.Duration(flags.ProofPollingInterval.Name),

packages/taiko-client/prover/config_test.go

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ func (s *ProverTestSuite) TestProverConfigShastaOnlySurface() {
3030
&cli.StringFlag{Name: flags.L1ProverPrivKey.Name},
3131
&cli.StringFlag{Name: flags.JWTSecret.Name},
3232
&cli.StringFlag{Name: flags.RaikoHostEndpoint.Name},
33-
&cli.StringFlag{Name: flags.RaikoZKVMHostEndpoint.Name},
3433
&cli.StringFlag{Name: flags.RaikoApiKeyPath.Name},
3534
&cli.DurationFlag{Name: flags.RaikoRequestTimeout.Name},
3635
}
@@ -44,7 +43,6 @@ func (s *ProverTestSuite) TestProverConfigShastaOnlySurface() {
4443
s.Equal(inbox.String(), c.InboxAddress.String())
4544
s.Equal(taikoAnchor.String(), c.TaikoAnchorAddress.String())
4645
s.Equal("http://raiko.host", c.RaikoHostEndpoint)
47-
s.Equal("http://raiko.zkvm", c.RaikoZKVMHostEndpoint)
4846
s.Equal("secret-key", c.RaikoApiKey)
4947
s.Equal(7*time.Second, c.RaikoRequestTimeout)
5048

@@ -64,7 +62,6 @@ func (s *ProverTestSuite) TestProverConfigShastaOnlySurface() {
6462
"--" + flags.L1ProverPrivKey.Name, encoding.GoldenTouchPrivKey,
6563
"--" + flags.JWTSecret.Name, os.Getenv("JWT_SECRET"),
6664
"--" + flags.RaikoHostEndpoint.Name, "http://raiko.host",
67-
"--" + flags.RaikoZKVMHostEndpoint.Name, "http://raiko.zkvm",
6865
"--" + flags.RaikoApiKeyPath.Name, tempAPIKey,
6966
"--" + flags.RaikoRequestTimeout.Name, "7s",
7067
}))
@@ -104,6 +101,42 @@ func TestNewConfigFromCliContextForceSP1Proof(t *testing.T) {
104101
})
105102
}
106103

104+
func TestNewConfigFromCliContextZkOnlyProofs(t *testing.T) {
105+
t.Run("uses default value", func(t *testing.T) {
106+
cfg := newTestConfigFromCLI(t)
107+
108+
require.False(t, cfg.ZkOnlyProofs)
109+
})
110+
111+
t.Run("uses flag value with the shared raiko host", func(t *testing.T) {
112+
cfg := newTestConfigFromCLI(
113+
t,
114+
"--"+flags.ZkOnlyProofs.Name,
115+
)
116+
117+
require.True(t, cfg.ZkOnlyProofs)
118+
require.Equal(t, "http://raiko.host", cfg.RaikoHostEndpoint)
119+
})
120+
}
121+
122+
func TestNewConfigFromCliContextRequiresRaikoHost(t *testing.T) {
123+
err := runTestConfigFromCLI(t, "--"+flags.RaikoHostEndpoint.Name, "")
124+
125+
require.ErrorContains(t, err, "--"+flags.RaikoHostEndpoint.Name)
126+
}
127+
128+
func TestNewConfigFromCliContextRejectsRemovedRaikoZKVMHost(t *testing.T) {
129+
err := runTestConfigFromCLI(t, "--raiko.host.zkvm", "http://raiko.zkvm")
130+
131+
require.ErrorContains(t, err, "flag provided but not defined")
132+
}
133+
134+
func TestNewConfigFromCliContextRejectsRemovedSGXBatchSize(t *testing.T) {
135+
err := runTestConfigFromCLI(t, "--prover.sgx.batchSize", "2")
136+
137+
require.ErrorContains(t, err, "flag provided but not defined")
138+
}
139+
107140
func (s *ProverTestSuite) TestNewConfigFromCliContextProverKeyError() {
108141
app := s.SetupApp()
109142

@@ -178,6 +211,8 @@ func runTestConfigFromCLIWithConfig(t *testing.T, cfg **Config, extraArgs ...str
178211
Value: flags.MaxRisc0ProofProposalDistance.Value,
179212
},
180213
&cli.BoolFlag{Name: flags.ForceSP1Proof.Name},
214+
&cli.BoolFlag{Name: flags.ZkOnlyProofs.Name},
215+
&cli.StringFlag{Name: flags.RaikoHostEndpoint.Name},
181216
}
182217

183218
app.Action = func(ctx *cli.Context) error {
@@ -194,6 +229,7 @@ func runTestConfigFromCLIWithConfig(t *testing.T, cfg **Config, extraArgs ...str
194229
"--" + flags.TaikoAnchorAddress.Name, common.HexToAddress("0x00000000000000000000000000000000000000bb").Hex(),
195230
"--" + flags.L1ProverPrivKey.Name, encoding.GoldenTouchPrivKey,
196231
"--" + flags.JWTSecret.Name, jwtSecret,
232+
"--" + flags.RaikoHostEndpoint.Name, "http://raiko.host",
197233
}
198234
args = append(args, extraArgs...)
199235

packages/taiko-client/prover/init.go

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,60 +19,50 @@ import (
1919
// initProofSubmitter initializes the proof submitter from the non-zero verifier addresses set in protocol.
2020
func (p *Prover) initProofSubmitter(ctx context.Context, txBuilder *transaction.ProveBatchesTxBuilder) error {
2121
var (
22-
// ZKVM proof producers.
23-
zkvmProducer producer.ProofProducer
24-
2522
// All activated proof types in protocol.
2623
proofTypes = make([]producer.ProofType, 0, proofSubmitter.MaxNumSupportedProofTypes)
2724

2825
// VerifierIDs
2926
sgxGethVerifierID uint8 = 1
30-
sgxRethVerifierID uint8 = 4
3127
risc0RethVerifierID uint8 = 5
3228
sp1RethVerifierID uint8 = 6
3329

3430
err error
3531
)
3632

37-
sgxGethProducer := &producer.SgxGethProofProducer{
38-
RaikoHostEndpoint: p.cfg.RaikoHostEndpoint,
39-
VerifierID: sgxGethVerifierID,
40-
ApiKey: p.cfg.RaikoApiKey,
41-
RaikoRequestTimeout: p.cfg.RaikoRequestTimeout,
42-
Dummy: p.cfg.Dummy,
33+
// A ZK-only prover can only finalize against a proof verifier that accepts the
34+
// [RISC0, SP1] sub-proof pair (ZkRequiredVerifier, live with the Unzen hardfork).
35+
// That is not checkable on-chain (areVerifiersSufficient is internal), so surface the
36+
// configured verifier loudly for the operator to check: on the pre-Unzen
37+
// MainnetVerifier every ZK-only submission reverts with CV_VERIFIERS_INSUFFICIENT.
38+
if p.cfg.ZkOnlyProofs {
39+
if inboxConfig, err := p.rpc.ShastaClients.Inbox.GetConfig(&bind.CallOpts{Context: ctx}); err != nil {
40+
log.Warn("ZK-only proof mode is enabled, but fetching the inbox's proof verifier failed", "error", err)
41+
} else {
42+
log.Warn(
43+
"ZK-only proof mode is enabled: the inbox's proof verifier must accept the [RISC0, SP1] "+
44+
"sub-proof pair (ZkRequiredVerifier, live with the Unzen hardfork), "+
45+
"otherwise every proof submission will revert",
46+
"proofVerifier", inboxConfig.ProofVerifier,
47+
)
48+
}
4349
}
44-
// Initialize the sgx proof producer.
45-
proofTypes = append(proofTypes, producer.ProofTypeSgx)
46-
sgxRethProducer := &producer.ComposeProofProducer{
47-
SgxGethProducer: sgxGethProducer,
48-
VerifierIDs: map[producer.ProofType]uint8{
49-
producer.ProofTypeSgx: sgxRethVerifierID,
50-
},
50+
51+
// Initialize the zk verifiers and zkvm proof producers.
52+
verifierIDs := map[producer.ProofType]uint8{
53+
producer.ProofTypeSgxGeth: sgxGethVerifierID,
54+
producer.ProofTypeZKR0: risc0RethVerifierID,
55+
producer.ProofTypeZKSP1: sp1RethVerifierID,
56+
}
57+
proofTypes = append(proofTypes, producer.ProofTypeZKR0, producer.ProofTypeZKSP1)
58+
59+
zkvmProducer := &producer.ComposeProofProducer{
60+
VerifierIDs: verifierIDs,
5161
RaikoHostEndpoint: p.cfg.RaikoHostEndpoint,
52-
ProofType: producer.ProofTypeSgx,
5362
ApiKey: p.cfg.RaikoApiKey,
5463
RaikoRequestTimeout: p.cfg.RaikoRequestTimeout,
5564
Dummy: p.cfg.Dummy,
5665
}
57-
58-
// Initialize the zk verifiers and zkvm proof producers.
59-
var zkVerifierIDs = make(map[producer.ProofType]uint8, proofSubmitter.MaxNumSupportedZkTypes)
60-
proofTypes = append(proofTypes, producer.ProofTypeZKR0)
61-
zkVerifierIDs[producer.ProofTypeZKR0] = risc0RethVerifierID
62-
proofTypes = append(proofTypes, producer.ProofTypeZKSP1)
63-
zkVerifierIDs[producer.ProofTypeZKSP1] = sp1RethVerifierID
64-
65-
if len(p.cfg.RaikoZKVMHostEndpoint) != 0 {
66-
zkvmProducer = &producer.ComposeProofProducer{
67-
VerifierIDs: zkVerifierIDs,
68-
SgxGethProducer: sgxGethProducer,
69-
RaikoHostEndpoint: p.cfg.RaikoZKVMHostEndpoint,
70-
ApiKey: p.cfg.RaikoApiKey,
71-
RaikoRequestTimeout: p.cfg.RaikoRequestTimeout,
72-
ProofType: producer.ProofTypeZKR0,
73-
Dummy: p.cfg.Dummy,
74-
}
75-
}
7666
// Init proof buffers.
7767
var (
7868
proofBuffers = make(map[producer.ProofType]*producer.ProofBuffer, proofSubmitter.MaxNumSupportedProofTypes)
@@ -86,8 +76,6 @@ func (p *Prover) initProofSubmitter(ctx context.Context, txBuilder *transaction.
8676
for _, proofType := range proofTypes {
8777
cacheMaps[proofType] = cmap.New[*producer.ProofResponse]()
8878
switch proofType {
89-
case producer.ProofTypeOp, producer.ProofTypeSgx:
90-
proofBuffers[proofType] = producer.NewProofBuffer(p.cfg.SGXProofBufferSize)
9179
case producer.ProofTypeZKR0, producer.ProofTypeZKSP1:
9280
proofBuffers[proofType] = producer.NewProofBuffer(p.cfg.ZKVMProofBufferSize)
9381
default:
@@ -97,7 +85,6 @@ func (p *Prover) initProofSubmitter(ctx context.Context, txBuilder *transaction.
9785

9886
if p.proofSubmitter, err = proofSubmitter.NewProofSubmitter(
9987
p.ctx,
100-
sgxRethProducer,
10188
zkvmProducer,
10289
p.batchProofGenerationCh,
10390
p.batchesAggregationNotify,
@@ -117,6 +104,7 @@ func (p *Prover) initProofSubmitter(ctx context.Context, txBuilder *transaction.
117104
new(big.Int).SetUint64(p.cfg.ProposalWindowSize),
118105
new(big.Int).SetUint64(p.cfg.MaxRisc0ProofProposalDistance),
119106
p.cfg.ForceSP1Proof,
107+
p.cfg.ZkOnlyProofs,
120108
); err != nil {
121109
return fmt.Errorf("failed to initialize proof submitter: %w", err)
122110
}

0 commit comments

Comments
 (0)