Skip to content

Commit

Permalink
feat: implement challenger proving fault with zkVM proof (#386)
Browse files Browse the repository at this point in the history
* feat(contracts): revive `getChallenge` method of `Colosseum`

* feat(validator): prove fault with witness generator, zkVM prover

* feat: verify zkVM program verification key

* fix: launch kroma-challenger in devnet

* chore(contracts): update bindings and snapshot

* feat: use rpc client for proof fetcher, witness generator
  • Loading branch information
seolaoh committed Nov 8, 2024
1 parent c7c4785 commit a820693
Show file tree
Hide file tree
Showing 23 changed files with 633 additions and 488 deletions.
44 changes: 22 additions & 22 deletions kroma-bindings/bindings/colosseum.go

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions kroma-bindings/bindings/colosseum_more.go

Large diffs are not rendered by default.

19 changes: 4 additions & 15 deletions kroma-bindings/bindings/types.go
100644 → 100755

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions kroma-bindings/bindings/zkproofverifier.go

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions kroma-bindings/bindings/zkproofverifier_more.go

Large diffs are not rendered by default.

35 changes: 21 additions & 14 deletions kroma-chain-ops/genesis/testdata/allocs-l1.json

Large diffs are not rendered by default.

6 changes: 1 addition & 5 deletions kroma-devnet/devnet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ def devnet_deploy(paths):
'--outfile.rollup', paths.rollup_config_path
], cwd=paths.op_node_dir)

rollup_config = read_json(paths.rollup_config_path)
addresses = read_json(paths.addresses_json_path)

# Start the L2.
Expand All @@ -266,8 +265,6 @@ def devnet_deploy(paths):
# Print out the addresses being used for easier debugging.
l2_output_oracle = addresses['L2OutputOracleProxy']
log.info(f'Using L2OutputOracle {l2_output_oracle}')
batch_inbox_address = rollup_config['batch_inbox_address']
log.info(f'Using batch inbox {batch_inbox_address}')
colosseum = addresses['ColosseumProxy']
log.info(f'Using Colosseum {colosseum}')
validator_pool = addresses['ValidatorPoolProxy']
Expand All @@ -278,14 +275,13 @@ def devnet_deploy(paths):
log.info(f'Using AssetManager {asset_manager}')

