feat: add counter based ccc (#1040)

* add rollup/ccc/logger.go

* update Cargo.toml & Cargo.lock

* update miner/miner.go

* update miner/miner_test.go

* update eth/backend.go

* update `Message` type

* update interfaces.go

* update core/rawdb/accessors_row_consumption.go

* update evm.go

* update accounts/abi/bind/backends/simulated.go

* fix rollup/ccc/logger.go

* update core/state_processor.go

* update rollup/ccc/async_checker_test.go

* update rollup/ccc/async_checker.go

* scroll_worker: var

* scroll_worker: work

* scroll_worker: reorgTrigger

* scroll_worker: worker

* scroll_worker: newWorker

* scroll_worker: rm getCCC

* scroll_worker: checkHeadRowConsumption

* scroll_worker: mainLoop

* scroll_worker: updateSnapshot

* scroll_worker: newWork

* scroll_worker: handlePipelineResult

* scroll_worker: tryCommitNewWork

* scroll_worker: handleForks

* scroll_worker: processTxPool

* scroll_worker: processTxnSlice

* scroll_worker: processReorgedTxns

* scroll_worker: processTxns

* scroll_worker: processTxn

* scroll_worker: commit

* scroll_worker: onTxFailing

* scroll_worker: skipTransaction

* scroll_worker: forceTestErr, scheduleCCCError, skip, onBlockFailingCCC, handleReorg, isCanonical

* scroll_worker: fix 1

* scroll_worker: fix 2

* scroll_worker: fix 3

* scroll_worker: fix 4

* scroll_worker: fix 5

* scroll_worker: fix 6

* scroll_worker: fix 7

* fix: disable headCCCCheck for follower nodes

* feat: double check ancestor RowConsumption before committing

* feat: add miner idle metric

* fix: nil-check curRc

* fix: skip ccc.ErrUnknown txns immediately

* fix: increase keccak usage safety buffer

* fix: ignore RC check if force committing

* fix: properly handle wrapped retryable errors
This commit is contained in:
HAOYUatHZ 2024-09-17 16:29:11 +10:00 committed by GitHub
parent a6c654318b
commit e0ba374ff7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 1571 additions and 375 deletions

View file

@ -706,6 +706,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
AccessList: call.AccessList, AccessList: call.AccessList,
SkipAccountChecks: true, SkipAccountChecks: true,
IsL1MessageTx: false, IsL1MessageTx: false,
TxSize: 0,
} }
// Create a new environment which holds all relevant information // Create a new environment which holds all relevant information

View file

@ -1716,6 +1716,10 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
if ctx.IsSet(MinerMaxAccountsNumFlag.Name) { if ctx.IsSet(MinerMaxAccountsNumFlag.Name) {
cfg.MaxAccountsNum = ctx.Int(MinerMaxAccountsNumFlag.Name) cfg.MaxAccountsNum = ctx.Int(MinerMaxAccountsNumFlag.Name)
} }
cfg.CCCMaxWorkers = runtime.GOMAXPROCS(0)
if ctx.IsSet(CircuitCapacityCheckWorkersFlag.Name) {
cfg.CCCMaxWorkers = int(ctx.Uint(CircuitCapacityCheckWorkersFlag.Name))
}
} }
func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) { func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {

View file

@ -81,9 +81,12 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, chainConfig *p
func NewEVMTxContext(msg *Message) vm.TxContext { func NewEVMTxContext(msg *Message) vm.TxContext {
return vm.TxContext{ return vm.TxContext{
Origin: msg.From, Origin: msg.From,
To: msg.To,
GasPrice: new(big.Int).Set(msg.GasPrice), GasPrice: new(big.Int).Set(msg.GasPrice),
BlobHashes: msg.BlobHashes, BlobHashes: msg.BlobHashes,
To: msg.To,
IsL1MessageTx: msg.IsL1MessageTx,
TxSize: msg.TxSize,
} }
} }

View file

@ -25,6 +25,11 @@ func WriteBlockRowConsumption(db ethdb.KeyValueWriter, l2BlockHash common.Hash,
} }
} }
// DeleteBlockRowConsumption deletes a RowConsumption of the block from the database
func DeleteBlockRowConsumption(db ethdb.KeyValueWriter, l2BlockHash common.Hash) error {
return db.Delete(rowConsumptionKey(l2BlockHash))
}
// ReadBlockRowConsumption retrieves the RowConsumption corresponding to the block hash. // ReadBlockRowConsumption retrieves the RowConsumption corresponding to the block hash.
func ReadBlockRowConsumption(db ethdb.Reader, l2BlockHash common.Hash) *types.RowConsumption { func ReadBlockRowConsumption(db ethdb.Reader, l2BlockHash common.Hash) *types.RowConsumption {
data := ReadBlockRowConsumptionRLP(db, l2BlockHash) data := ReadBlockRowConsumptionRLP(db, l2BlockHash)

View file

@ -144,6 +144,11 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
// Apply the transaction to the current state (included in the env). // Apply the transaction to the current state (included in the env).
applyMessageStartTime := time.Now() applyMessageStartTime := time.Now()
result, err := ApplyMessage(evm, msg, gp, l1DataFee) result, err := ApplyMessage(evm, msg, gp, l1DataFee)
if evm.Config.Tracer.IsDebug() {
if erroringTracer, ok := evm.Config.Tracer.(interface{ Error() error }); ok {
err = errors.Join(err, erroringTracer.Error())
}
}
applyMessageTimer.Update(time.Since(applyMessageStartTime)) applyMessageTimer.Update(time.Since(applyMessageStartTime))
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -155,6 +155,7 @@ type Message struct {
// scroll-related fields // scroll-related fields
IsL1MessageTx bool IsL1MessageTx bool
TxSize uint64
} }
func (m *Message) GetFrom() common.Address { return m.From } func (m *Message) GetFrom() common.Address { return m.From }
@ -168,6 +169,7 @@ func (m *Message) GetNonce() uint64 { return m.Nonce }
func (m *Message) GetData() []byte { return m.Data } func (m *Message) GetData() []byte { return m.Data }
func (m *Message) GetAccessList() types.AccessList { return m.AccessList } func (m *Message) GetAccessList() types.AccessList { return m.AccessList }
func (m *Message) GetIsL1MessageTx() bool { return m.IsL1MessageTx } func (m *Message) GetIsL1MessageTx() bool { return m.IsL1MessageTx }
func (m *Message) GetTxSize() uint64 { return m.TxSize }
// TransactionToMessage converts a transaction into a Message. // TransactionToMessage converts a transaction into a Message.
func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) { func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) {
@ -185,6 +187,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
BlobHashes: tx.BlobHashes(), BlobHashes: tx.BlobHashes(),
BlobGasFeeCap: tx.BlobGasFeeCap(), BlobGasFeeCap: tx.BlobGasFeeCap(),
IsL1MessageTx: tx.IsL1MessageTx(), IsL1MessageTx: tx.IsL1MessageTx(),
TxSize: tx.Size(),
} }
// If baseFee provided, set gasPrice to effectiveGasPrice. // If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil { if baseFee != nil {

View file

@ -20,11 +20,11 @@ import (
"math/big" "math/big"
"sync/atomic" "sync/atomic"
"github.com/holiman/uint256"
"github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/crypto" "github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/params"
"github.com/holiman/uint256"
) )
type ( type (
@ -85,10 +85,13 @@ type BlockContext struct {
// All fields can change between transactions. // All fields can change between transactions.
type TxContext struct { type TxContext struct {
// Message information // Message information
Origin common.Address // Provides information for ORIGIN Origin common.Address // Provides information for ORIGIN
To *common.Address // Provides information for TO in trace GasPrice *big.Int // Provides information for GASPRICE
GasPrice *big.Int // Provides information for GASPRICE BlobHashes []common.Hash // Provides information for BLOBHASH
BlobHashes []common.Hash // Provides information for BLOBHASH
To *common.Address // Provides information for TO in trace
IsL1MessageTx bool // Provides information for trace
TxSize uint64 // Provides information for trace
} }
// EVM is the Ethereum Virtual Machine base object and provides // EVM is the Ethereum Virtual Machine base object and provides

View file

@ -41,4 +41,6 @@ type EVMLogger interface {
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
// Helper function
IsDebug() bool
} }

View file

@ -221,7 +221,7 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client sync_service.EthCl
return nil, err return nil, err
} }
if config.CheckCircuitCapacity { if config.CheckCircuitCapacity {
eth.asyncChecker = ccc.NewAsyncChecker(eth.blockchain, config.CCCMaxWorkers, true) eth.asyncChecker = ccc.NewAsyncChecker(eth.blockchain, config.CCCMaxWorkers, false)
eth.asyncChecker.WithOnFailingBlock(func(b *types.Block, err error) { eth.asyncChecker.WithOnFailingBlock(func(b *types.Block, err error) {
log.Warn("block failed CCC check, it will be reorged by the sequencer", "hash", b.Hash(), "err", err) log.Warn("block failed CCC check, it will be reorged by the sequencer", "hash", b.Hash(), "err", err)
}) })

View file

@ -327,6 +327,10 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad
} }
} }
func (t *jsTracer) IsDebug() bool {
return false
}
// CaptureExit is called when EVM exits a scope, even if the scope didn't // CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code. // execute any code.
func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) { func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {

View file

@ -185,3 +185,7 @@ func (a *AccessListTracer) AccessList() types.AccessList {
func (a *AccessListTracer) Equal(other *AccessListTracer) bool { func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
return a.list.equal(other.list) return a.list.equal(other.list)
} }
func (a *AccessListTracer) IsDebug() bool {
return false
}

View file

@ -420,6 +420,8 @@ func (l *StructLogger) UpdatedStorages() map[common.Address]Storage {
// CreatedAccount return the account data in case it is a create tx // CreatedAccount return the account data in case it is a create tx
func (l *StructLogger) CreatedAccount() *types.AccountWrapper { return l.createdAccount } func (l *StructLogger) CreatedAccount() *types.AccountWrapper { return l.createdAccount }
func (l *StructLogger) IsDebug() bool { return l.cfg.Debug }
// WriteTrace writes a formatted trace to the given writer // WriteTrace writes a formatted trace to the given writer
func WriteTrace(writer io.Writer, logs []StructLog) { func WriteTrace(writer io.Writer, logs []StructLog) {
for _, log := range logs { for _, log := range logs {

View file

@ -104,3 +104,7 @@ func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
func (l *JSONLogger) CaptureTxStart(gasLimit uint64) {} func (l *JSONLogger) CaptureTxStart(gasLimit uint64) {}
func (l *JSONLogger) CaptureTxEnd(restGas uint64) {} func (l *JSONLogger) CaptureTxEnd(restGas uint64) {}
func (l *JSONLogger) IsDebug() bool {
return l.cfg.Debug
}

View file

@ -115,6 +115,10 @@ func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to comm
t.store(input[0:4], len(input)-4) t.store(input[0:4], len(input)-4)
} }
func (t *fourByteTracer) IsDebug() bool {
return false
}
// GetResult returns the json-encoded nested list of call traces, and any // GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`). // error arising from the encoding or forceful termination (via `Stop`).
func (t *fourByteTracer) GetResult() (json.RawMessage, error) { func (t *fourByteTracer) GetResult() (json.RawMessage, error) {

View file

@ -259,6 +259,10 @@ func (t *CallTracer) CaptureTxEnd(restGas uint64) {
} }
} }
func (t *CallTracer) IsDebug() bool {
return false
}
// GetResult returns the json-encoded nested list of call traces, and any // GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`). // error arising from the encoding or forceful termination (via `Stop`).
func (t *CallTracer) GetResult() (json.RawMessage, error) { func (t *CallTracer) GetResult() (json.RawMessage, error) {

View file

@ -213,6 +213,10 @@ func (t *flatCallTracer) CaptureTxEnd(restGas uint64) {
t.tracer.CaptureTxEnd(restGas) t.tracer.CaptureTxEnd(restGas)
} }
func (t *flatCallTracer) IsDebug() bool {
return false
}
// GetResult returns an empty json object. // GetResult returns an empty json object.
func (t *flatCallTracer) GetResult() (json.RawMessage, error) { func (t *flatCallTracer) GetResult() (json.RawMessage, error) {
if len(t.tracer.callstack) < 1 { if len(t.tracer.callstack) < 1 {

View file

@ -122,6 +122,10 @@ func (t *MuxTracer) CaptureTxEnd(restGas uint64) {
} }
} }
func (t *MuxTracer) IsDebug() bool {
return false
}
// GetResult returns an empty json object. // GetResult returns an empty json object.
func (t *MuxTracer) GetResult() (json.RawMessage, error) { func (t *MuxTracer) GetResult() (json.RawMessage, error) {
resObject := make(map[string]json.RawMessage) resObject := make(map[string]json.RawMessage)

View file

@ -71,6 +71,8 @@ func (*noopTracer) CaptureTxStart(gasLimit uint64) {}
func (*noopTracer) CaptureTxEnd(restGas uint64) {} func (*noopTracer) CaptureTxEnd(restGas uint64) {}
func (*noopTracer) IsDebug() bool { return false }
// GetResult returns an empty json object. // GetResult returns an empty json object.
func (t *noopTracer) GetResult() (json.RawMessage, error) { func (t *noopTracer) GetResult() (json.RawMessage, error) {
return json.RawMessage(`{}`), nil return json.RawMessage(`{}`), nil

View file

@ -149,6 +149,7 @@ type CallMsg struct {
// scroll-related: // scroll-related:
// not need to have a `IsL1MessageTx` field, should always be false // not need to have a `IsL1MessageTx` field, should always be false
// not need to have a `TxSize` field, not known at the moment
} }
// A ContractCaller provides contract calls, essentially transactions that are executed by // A ContractCaller provides contract calls, essentially transactions that are executed by

View file

@ -60,6 +60,7 @@ type Config struct {
StoreSkippedTxTraces bool // Whether store the wrapped traces when storing a skipped tx StoreSkippedTxTraces bool // Whether store the wrapped traces when storing a skipped tx
MaxAccountsNum int // Maximum number of accounts that miner will fetch the pending transactions of when building a new block MaxAccountsNum int // Maximum number of accounts that miner will fetch the pending transactions of when building a new block
CCCMaxWorkers int // Maximum number of workers to use for async CCC tasks
} }
// DefaultConfig contains default settings for miner. // DefaultConfig contains default settings for miner.

View file

@ -308,6 +308,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) {
config := Config{ config := Config{
Etherbase: common.HexToAddress("123456789"), Etherbase: common.HexToAddress("123456789"),
MaxAccountsNum: math.MaxInt, MaxAccountsNum: math.MaxInt,
CCCMaxWorkers: 2,
} }
// Create chainConfig // Create chainConfig
chainDB := rawdb.NewMemoryDatabase() chainDB := rawdb.NewMemoryDatabase()

File diff suppressed because it is too large Load diff

View file

@ -71,6 +71,7 @@ var (
Recommit: time.Second, Recommit: time.Second,
GasCeil: params.GenesisGasLimit, GasCeil: params.GenesisGasLimit,
MaxAccountsNum: math.MaxInt, MaxAccountsNum: math.MaxInt,
CCCMaxWorkers: 2,
} }
) )

View file

@ -48,16 +48,21 @@ type AsyncChecker struct {
currentHead *types.Header currentHead *types.Header
forkCtx context.Context forkCtx context.Context
forkCtxCancelFunc context.CancelFunc forkCtxCancelFunc context.CancelFunc
// tests
blockNumberToFail uint64
txnIdxToFail uint64
} }
type ErrorWithTxnIdx struct { type ErrorWithTxnIdx struct {
txIdx uint TxIdx uint
err error err error
shouldSkip bool ShouldSkip bool
AccRc *types.RowConsumption
} }
func (e *ErrorWithTxnIdx) Error() string { func (e *ErrorWithTxnIdx) Error() string {
return fmt.Sprintf("txn at index %d failed with %s", e.txIdx, e.err) return fmt.Sprintf("txn at index %d failed with %s (rc = %s)", e.TxIdx, e.err, fmt.Sprint(e.AccRc))
} }
func (e *ErrorWithTxnIdx) Unwrap() error { func (e *ErrorWithTxnIdx) Unwrap() error {
@ -94,8 +99,10 @@ func (c *AsyncChecker) Wait() {
// Check spawns an async CCC verification task. // Check spawns an async CCC verification task.
func (c *AsyncChecker) Check(block *types.Block) error { func (c *AsyncChecker) Check(block *types.Block) error {
if block.NumberU64() > c.currentHead.Number.Uint64()+1 { if block.NumberU64() > c.currentHead.Number.Uint64()+1 {
log.Error("non continuous chain observed in AsyncChecker", "prev", c.currentHead, "got", block.Header()) log.Warn("non continuous chain observed in AsyncChecker", "prev", c.currentHead, "got", block.Header())
} else if block.ParentHash() != c.currentHead.Hash() { }
if block.ParentHash() != c.currentHead.Hash() {
// seems like there is a fork happening, a block from the canonical chain must have failed CCC check // seems like there is a fork happening, a block from the canonical chain must have failed CCC check
// assume the incoming block is the new tip in the fork // assume the incoming block is the new tip in the fork
c.forkCtx, c.forkCtxCancelFunc = context.WithCancel(context.Background()) c.forkCtx, c.forkCtxCancelFunc = context.WithCancel(context.Background())
@ -106,7 +113,11 @@ func (c *AsyncChecker) Check(block *types.Block) error {
// all blocks in the same fork share the same context to allow terminating them all at once if needed // all blocks in the same fork share the same context to allow terminating them all at once if needed
ctx, ctxCancelFunc := c.forkCtx, c.forkCtxCancelFunc ctx, ctxCancelFunc := c.forkCtx, c.forkCtxCancelFunc
c.workers.Go(func() stream.Callback { c.workers.Go(func() stream.Callback {
return c.checkerTask(block, checker, ctx, ctxCancelFunc) taskCb := c.checkerTask(block, checker, ctx, ctxCancelFunc)
return func() {
taskCb()
c.freeCheckers <- checker
}
}) })
return nil return nil
} }
@ -126,7 +137,6 @@ func (c *AsyncChecker) checkerTask(block *types.Block, ccc *Checker, forkCtx con
checkStart := time.Now() checkStart := time.Now()
defer func() { defer func() {
checkTimer.UpdateSince(checkStart) checkTimer.UpdateSince(checkStart)
c.freeCheckers <- ccc
activeWorkersGauge.Dec(1) activeWorkersGauge.Dec(1)
}() }()
@ -148,6 +158,15 @@ func (c *AsyncChecker) checkerTask(block *types.Block, ccc *Checker, forkCtx con
} }
} }
if c.blockNumberToFail == block.NumberU64() {
err = &ErrorWithTxnIdx{
TxIdx: uint(c.txnIdxToFail),
err: err,
}
c.blockNumberToFail = 0
return failingCallback
}
statedb, err := c.bc.StateAt(parent.Root()) statedb, err := c.bc.StateAt(parent.Root())
if err != nil { if err != nil {
return failingCallback return failingCallback
@ -158,7 +177,7 @@ func (c *AsyncChecker) checkerTask(block *types.Block, ccc *Checker, forkCtx con
gasPool := new(core.GasPool).AddGas(header.GasLimit) gasPool := new(core.GasPool).AddGas(header.GasLimit)
ccc.Reset() ccc.Reset()
var accRc *types.RowConsumption accRc := new(types.RowConsumption)
for txIdx, tx := range block.Transactions() { for txIdx, tx := range block.Transactions() {
if !isForkStillActive(forkCtx) { if !isForkStillActive(forkCtx) {
return noopCb return noopCb
@ -168,11 +187,12 @@ func (c *AsyncChecker) checkerTask(block *types.Block, ccc *Checker, forkCtx con
curRc, err = c.checkTxAndApply(parent, header, statedb, gasPool, tx, ccc) curRc, err = c.checkTxAndApply(parent, header, statedb, gasPool, tx, ccc)
if err != nil { if err != nil {
err = &ErrorWithTxnIdx{ err = &ErrorWithTxnIdx{
txIdx: uint(txIdx), TxIdx: uint(txIdx),
err: err, err: err,
// if the txn is the first in block or the additional resource utilization caused // if the txn is the first in block or the additional resource utilization caused
// by this txn alone is enough to overflow the circuit, skip // by this txn alone is enough to overflow the circuit, skip
shouldSkip: txIdx == 0 || curRc.Difference(*accRc).IsOverflown(), ShouldSkip: txIdx == 0 || curRc == nil || curRc.Difference(*accRc).IsOverflown(),
AccRc: curRc,
} }
return failingCallback return failingCallback
} }
@ -222,3 +242,9 @@ func (c *AsyncChecker) checkTxAndApply(parent *types.Block, header *types.Header
} }
return rc, nil return rc, nil
} }
// ScheduleError forces a block to error on a given transaction index
func (c *AsyncChecker) ScheduleError(blockNumber uint64, txnIndx uint64) {
c.blockNumberToFail = blockNumber
c.txnIdxToFail = txnIndx
}

View file

@ -78,6 +78,6 @@ func TestAsyncChecker(t *testing.T) {
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
require.Equal(t, reorgBlocks[3].Hash(), failingBlockHash) require.Equal(t, reorgBlocks[3].Hash(), failingBlockHash)
require.Equal(t, uint(3), errWithIdx.txIdx) require.Equal(t, uint(3), errWithIdx.TxIdx)
require.True(t, errWithIdx.shouldSkip) require.True(t, errWithIdx.ShouldSkip)
} }

View file

@ -31,7 +31,7 @@ dependencies = [
[[package]] [[package]]
name = "aggregator" name = "aggregator"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"ark-std 0.3.0", "ark-std 0.3.0",
"bitstream-io", "bitstream-io",
@ -537,7 +537,7 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]] [[package]]
name = "bus-mapping" name = "bus-mapping"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"eth-types", "eth-types",
"ethers-core", "ethers-core",
@ -1126,7 +1126,7 @@ dependencies = [
[[package]] [[package]]
name = "eth-types" name = "eth-types"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.13.1",
"ethers-core", "ethers-core",
@ -1283,7 +1283,7 @@ dependencies = [
[[package]] [[package]]
name = "external-tracer" name = "external-tracer"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"eth-types", "eth-types",
"geth-utils", "geth-utils",
@ -1465,7 +1465,7 @@ dependencies = [
[[package]] [[package]]
name = "gadgets" name = "gadgets"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"eth-types", "eth-types",
"halo2_proofs", "halo2_proofs",
@ -1488,7 +1488,7 @@ dependencies = [
[[package]] [[package]]
name = "geth-utils" name = "geth-utils"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"env_logger 0.10.0", "env_logger 0.10.0",
"gobuild", "gobuild",
@ -2237,7 +2237,7 @@ dependencies = [
[[package]] [[package]]
name = "mock" name = "mock"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"eth-types", "eth-types",
"ethers-core", "ethers-core",
@ -2252,7 +2252,7 @@ dependencies = [
[[package]] [[package]]
name = "mpt-zktrie" name = "mpt-zktrie"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"eth-types", "eth-types",
"halo2curves", "halo2curves",
@ -2635,17 +2635,18 @@ dependencies = [
[[package]] [[package]]
name = "poseidon-base" name = "poseidon-base"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/scroll-tech/poseidon-circuit.git?branch=main#7b96835c6201afdbfaf3d13d641efbaaf5db2d20" source = "git+https://github.com/scroll-tech/poseidon-circuit.git?branch=main#6cc36ab9dfa153f554ff7b84305f39838366a8df"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"halo2curves", "halo2curves",
"lazy_static", "lazy_static",
"once_cell",
] ]
[[package]] [[package]]
name = "poseidon-circuit" name = "poseidon-circuit"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/scroll-tech/poseidon-circuit.git?branch=main#7b96835c6201afdbfaf3d13d641efbaaf5db2d20" source = "git+https://github.com/scroll-tech/poseidon-circuit.git?branch=main#6cc36ab9dfa153f554ff7b84305f39838366a8df"
dependencies = [ dependencies = [
"ff", "ff",
"halo2_proofs", "halo2_proofs",
@ -2724,7 +2725,7 @@ dependencies = [
[[package]] [[package]]
name = "prover" name = "prover"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"aggregator", "aggregator",
"anyhow", "anyhow",
@ -4361,7 +4362,7 @@ dependencies = [
[[package]] [[package]]
name = "zkevm-circuits" name = "zkevm-circuits"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.4#38a68e22d3d8449bd39a50c22da55b9e741de453" source = "git+https://github.com/scroll-tech/zkevm-circuits.git?tag=v0.11.8#1f2f15c48edcd56ad05bad9dc04bfbec1ed36c05"
dependencies = [ dependencies = [
"array-init", "array-init",
"bus-mapping", "bus-mapping",
@ -4419,7 +4420,7 @@ dependencies = [
[[package]] [[package]]
name = "zktrie" name = "zktrie"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/scroll-tech/zktrie.git?branch=main#23181f209e94137f74337b150179aeb80c72e7c8" source = "git+https://github.com/scroll-tech/zktrie.git?branch=v0.7#23181f209e94137f74337b150179aeb80c72e7c8"
dependencies = [ dependencies = [
"gobuild", "gobuild",
"zktrie_rust", "zktrie_rust",
@ -4428,7 +4429,7 @@ dependencies = [
[[package]] [[package]]
name = "zktrie_rust" name = "zktrie_rust"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/scroll-tech/zktrie.git?branch=main#23181f209e94137f74337b150179aeb80c72e7c8" source = "git+https://github.com/scroll-tech/zktrie.git?branch=v0.7#23181f209e94137f74337b150179aeb80c72e7c8"
dependencies = [ dependencies = [
"hex", "hex",
"lazy_static", "lazy_static",

View file

@ -23,7 +23,7 @@ poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "main
bls12_381 = { git = "https://github.com/scroll-tech/bls12_381", branch = "feat/impl_scalar_field" } bls12_381 = { git = "https://github.com/scroll-tech/bls12_381", branch = "feat/impl_scalar_field" }
[dependencies] [dependencies]
prover = { git = "https://github.com/scroll-tech/zkevm-circuits.git", tag = "v0.11.4", default-features = false, features = ["parallel_syn", "scroll", "strict-ccc"] } prover = { git = "https://github.com/scroll-tech/zkevm-circuits.git", tag = "v0.11.8", default-features = false, features = ["parallel_syn", "scroll", "strict-ccc"] }
anyhow = "1.0" anyhow = "1.0"
base64 = "0.13.0" base64 = "0.13.0"

874
rollup/ccc/logger.go Normal file
View file

@ -0,0 +1,874 @@
package ccc
import (
"maps"
"math/big"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/core/vm"
"github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/eth/tracers"
"github.com/scroll-tech/go-ethereum/log"
)
const (
sigCountMax = 127
ecAddCountMax = 50
ecMulCountMax = 50
ecPairingCountMax = 2
rowUsageMax = 1_000_000
keccakRounds = 24
keccakRowsPerRound = 12
keccakRowsPerChunk = (keccakRounds + 1) * keccakRowsPerRound
)
var _ vm.EVMLogger = (*Logger)(nil)
// Logger is a tracer that keeps track of resource usages of each subcircuit
// that Scroll's halo2 based zkEVM has. Some subcircuits are not tracked
// here for the following reasons.
//
// rlp: worker already keeps track of how big a block is and the block size limit
// it uses is way below what the rlp circuit allows
// pi: row usage purely depends on the number txns and we already have a limit
// on how many txns that worker will pack in a block
// poseidon: not straight forward to track in block building phase. We can do
// worst case estimation down the line if needed.
// mpt: not straight forward to track in block building phase. We can do
// worst case estimation down the line if needed.
// tx: row usage depends on the length of raw txns and the number of storage
// slots and/or accounts accessed. With the current gas limit of 10M, it is not possible
// to overflow the circuit.
type Logger struct {
currentEnv *vm.EVM
isCreate bool
codesAccessed map[common.Hash]bool
evmUsage uint64
stateUsage uint64
bytecodeUsage uint64
sigCount uint64
ecAddCount uint64
ecMulCount uint64
ecPairingCount uint64
copyUsage uint64
sha256Usage uint64
expUsage uint64
modExpUsage uint64
keccakUsage uint64
l2TxnsRlpSize uint64
}
func NewLogger() *Logger {
const miscKeccakUsage = 100_000 // heuristically selected safe number to account for Rust side implementation details
const miscBytecodeUsage = 50_000 // to account for the inaccuracies in bytecode tracking
return &Logger{
codesAccessed: make(map[common.Hash]bool),
keccakUsage: miscKeccakUsage,
bytecodeUsage: miscBytecodeUsage,
}
}
// Snapshot creates an independent copy of the logger
func (l *Logger) Snapshot() *Logger {
newL := *l
newL.codesAccessed = maps.Clone(newL.codesAccessed)
newL.currentEnv = nil
return &newL
}
// logBytecodeAccess logs access to the bytecode identified by the given code hash
func (l *Logger) logBytecodeAccess(codeHash common.Hash, codeSize uint64) {
if codeHash != (common.Hash{}) && !l.codesAccessed[codeHash] {
l.bytecodeUsage += codeSize + 1
l.codesAccessed[codeHash] = true
}
}
// logBytecodeAccessAt logs access to the bytecode at the given addr
func (l *Logger) logBytecodeAccessAt(addr common.Address) {
codeHash := l.currentEnv.StateDB.GetKeccakCodeHash(addr)
l.logBytecodeAccess(codeHash, l.currentEnv.StateDB.GetCodeSize(addr))
}
// logRawBytecode logs access to a raw byte code
// useful for CREATE/CREATE2 flows
func (l *Logger) logRawBytecode(code []byte) {
l.logBytecodeAccess(crypto.Keccak256Hash(code), uint64(len(code)))
}
// computeKeccakRows computes the number of rows used in keccak256 for the given bytes array length
func computeKeccakRows(length uint64) uint64 {
return ((length + 135) / 136) * keccakRowsPerChunk
}
// logPrecompileAccess checks if the invoked address is a precompile and increments
// resource usage of associated subcircuit
func (l *Logger) logPrecompileAccess(to common.Address, inputLen uint64, inputFn func(int64, int64) ([]byte, error)) {
l.logCopy(inputLen)
var outputLen uint64
switch to {
case common.BytesToAddress([]byte{1}): // &ecrecover{},
l.sigCount++
l.keccakUsage += computeKeccakRows(64)
outputLen = 32
case common.BytesToAddress([]byte{2}): // &sha256hash{},
l.logSha256(inputLen)
outputLen = 32
case common.BytesToAddress([]byte{3}): // &ripemd160hashDisabled{},
case common.BytesToAddress([]byte{4}): // &dataCopy{},
outputLen = inputLen
case common.BytesToAddress([]byte{5}): // &bigModExp{eip2565: true},
const rowsPerModExpCall = 39962
l.modExpUsage += rowsPerModExpCall
if inputLen >= 96 {
input, err := inputFn(64, 32)
if err == nil {
outputLen = new(big.Int).SetBytes(input).Uint64() // mSize
}
}
case common.BytesToAddress([]byte{6}): // &bn256AddIstanbul{},
l.ecAddCount++
outputLen = 64
case common.BytesToAddress([]byte{7}): // &bn256ScalarMulIstanbul{},
l.ecMulCount++
outputLen = 64
case common.BytesToAddress([]byte{8}): // &bn256PairingIstanbul{},
l.ecPairingCount++
outputLen = 32
case common.BytesToAddress([]byte{9}): // &blake2FDisabled{},
}
l.logCopy(2 * outputLen)
}
// logCall logs call to a given address, regardless of the address being a precompile or not
func (l *Logger) logCall(to common.Address, inputLen uint64, inputFn func(int64, int64) ([]byte, error)) {
l.logBytecodeAccessAt(to)
l.logPrecompileAccess(to, inputLen, inputFn)
}
func (l *Logger) logCopy(len uint64) {
l.copyUsage += len * 2
}
func (l *Logger) logSha256(inputLen uint64) {
const blockRows = 2114
const blockSizeInBytes = 64
const minPaddingBytes = 9
numBlocks := (inputLen + minPaddingBytes + blockSizeInBytes - 1) / blockSizeInBytes
l.sha256Usage += numBlocks * blockRows
}
func (l *Logger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
l.currentEnv = env
l.isCreate = create
if !l.isCreate {
l.logCall(to, uint64(len(input)), func(argOffset, argLen int64) ([]byte, error) {
return input[argOffset : argOffset+argLen], nil
})
} else {
l.logRawBytecode(input) // init bytecode
}
if !env.TxContext.IsL1MessageTx {
l.sigCount++
l.l2TxnsRlpSize += uint64(env.TxContext.TxSize)
}
l.keccakUsage += computeKeccakRows(uint64(env.TxContext.TxSize))
l.keccakUsage += computeKeccakRows(64) // ecrecover per txn
}
func (l *Logger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
if err != nil {
return
}
l.evmUsage += evmUsagePerOpCode[op]
l.stateUsage += stateUsagePerOpCode[op](scope, depth)
getInputFn := func(inputOffset int64) func(int64, int64) ([]byte, error) {
return func(argOffset, argLen int64) ([]byte, error) {
input, err := tracers.GetMemoryCopyPadded(scope.Memory, inputOffset+argOffset, argLen)
if err != nil {
log.Warn("failed to read call input", "err", err)
}
return input, err
}
}
switch op {
case vm.EXTCODECOPY:
l.logBytecodeAccessAt(scope.Stack.Back(0).Bytes20())
l.logCopy(scope.Stack.Back(3).Uint64())
case vm.CALLDATACOPY, vm.RETURNDATACOPY, vm.CODECOPY, vm.MCOPY, vm.CREATE, vm.CREATE2:
l.logCopy(scope.Stack.Back(2).Uint64())
case vm.KECCAK256:
l.keccakUsage += computeKeccakRows(scope.Stack.Back(1).Uint64())
l.logCopy(scope.Stack.Back(1).Uint64())
case vm.LOG0, vm.LOG1, vm.LOG2, vm.LOG3, vm.LOG4, vm.RETURN, vm.REVERT:
l.logCopy(scope.Stack.Back(1).Uint64())
case vm.DELEGATECALL, vm.STATICCALL:
inputOffset := int64(scope.Stack.Back(2).Uint64())
inputLen := int64(scope.Stack.Back(3).Uint64())
l.logCall(scope.Stack.Back(1).Bytes20(), uint64(inputLen), getInputFn(inputOffset))
case vm.CALL, vm.CALLCODE:
inputOffset := int64(scope.Stack.Back(3).Uint64())
inputLen := int64(scope.Stack.Back(4).Uint64())
l.logCall(scope.Stack.Back(1).Bytes20(), uint64(inputLen), getInputFn(inputOffset))
case vm.EXP:
const rowsPerExpCall = 8
l.expUsage += rowsPerExpCall
}
}
func (l *Logger) CaptureStateAfter(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
if err != nil {
return
}
switch op {
case vm.CREATE, vm.CREATE2:
l.logBytecodeAccessAt(scope.Stack.Back(0).Bytes20()) // deployed bytecode
}
}
func (l *Logger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
switch typ {
case vm.CREATE, vm.CREATE2:
l.logRawBytecode(input) // init bytecode
}
}
func (l *Logger) CaptureExit(output []byte, gasUsed uint64, err error) {
}
func (l *Logger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
}
func (l *Logger) CaptureEnd(output []byte, gasUsed uint64, err error) {
if l.isCreate && err != nil {
l.logRawBytecode(output) // deployed bytecode
}
}
func (l *Logger) CaptureTxStart(gasLimit uint64) {
}
func (l *Logger) CaptureTxEnd(restGas uint64) {
}
func (l *Logger) IsDebug() bool {
return true
}
// Error returns an error if executed txns triggered an overflow
// Caller should revert some transactions and close the block
func (l *Logger) Error() error {
if l.RowConsumption().IsOverflown() {
return ErrBlockRowConsumptionOverflow
}
return nil
}
// RowConsumption returns the accumulated resource utilization for each subcircuit so far
func (l *Logger) RowConsumption() types.RowConsumption {
return types.RowConsumption{
{
Name: "evm",
RowNumber: l.evmUsage,
}, {
Name: "state",
RowNumber: l.stateUsage,
}, {
Name: "bytecode",
RowNumber: l.bytecodeUsage,
}, {
Name: "sig",
RowNumber: uint64(rowUsageMax * (float64(l.sigCount) / sigCountMax)),
}, {
Name: "ecc",
RowNumber: max(
// multiply with types.RowConsumptionLimit here, confidence factor is 1.0 on rust side
uint64(types.RowConsumptionLimit*(float64(l.ecAddCount)/ecAddCountMax)),
uint64(types.RowConsumptionLimit*(float64(l.ecMulCount)/ecMulCountMax)),
uint64(types.RowConsumptionLimit*(float64(l.ecPairingCount)/ecPairingCountMax)),
),
}, {
Name: "copy",
RowNumber: l.copyUsage,
}, {
Name: "sha256",
RowNumber: l.sha256Usage,
}, {
Name: "exp",
RowNumber: l.expUsage,
}, {
Name: "mod_exp",
RowNumber: l.modExpUsage,
}, {
Name: "keccak",
RowNumber: l.keccakUsage + computeKeccakRows(l.l2TxnsRlpSize),
},
}
}
// ForceError makes sure to trigger an error on the next step, should only be used in tests
func (l *Logger) ForceError() {
l.evmUsage += types.RowConsumptionLimit + 1
l.stateUsage += types.RowConsumptionLimit + 1
l.bytecodeUsage += types.RowConsumptionLimit + 1
}
// evm circuit resource usage per OpCode
var evmUsagePerOpCode = [256]uint64{
2, // STOP (0)
3, // ADD (1)
4, // MUL (2)
3, // SUB (3)
4, // DIV (4)
10, // SDIV (5)
4, // MOD (6)
10, // SMOD (7)
9, // ADDMOD (8)
10, // MULMOD (9)
3, // EXP (10)
2, // SIGNEXTEND (11)
0, // UNDEFINED (12)
0, // UNDEFINED (13)
0, // UNDEFINED (14)
0, // UNDEFINED (15)
3, // LT (16)
3, // GT (17)
3, // SLT (18)
3, // SGT (19)
3, // EQ (20)
1, // ISZERO (21)
4, // AND (22)
4, // OR (23)
4, // XOR (24)
4, // NOT (25)
2, // BYTE (26)
5, // SHL (27)
5, // SHR (28)
5, // SAR (29)
0, // UNDEFINED (30)
0, // UNDEFINED (31)
2, // SHA3 (32)
0, // UNDEFINED (33)
0, // UNDEFINED (34)
0, // UNDEFINED (35)
0, // UNDEFINED (36)
0, // UNDEFINED (37)
0, // UNDEFINED (38)
0, // UNDEFINED (39)
0, // UNDEFINED (40)
0, // UNDEFINED (41)
0, // UNDEFINED (42)
0, // UNDEFINED (43)
0, // UNDEFINED (44)
0, // UNDEFINED (45)
0, // UNDEFINED (46)
0, // UNDEFINED (47)
1, // ADDRESS (48)
2, // BALANCE (49)
1, // ORIGIN (50)
1, // CALLER (51)
1, // CALLVALUE (52)
8, // CALLDATALOAD (53)
1, // CALLDATASIZE (54)
2, // CALLDATACOPY (55)
2, // CODESIZE (56)
2, // CODECOPY (57)
1, // GASPRICE (58)
2, // EXTCODESIZE (59)
3, // EXTCODECOPY (60)
1, // RETURNDATASIZE (61)
4, // RETURNDATACOPY (62)
1, // EXTCODEHASH (63)
3, // BLOCKHASH (64)
1, // COINBASE (65)
1, // TIMESTAMP (66)
1, // NUMBER (67)
1, // DIFFICULTY (68)
1, // GASLIMIT (69)
1, // CHAINID (70)
1, // SELFBALANCE (71)
1, // BASEFEE (72)
0, // UNDEFINED (73)
0, // UNDEFINED (74)
0, // UNDEFINED (75)
0, // UNDEFINED (76)
0, // UNDEFINED (77)
0, // UNDEFINED (78)
0, // UNDEFINED (79)
1, // POP (80)
5, // MLOAD (81)
5, // MSTORE (82)
5, // MSTORE8 (83)
2, // SLOAD (84)
3, // SSTORE (85)
2, // JUMP (86)
2, // JUMPI (87)
1, // PC (88)
1, // MSIZE (89)
1, // GAS (90)
1, // JUMPDEST (91)
2, // TLOAD (92)
3, // TSTORE (93)
2, // MCOPY (94)
1, // PUSH0 (95)
1, // PUSH1 (96)
1, // PUSH2 (97)
1, // PUSH3 (98)
1, // PUSH4 (99)
1, // PUSH5 (100)
1, // PUSH6 (101)
1, // PUSH7 (102)
1, // PUSH8 (103)
1, // PUSH9 (104)
1, // PUSH10 (105)
1, // PUSH11 (106)
1, // PUSH12 (107)
1, // PUSH13 (108)
1, // PUSH14 (109)
1, // PUSH15 (110)
1, // PUSH16 (111)
1, // PUSH17 (112)
1, // PUSH18 (113)
1, // PUSH19 (114)
1, // PUSH20 (115)
1, // PUSH21 (116)
1, // PUSH22 (117)
1, // PUSH23 (118)
1, // PUSH24 (119)
1, // PUSH25 (120)
1, // PUSH26 (121)
1, // PUSH27 (122)
1, // PUSH28 (123)
1, // PUSH29 (124)
1, // PUSH30 (125)
1, // PUSH31 (126)
1, // PUSH32 (127)
1, // DUP1 (128)
1, // DUP2 (129)
1, // DUP3 (130)
1, // DUP4 (131)
1, // DUP5 (132)
1, // DUP6 (133)
1, // DUP7 (134)
1, // DUP8 (135)
1, // DUP9 (136)
1, // DUP10 (137)
1, // DUP11 (138)
1, // DUP12 (139)
1, // DUP13 (140)
1, // DUP14 (141)
1, // DUP15 (142)
1, // DUP16 (143)
1, // SWAP1 (144)
1, // SWAP2 (145)
1, // SWAP3 (146)
1, // SWAP4 (147)
1, // SWAP5 (148)
1, // SWAP6 (149)
1, // SWAP7 (150)
1, // SWAP8 (151)
1, // SWAP9 (152)
1, // SWAP10 (153)
1, // SWAP11 (154)
1, // SWAP12 (155)
1, // SWAP13 (156)
1, // SWAP14 (157)
1, // SWAP15 (158)
1, // SWAP16 (159)
2, // LOG0 (160)
2, // LOG1 (161)
2, // LOG2 (162)
2, // LOG3 (163)
2, // LOG4 (164)
0, // UNDEFINED (165)
0, // UNDEFINED (166)
0, // UNDEFINED (167)
0, // UNDEFINED (168)
0, // UNDEFINED (169)
0, // UNDEFINED (170)
0, // UNDEFINED (171)
0, // UNDEFINED (172)
0, // UNDEFINED (173)
0, // UNDEFINED (174)
0, // UNDEFINED (175)
0, // UNDEFINED (176)
0, // UNDEFINED (177)
0, // UNDEFINED (178)
0, // UNDEFINED (179)
0, // UNDEFINED (180)
0, // UNDEFINED (181)
0, // UNDEFINED (182)
0, // UNDEFINED (183)
0, // UNDEFINED (184)
0, // UNDEFINED (185)
0, // UNDEFINED (186)
0, // UNDEFINED (187)
0, // UNDEFINED (188)
0, // UNDEFINED (189)
0, // UNDEFINED (190)
0, // UNDEFINED (191)
0, // UNDEFINED (192)
0, // UNDEFINED (193)
0, // UNDEFINED (194)
0, // UNDEFINED (195)
0, // UNDEFINED (196)
0, // UNDEFINED (197)
0, // UNDEFINED (198)
0, // UNDEFINED (199)
0, // UNDEFINED (200)
0, // UNDEFINED (201)
0, // UNDEFINED (202)
0, // UNDEFINED (203)
0, // UNDEFINED (204)
0, // UNDEFINED (205)
0, // UNDEFINED (206)
0, // UNDEFINED (207)
0, // UNDEFINED (208)
0, // UNDEFINED (209)
0, // UNDEFINED (210)
0, // UNDEFINED (211)
0, // UNDEFINED (212)
0, // UNDEFINED (213)
0, // UNDEFINED (214)
0, // UNDEFINED (215)
0, // UNDEFINED (216)
0, // UNDEFINED (217)
0, // UNDEFINED (218)
0, // UNDEFINED (219)
0, // UNDEFINED (220)
0, // UNDEFINED (221)
0, // UNDEFINED (222)
0, // UNDEFINED (223)
0, // UNDEFINED (224)
0, // UNDEFINED (225)
0, // UNDEFINED (226)
0, // UNDEFINED (227)
0, // UNDEFINED (228)
0, // UNDEFINED (229)
0, // UNDEFINED (230)
0, // UNDEFINED (231)
0, // UNDEFINED (232)
0, // UNDEFINED (233)
0, // UNDEFINED (234)
0, // UNDEFINED (235)
0, // UNDEFINED (236)
0, // UNDEFINED (237)
0, // UNDEFINED (238)
0, // UNDEFINED (239)
9, // CREATE (240)
12, // CALL (241)
12, // CALLCODE (242)
4, // RETURN (243)
12, // DELEGATECALL (244)
9, // CREATE2 (245)
0, // UNDEFINED (246)
0, // UNDEFINED (247)
0, // UNDEFINED (248)
0, // UNDEFINED (249)
12, // STATICCALL (250)
0, // UNDEFINED (251)
0, // UNDEFINED (252)
4, // REVERT (253)
0, // INVALID (254)
0, // SELFDESTRUCT (255)
}
func constantStateUsage(usage uint64) func(*vm.ScopeContext, int) uint64 {
return func(_ *vm.ScopeContext, _ int) uint64 {
return usage
}
}
func logStateUsage(size uint64) func(*vm.ScopeContext, int) uint64 {
return func(scope *vm.ScopeContext, _ int) uint64 {
return 2*(scope.Stack.Back(1).Uint64()/32) + 7 + 2*size
}
}
// state circuit resource usage per OpCode
var stateUsagePerOpCode = [256]func(*vm.ScopeContext, int) uint64{
constantStateUsage(13), // STOP (0)
constantStateUsage(3), // ADD (1)
constantStateUsage(3), // MUL (2)
constantStateUsage(3), // SUB (3)
constantStateUsage(3), // DIV (4)
constantStateUsage(3), // SDIV (5)
constantStateUsage(3), // MOD (6)
constantStateUsage(3), // SMOD (7)
constantStateUsage(4), // ADDMOD (8)
constantStateUsage(4), // MULMOD (9)
constantStateUsage(3), // EXP (10)
constantStateUsage(3), // SIGNEXTEND (11)
constantStateUsage(0), // UNDEFINED (12)
constantStateUsage(0), // UNDEFINED (13)
constantStateUsage(0), // UNDEFINED (14)
constantStateUsage(0), // UNDEFINED (15)
constantStateUsage(3), // LT (16)
constantStateUsage(3), // GT (17)
constantStateUsage(3), // SLT (18)
constantStateUsage(3), // SGT (19)
constantStateUsage(3), // EQ (20)
constantStateUsage(2), // ISZERO (21)
constantStateUsage(3), // AND (22)
constantStateUsage(3), // OR (23)
constantStateUsage(3), // XOR (24)
constantStateUsage(2), // NOT (25)
constantStateUsage(3), // BYTE (26)
constantStateUsage(3), // SHL (27)
constantStateUsage(3), // SHR (28)
constantStateUsage(3), // SAR (29)
constantStateUsage(0), // UNDEFINED (30)
constantStateUsage(0), // UNDEFINED (31)
func(scope *vm.ScopeContext, _ int) uint64 {
// let n = # bytes, then row_consumption = (n/32) + 3
return scope.Stack.Back(1).Uint64()/32 + 3
}, // SHA3 (32)
constantStateUsage(0), // UNDEFINED (33)
constantStateUsage(0), // UNDEFINED (34)
constantStateUsage(0), // UNDEFINED (35)
constantStateUsage(0), // UNDEFINED (36)
constantStateUsage(0), // UNDEFINED (37)
constantStateUsage(0), // UNDEFINED (38)
constantStateUsage(0), // UNDEFINED (39)
constantStateUsage(0), // UNDEFINED (40)
constantStateUsage(0), // UNDEFINED (41)
constantStateUsage(0), // UNDEFINED (42)
constantStateUsage(0), // UNDEFINED (43)
constantStateUsage(0), // UNDEFINED (44)
constantStateUsage(0), // UNDEFINED (45)
constantStateUsage(0), // UNDEFINED (46)
constantStateUsage(0), // UNDEFINED (47)
constantStateUsage(2), // ADDRESS (48)
constantStateUsage(7), // BALANCE (49)
constantStateUsage(2), // ORIGIN (50)
constantStateUsage(2), // CALLER (51)
constantStateUsage(2), // CALLVALUE (52)
constantStateUsage(7), // CALLDATALOAD (53)
constantStateUsage(2), // CALLDATASIZE (54)
func(scope *vm.ScopeContext, depth int) uint64 {
// let n = # bytes in calldata, then row_consumption = (n/32)*2 + (is_root? 5 : 6)
constant := uint64(5)
if depth != 0 {
constant = 6
}
return 2*(scope.Stack.Back(2).Uint64()/32) + constant
}, // CALLDATACOPY (55)
constantStateUsage(1), // CODESIZE (56)
func(scope *vm.ScopeContext, _ int) uint64 {
// let n = # bytes in code, then row_consumption = (n/32) + 3
return scope.Stack.Back(2).Uint64()/32 + 3
}, // CODECOPY (57)
constantStateUsage(2), // GASPRICE (58)
constantStateUsage(7), // EXTCODESIZE (59)
func(scope *vm.ScopeContext, _ int) uint64 {
// let n = # bytes in code, then row_consumption = (n/32) + 9
return scope.Stack.Back(3).Uint64()/32 + 3
}, // EXTCODECOPY (60)
constantStateUsage(2), // RETURNDATASIZE (61)
func(scope *vm.ScopeContext, _ int) uint64 {
// let n = # of bytes to return, then row_consumption = (n/32)*2 + 6
return 2*(scope.Stack.Back(2).Uint64()/32) + 6
}, // RETURNDATACOPY (62)
constantStateUsage(7), // EXTCODEHASH (63)
constantStateUsage(2), // BLOCKHASH (64)
constantStateUsage(1), // COINBASE (65)
constantStateUsage(1), // TIMESTAMP (66)
constantStateUsage(1), // NUMBER (67)
constantStateUsage(1), // DIFFICULTY (68)
constantStateUsage(1), // GASLIMIT (69)
constantStateUsage(1), // CHAINID (70)
constantStateUsage(3), // SELFBALANCE (71)
constantStateUsage(1), // BASEFEE (72)
constantStateUsage(0), // UNDEFINED (73)
constantStateUsage(0), // UNDEFINED (74)
constantStateUsage(0), // UNDEFINED (75)
constantStateUsage(0), // UNDEFINED (76)
constantStateUsage(0), // UNDEFINED (77)
constantStateUsage(0), // UNDEFINED (78)
constantStateUsage(0), // UNDEFINED (79)
constantStateUsage(1), // POP (80)
constantStateUsage(4), // MLOAD (81)
constantStateUsage(4), // MSTORE (82)
constantStateUsage(3), // MSTORE8 (83)
constantStateUsage(9), // SLOAD (84)
constantStateUsage(11), // SSTORE (85)
constantStateUsage(1), // JUMP (86)
constantStateUsage(2), // JUMPI (87)
constantStateUsage(1), // PC (88)
constantStateUsage(1), // MSIZE (89)
constantStateUsage(1), // GAS (90)
constantStateUsage(0), // JUMPDEST (91)
constantStateUsage(5), // TLOAD (92)
constantStateUsage(8), // TSTORE (93)
constantStateUsage(7), // MCOPY (94)
constantStateUsage(1), // PUSH0 (95)
constantStateUsage(1), // PUSH1 (96)
constantStateUsage(1), // PUSH2 (97)
constantStateUsage(1), // PUSH3 (98)
constantStateUsage(1), // PUSH4 (99)
constantStateUsage(1), // PUSH5 (100)
constantStateUsage(1), // PUSH6 (101)
constantStateUsage(1), // PUSH7 (102)
constantStateUsage(1), // PUSH8 (103)
constantStateUsage(1), // PUSH9 (104)
constantStateUsage(1), // PUSH10 (105)
constantStateUsage(1), // PUSH11 (106)
constantStateUsage(1), // PUSH12 (107)
constantStateUsage(1), // PUSH13 (108)
constantStateUsage(1), // PUSH14 (109)
constantStateUsage(1), // PUSH15 (110)
constantStateUsage(1), // PUSH16 (111)
constantStateUsage(1), // PUSH17 (112)
constantStateUsage(1), // PUSH18 (113)
constantStateUsage(1), // PUSH19 (114)
constantStateUsage(1), // PUSH20 (115)
constantStateUsage(1), // PUSH21 (116)
constantStateUsage(1), // PUSH22 (117)
constantStateUsage(1), // PUSH23 (118)
constantStateUsage(1), // PUSH24 (119)
constantStateUsage(1), // PUSH25 (120)
constantStateUsage(1), // PUSH26 (121)
constantStateUsage(1), // PUSH27 (122)
constantStateUsage(1), // PUSH28 (123)
constantStateUsage(1), // PUSH29 (124)
constantStateUsage(1), // PUSH30 (125)
constantStateUsage(1), // PUSH31 (126)
constantStateUsage(1), // PUSH32 (127)
constantStateUsage(2), // DUP1 (128)
constantStateUsage(2), // DUP2 (129)
constantStateUsage(2), // DUP3 (130)
constantStateUsage(2), // DUP4 (131)
constantStateUsage(2), // DUP5 (132)
constantStateUsage(2), // DUP6 (133)
constantStateUsage(2), // DUP7 (134)
constantStateUsage(2), // DUP8 (135)
constantStateUsage(2), // DUP9 (136)
constantStateUsage(2), // DUP10 (137)
constantStateUsage(2), // DUP11 (138)
constantStateUsage(2), // DUP12 (139)
constantStateUsage(2), // DUP13 (140)
constantStateUsage(2), // DUP14 (141)
constantStateUsage(2), // DUP15 (142)
constantStateUsage(2), // DUP16 (143)
constantStateUsage(4), // SWAP1 (144)
constantStateUsage(4), // SWAP2 (145)
constantStateUsage(4), // SWAP3 (146)
constantStateUsage(4), // SWAP4 (147)
constantStateUsage(4), // SWAP5 (148)
constantStateUsage(4), // SWAP6 (149)
constantStateUsage(4), // SWAP7 (150)
constantStateUsage(4), // SWAP8 (151)
constantStateUsage(4), // SWAP9 (152)
constantStateUsage(4), // SWAP10 (153)
constantStateUsage(4), // SWAP11 (154)
constantStateUsage(4), // SWAP12 (155)
constantStateUsage(4), // SWAP13 (156)
constantStateUsage(4), // SWAP14 (157)
constantStateUsage(4), // SWAP15 (158)
constantStateUsage(4), // SWAP16 (159)
logStateUsage(0), // LOG0 (160)
logStateUsage(1), // LOG1 (161)
logStateUsage(2), // LOG2 (162)
logStateUsage(3), // LOG3 (163)
logStateUsage(4), // LOG4 (164)
constantStateUsage(0), // UNDEFINED (165)
constantStateUsage(0), // UNDEFINED (166)
constantStateUsage(0), // UNDEFINED (167)
constantStateUsage(0), // UNDEFINED (168)
constantStateUsage(0), // UNDEFINED (169)
constantStateUsage(0), // UNDEFINED (170)
constantStateUsage(0), // UNDEFINED (171)
constantStateUsage(0), // UNDEFINED (172)
constantStateUsage(0), // UNDEFINED (173)
constantStateUsage(0), // UNDEFINED (174)
constantStateUsage(0), // UNDEFINED (175)
constantStateUsage(0), // UNDEFINED (176)
constantStateUsage(0), // UNDEFINED (177)
constantStateUsage(0), // UNDEFINED (178)
constantStateUsage(0), // UNDEFINED (179)
constantStateUsage(0), // UNDEFINED (180)
constantStateUsage(0), // UNDEFINED (181)
constantStateUsage(0), // UNDEFINED (182)
constantStateUsage(0), // UNDEFINED (183)
constantStateUsage(0), // UNDEFINED (184)
constantStateUsage(0), // UNDEFINED (185)
constantStateUsage(0), // UNDEFINED (186)
constantStateUsage(0), // UNDEFINED (187)
constantStateUsage(0), // UNDEFINED (188)
constantStateUsage(0), // UNDEFINED (189)
constantStateUsage(0), // UNDEFINED (190)
constantStateUsage(0), // UNDEFINED (191)
constantStateUsage(0), // UNDEFINED (192)
constantStateUsage(0), // UNDEFINED (193)
constantStateUsage(0), // UNDEFINED (194)
constantStateUsage(0), // UNDEFINED (195)
constantStateUsage(0), // UNDEFINED (196)
constantStateUsage(0), // UNDEFINED (197)
constantStateUsage(0), // UNDEFINED (198)
constantStateUsage(0), // UNDEFINED (199)
constantStateUsage(0), // UNDEFINED (200)
constantStateUsage(0), // UNDEFINED (201)
constantStateUsage(0), // UNDEFINED (202)
constantStateUsage(0), // UNDEFINED (203)
constantStateUsage(0), // UNDEFINED (204)
constantStateUsage(0), // UNDEFINED (205)
constantStateUsage(0), // UNDEFINED (206)
constantStateUsage(0), // UNDEFINED (207)
constantStateUsage(0), // UNDEFINED (208)
constantStateUsage(0), // UNDEFINED (209)
constantStateUsage(0), // UNDEFINED (210)
constantStateUsage(0), // UNDEFINED (211)
constantStateUsage(0), // UNDEFINED (212)
constantStateUsage(0), // UNDEFINED (213)
constantStateUsage(0), // UNDEFINED (214)
constantStateUsage(0), // UNDEFINED (215)
constantStateUsage(0), // UNDEFINED (216)
constantStateUsage(0), // UNDEFINED (217)
constantStateUsage(0), // UNDEFINED (218)
constantStateUsage(0), // UNDEFINED (219)
constantStateUsage(0), // UNDEFINED (220)
constantStateUsage(0), // UNDEFINED (221)
constantStateUsage(0), // UNDEFINED (222)
constantStateUsage(0), // UNDEFINED (223)
constantStateUsage(0), // UNDEFINED (224)
constantStateUsage(0), // UNDEFINED (225)
constantStateUsage(0), // UNDEFINED (226)
constantStateUsage(0), // UNDEFINED (227)
constantStateUsage(0), // UNDEFINED (228)
constantStateUsage(0), // UNDEFINED (229)
constantStateUsage(0), // UNDEFINED (230)
constantStateUsage(0), // UNDEFINED (231)
constantStateUsage(0), // UNDEFINED (232)
constantStateUsage(0), // UNDEFINED (233)
constantStateUsage(0), // UNDEFINED (234)
constantStateUsage(0), // UNDEFINED (235)
constantStateUsage(0), // UNDEFINED (236)
constantStateUsage(0), // UNDEFINED (237)
constantStateUsage(0), // UNDEFINED (238)
constantStateUsage(0), // UNDEFINED (239)
constantStateUsage(42), // CREATE (240)
constantStateUsage(26), // CALL (241)
constantStateUsage(22), // CALLCODE (242)
constantStateUsage(273), // RETURN (243)
constantStateUsage(23), // DELEGATECALL (244)
constantStateUsage(43), // CREATE2 (245)
constantStateUsage(0), // UNDEFINED (246)
constantStateUsage(0), // UNDEFINED (247)
constantStateUsage(0), // UNDEFINED (248)
constantStateUsage(0), // UNDEFINED (249)
constantStateUsage(21), // STATICCALL (250)
constantStateUsage(0), // UNDEFINED (251)
constantStateUsage(0), // UNDEFINED (252)
constantStateUsage(274), // REVERT (253)
constantStateUsage(0), // INVALID (254)
constantStateUsage(0), // SELFDESTRUCT (255)
}