log.info('Bringing up `kroma-node`, `kroma-batcher` and `kroma-validator`.')
run_command(['docker', 'compose', 'up', '-d', 'kroma-node', 'kroma-batcher', 'kroma-validator'], cwd=paths.ops_bedrock_dir, env={
run_command(['docker', 'compose', 'up', '-d', 'kroma-node', 'kroma-batcher', 'kroma-validator', 'kroma-challenger'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir,
'L2OO_ADDRESS': l2_output_oracle,
'COLOSSEUM_ADDRESS': colosseum,
'VALPOOL_ADDRESS': validator_pool,
'VALMGR_ADDRESS': validator_manager,
'ASSETMANAGER_ADDRESS': asset_manager,
'SEQUENCER_BATCH_INBOX_ADDRESS': batch_inbox_address
})

log.info("Deposit ETH into ValidatorPool contract to be a validator...")
Expand Down
147 changes: 0 additions & 147 deletions kroma-validator/challenge/fetcher.go

This file was deleted.

114 changes: 114 additions & 0 deletions kroma-validator/challenge/proof_fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package challenge

import (
"context"
"encoding/hex"
"fmt"
"math/big"
"strings"

"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum/go-ethereum/common"
)

type ZkEVMProofFetcher struct {
rpc client.RPC
}

type ZkEVMProveResponse struct {
FinalPair []byte `json:"final_pair"`
Proof []byte `json:"proof"`
}

type ProofAndPair struct {
Proof []*big.Int
Pair []*big.Int
}

func NewZkEVMProofFetcher(rpc client.RPC) *ZkEVMProofFetcher {
return &ZkEVMProofFetcher{rpc}
}

func (z *ZkEVMProofFetcher) FetchProofAndPair(ctx context.Context, trace string) (*ProofAndPair, error) {
var output *ZkEVMProveResponse
err := z.rpc.CallContext(ctx, &output, "prove", trace)
if err != nil {
return nil, fmt.Errorf("failed to request prove: %w", err)
}

proofAndPair := &ProofAndPair{
Proof: decode(output.Proof),
Pair: decode(output.FinalPair),
}

return proofAndPair, nil
}

func decode(data []byte) []*big.Int {
result := make([]*big.Int, len(data)/32)

for i := 0; i < len(data)/32; i++ {
// The best is data is given in Big Endian.
for j := 0; j < 16; j++ {
data[i*32+j], data[i*32+31-j] = data[i*32+31-j], data[i*32+j]
}
result[i] = new(big.Int).SetBytes(data[i*32 : (i+1)*32])
}

return result
}

type ZkVMProofFetcher struct {
rpc client.RPC
}

type HexBytes []byte

type ZkVMProofResponse struct {
RequestStatus RequestStatusType `json:"request_status"`
VKeyHash common.Hash `json:"vkey_hash"`
RequestID string `json:"request_id"`
PublicValues HexBytes `json:"public_values"`
Proof HexBytes `json:"proof"`
}

func NewZkVMProofFetcher(rpc client.RPC) *ZkVMProofFetcher {
return &ZkVMProofFetcher{rpc}
}

func (z *ZkVMProofFetcher) Spec(ctx context.Context) (*SpecResponse, error) {
var output *SpecResponse
err := z.rpc.CallContext(ctx, &output, "spec")
return output, err
}

func (z *ZkVMProofFetcher) RequestProve(ctx context.Context, blockHash string, l1Head string, witness string) (*RequestStatusType, error) {
var output *RequestStatusType
err := z.rpc.CallContext(ctx, &output, "requestProve", blockHash, l1Head, witness)
return output, err
}

func (z *ZkVMProofFetcher) GetProof(ctx context.Context, blockHash string, l1Head string) (*ZkVMProofResponse, error) {
var output *ZkVMProofResponse
err := z.rpc.CallContext(ctx, &output, "getProof", blockHash, l1Head)
return output, err
}

// UnmarshalJSON handles the conversion from a hex string to a byte array.
func (h *HexBytes) UnmarshalJSON(data []byte) error {
// Remove quotes around the hex string
str := string(data)
if len(str) >= 2 && str[0] == '"' && str[len(str)-1] == '"' {
str = str[1 : len(str)-1]
}

// Decode the hex string to byte array
str = strings.TrimPrefix(str, "0x")
decoded, err := hex.DecodeString(str)
if err != nil {
return err
}

*h = decoded
return nil
}
55 changes: 55 additions & 0 deletions kroma-validator/challenge/witness_generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package challenge

import (
"context"

"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum/go-ethereum/common"
)

const (
RequestNone RequestStatusType = "None"
RequestProcessing RequestStatusType = "Processing"
RequestCompleted RequestStatusType = "Completed"
RequestFailed RequestStatusType = "Failed"
)

type WitnessGenerator struct {
rpc client.RPC
}

type SpecResponse struct {
Version string `json:"version"`
SP1Version string `json:"sp1_version"`
VKeyHash common.Hash `json:"vkey_hash"`
}

type RequestStatusType string

type WitnessResponse struct {
RequestStatus RequestStatusType `json:"status"`
VKeyHash common.Hash `json:"vkey_hash"`
Witness string `json:"witness"`
}

func NewWitnessGenerator(rpc client.RPC) *WitnessGenerator {
return &WitnessGenerator{rpc}
}

func (w *WitnessGenerator) Spec(ctx context.Context) (*SpecResponse, error) {
var output *SpecResponse
err := w.rpc.CallContext(ctx, &output, "spec")
return output, err
}

func (w *WitnessGenerator) RequestWitness(ctx context.Context, blockHash string, l1Head string) (*RequestStatusType, error) {
var output *RequestStatusType
err := w.rpc.CallContext(ctx, &output, "requestWitness", blockHash, l1Head)
return output, err
}

func (w *WitnessGenerator) GetWitness(ctx context.Context, blockHash string, l1Head string) (*WitnessResponse, error) {
var output *WitnessResponse
err := w.rpc.CallContext(ctx, &output, "getWitness", blockHash, l1Head)
return output, err
}
Loading

0 comments on commit a820693

Please sign in to comment.