mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
feat(rollup): add circuits capacity checker (#321)
* add proof for predeployed storages * reverse inneeded code * update for mainbranch merging * add pertx storage trace * dummy tx proof * add txstorage trace * add coinbase storage as trace * enable (sync) tracing by default * basic * init cgo framework * fix break loop * integrate the right zkevm version (#323) * finish rust codes * use dylib (#325) * flip * ? * use cdylib * revert * fix * apply_tx * rename * fixing types fixing types fixing types * clean up * ExecutionResults (#328) * filling * filling * more * clean up * filling * coinbase * add * MPTWitness * ExecutionResults WIP * L1fee L1fee * sender * to * Failed & ReturnValue * createdAcc & after * remove MPTWitness * txStorageTrace * add FeeRecipient * add StorageTrace * fix FFI types * better logger * cargo fmt * fix * add build tags * update Makefile * fix library * improve ld path * correctly deal with circuit_capacity_checker returned result * fix return value * update cargo (#333) * update cargo * update * update go * refactor * raname `circuits capacity checker` to `circuit capacity checker` * some refactorings * [Fix] storage proof generation in capacity checker (#348) * make per-tx storage and deletion proof work * format * fix misplaced markdeletion --------- Co-authored-by: HAOYUatHZ <haoyu@protonmail.com> * docker (#363) * update Dockerfile * build: update go version to 1.19 * update * fix * fix * try * simplify * revert go version update l2geth Dockerfiles * fix * fix coinbase * fix (#369) * format * Update version.go * address comments * Capacity refactor (#374) * init * id * support multiple instances * fix id * fix conflicts * refactor to use same codes (#379) * re-init * WIP * WIP * refactor * go * minor * fix storage proof of l1gas price oracle * move 1 * move 2 * move 3 * move 4 * move 5 move 5 * move 6 move 6 * move 7 * move 8 * move 9 * move 10 * clean up clean up --------- Co-authored-by: Ho Vei <noelwei@gmail.com> * finish basic * minor * config capacity check in block_validator (#380) * init * done ref * fix tests fix tests fix tests fix tests * add more comments * apply_block * improve logs * cargo fmt * Capacity big refactor (#383) * CreateTraceEnv * WIP * draft more fix * for test * fortet * clean up * add more comments * goimports -local github.com/scroll-tech/go-ethereum -w . * fix typos * attempt 1 * attempt 2 * attempt 3 * gogogo * clean up * fix * fix * rename * minor * fix * minor * minor * improve doc * use dedicated `checkCircuitCapacity` flag (#394) * refactor * fix * add lock * [feat] capacity checking: upgrade libzkp (#395) * upgrade * upgrade libzkp * write RowConsumption (#396) * write RowConsumption * name alignments * revert some formatting * add lock to CircuitCapacityChecker in BlockValidator * remove mutex pointer * improve github workflow * improve * store row consumption in mining (#397) * prepare * finish * add more logs * mark `ApplyBlock` as ready * update libzkp (#401) * fix * Capacity detail (#402) * fix(block-validation): consider skipping in ValidateL1Messages (#405) * fix(block-validation): consider skipping in ValidateL1Messages * fix(block): consider skipping in L1MessageCount * fix l1 validation tests * fix NumL1Messages * fix impl.go return types fix * better error handling (#407) * add add * add * add * add * add * cargo fmt * add * update * add * WIP * minor * gogogo * gogogo * fix * fix * fix * cargo clippy * improve * improve * creation lock (#408) * creation lock * update * Debug log (#409) * add more logs * more * more * fix * improve * Update cmd/utils/flags.go Co-authored-by: Péter Garamvölgyi <peter@scroll.io> * refactor worker.commit() * avoid re-calculate * txpool ccc err handling (#411) * more explicit error comments * add more logs * fix unnecessary commit * add more logs * fix `ineffassign` * add more comments * log id for `NewCircuitCapacityChecker` (#414) add log to `NewCircuitCapacityChecker` * Persist skip info for block where all L1 msgs are skipped (#415) persist skip info for block where all L1 msgs are skipped * Update version.go --------- Co-authored-by: Ho Vei <noelwei@gmail.com> Co-authored-by: Zhang Zhuo <mycinbrin@gmail.com> Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
This commit is contained in:
parent
134e1b8f92
commit
2f9edf73ca
56 changed files with 5986 additions and 689 deletions
21
.github/workflows/l2geth_ci.yml
vendored
21
.github/workflows/l2geth_ci.yml
vendored
|
|
@ -13,7 +13,7 @@ on:
|
|||
- ready_for_review
|
||||
name: CI
|
||||
jobs:
|
||||
build:
|
||||
build-mock-ccc-geth: # build geth with mock circuit capacity checker
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
@ -23,6 +23,25 @@ jobs:
|
|||
go-version: 1.18.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: |
|
||||
make nccc_geth
|
||||
build-geth: # build geth with circuit capacity checker
|
||||
if: github.event_name == 'push' # will only be triggered when pushing to main & staging & develop & alpha
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
- name: Install rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2022-12-10
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
run: |
|
||||
make geth
|
||||
|
|
|
|||
32
Dockerfile
32
Dockerfile
|
|
@ -3,19 +3,39 @@ ARG COMMIT=""
|
|||
ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.18-alpine as builder
|
||||
# Build libzkp dependency
|
||||
FROM scrolltech/go-rust-builder:go-1.19-rust-nightly-2022-12-10 as chef
|
||||
WORKDIR app
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
FROM chef as planner
|
||||
COPY ./rollup/circuitcapacitychecker/libzkp/ .
|
||||
RUN cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
FROM chef as zkp-builder
|
||||
COPY ./rollup/circuitcapacitychecker/libzkp/rust-toolchain ./
|
||||
COPY --from=planner /app/recipe.json recipe.json
|
||||
RUN cargo chef cook --release --recipe-path recipe.json
|
||||
|
||||
COPY ./rollup/circuitcapacitychecker/libzkp .
|
||||
RUN cargo build --release
|
||||
RUN find ./ | grep libzktrie.so | xargs -I{} cp {} /app/target/release/
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM scrolltech/go-rust-builder:go-1.19-rust-nightly-2022-12-10 as builder
|
||||
|
||||
ADD . /go-ethereum
|
||||
RUN cd /go-ethereum && go run build/ci.go install ./cmd/geth
|
||||
COPY --from=zkp-builder /app/target/release/libzkp.so /usr/local/lib/
|
||||
COPY --from=zkp-builder /app/target/release/libzktrie.so /usr/local/lib/
|
||||
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/
|
||||
RUN cd /go-ethereum && env GO111MODULE=on go run build/ci.go install -buildtags circuit_capacity_checker ./cmd/geth
|
||||
|
||||
# Pull Geth into a second stage deploy alpine container
|
||||
FROM alpine:latest
|
||||
FROM ubuntu:20.04
|
||||
|
||||
RUN apk add --no-cache ca-certificates
|
||||
COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
|
||||
COPY --from=zkp-builder /app/target/release/libzkp.so /usr/local/lib/
|
||||
COPY --from=zkp-builder /app/target/release/libzktrie.so /usr/local/lib/
|
||||
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib/
|
||||
|
||||
EXPOSE 8545 8546 30303 30303/udp
|
||||
ENTRYPOINT ["geth"]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
|||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.18-alpine as builder
|
||||
FROM golang:1.19-alpine as builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
|
|
|
|||
13
Makefile
13
Makefile
|
|
@ -2,17 +2,26 @@
|
|||
# with Go source code. If you know what GOPATH is then you probably
|
||||
# don't need to bother with make.
|
||||
|
||||
.PHONY: geth android ios evm all test clean
|
||||
.PHONY: geth android ios evm all test clean libzkp
|
||||
|
||||
GOBIN = ./build/bin
|
||||
GO ?= latest
|
||||
GORUN = env GO111MODULE=on go run
|
||||
|
||||
geth:
|
||||
|
||||
libzkp:
|
||||
cd $(PWD)/rollup/circuitcapacitychecker/libzkp && make libzkp
|
||||
|
||||
nccc_geth: ## geth without circuit capacity checker
|
||||
$(GORUN) build/ci.go install ./cmd/geth
|
||||
@echo "Done building."
|
||||
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
||||
|
||||
geth: libzkp
|
||||
$(GORUN) build/ci.go install -buildtags circuit_capacity_checker ./cmd/geth
|
||||
@echo "Done building."
|
||||
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
||||
|
||||
all:
|
||||
$(GORUN) build/ci.go install
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ type SimulatedBackend struct {
|
|||
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
||||
genesis.MustCommit(database)
|
||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
|
||||
backend := &SimulatedBackend{
|
||||
database: database,
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ func doInstall(cmdline []string) {
|
|||
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
|
||||
arch = flag.String("arch", "", "Architecture to cross build for")
|
||||
cc = flag.String("cc", "", "C compiler to cross build with")
|
||||
buildtags = flag.String("buildtags", "", "Tags for go build")
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
|
||||
|
|
@ -215,6 +216,9 @@ func doInstall(cmdline []string) {
|
|||
// Configure the build.
|
||||
env := build.Env()
|
||||
gobuild := tc.Go("build", buildFlags(env)...)
|
||||
if len(*buildtags) != 0 {
|
||||
gobuild.Args = append(gobuild.Args, "-tags", *buildtags)
|
||||
}
|
||||
|
||||
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
||||
// better disable it. This check isn't the best, it should probably
|
||||
|
|
|
|||
|
|
@ -812,6 +812,12 @@ var (
|
|||
Name: "l1.sync.startblock",
|
||||
Usage: "L1 block height to start syncing from. Should be set to the L1 message queue deployment block number.",
|
||||
}
|
||||
|
||||
// Circuit capacity check settings
|
||||
CircuitCapacityCheckEnabledFlag = cli.BoolFlag{
|
||||
Name: "ccc",
|
||||
Usage: "Enable circuit capacity check during block validation",
|
||||
}
|
||||
)
|
||||
|
||||
// MakeDataDir retrieves the currently requested data directory, terminating
|
||||
|
|
@ -1493,6 +1499,12 @@ func setWhitelist(ctx *cli.Context, cfg *ethconfig.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
func setCircuitCapacityCheck(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||
if ctx.GlobalIsSet(CircuitCapacityCheckEnabledFlag.Name) {
|
||||
cfg.CheckCircuitCapacity = ctx.GlobalBool(CircuitCapacityCheckEnabledFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// CheckExclusive verifies that only a single instance of the provided flags was
|
||||
// set by the user. Each flag might optionally be followed by a string type to
|
||||
// specialize it further.
|
||||
|
|
@ -1558,6 +1570,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
setMiner(ctx, &cfg.Miner)
|
||||
setWhitelist(ctx, cfg)
|
||||
setLes(ctx, cfg)
|
||||
setCircuitCapacityCheck(ctx, cfg)
|
||||
|
||||
// Cap the cache allowance and tune the garbage collector
|
||||
mem, err := gopsutil.VirtualMemory()
|
||||
|
|
@ -2006,7 +2019,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
|
|||
|
||||
// TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only.
|
||||
// Disable transaction indexing/unindexing by default.
|
||||
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil)
|
||||
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil, false)
|
||||
if err != nil {
|
||||
Fatalf("Can't create BlockChain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ func TestReimportMirroredState(t *testing.T) {
|
|||
genesis := genspec.MustCommit(db)
|
||||
|
||||
// Generate a batch of blocks, each properly signed
|
||||
chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
blocks, _ := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, 3, func(i int, block *core.BlockGen) {
|
||||
|
|
@ -89,7 +89,7 @@ func TestReimportMirroredState(t *testing.T) {
|
|||
db = rawdb.NewMemoryDatabase()
|
||||
genspec.MustCommit(db)
|
||||
|
||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
if _, err := chain.InsertChain(blocks[:2]); err != nil {
|
||||
|
|
@ -102,7 +102,7 @@ func TestReimportMirroredState(t *testing.T) {
|
|||
// Simulate a crash by creating a new chain on top of the database, without
|
||||
// flushing the dirty states out. Insert the last block, triggering a sidechain
|
||||
// reimport.
|
||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
if _, err := chain.InsertChain(blocks[2:]); err != nil {
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ func TestClique(t *testing.T) {
|
|||
batches[len(batches)-1] = append(batches[len(batches)-1], block)
|
||||
}
|
||||
// Pass all the headers through clique and ensure tallying succeeds
|
||||
chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Errorf("test %d: failed to create test chain: %v", i, err)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@ var (
|
|||
|
||||
// ErrInvalidL1MessageOrder is returned if a block contains L1 messages in the wrong
|
||||
// order. Possible scenarios are: (1) L1 messages do not follow their QueueIndex order,
|
||||
// (2) the block skipped one or more L1 messages, (3) L1 messages are not included in
|
||||
// a contiguous block at the front of the block.
|
||||
// (2) L1 messages are not included in a contiguous block at the front of the block.
|
||||
ErrInvalidL1MessageOrder = errors.New("invalid L1 message order")
|
||||
|
||||
// ErrUnknownL1Message is returned if a block contains an L1 message that does not
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
|||
|
||||
// Time the insertion of the new chain.
|
||||
// State and blocks are stored in the same DB.
|
||||
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer chainman.Stop()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
|
@ -316,7 +316,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
|||
if err != nil {
|
||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||
}
|
||||
chain, err := NewBlockChain(db, &cacheConfig, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(db, &cacheConfig, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
b.Fatalf("error creating chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,18 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/consensus"
|
||||
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
||||
"github.com/scroll-tech/go-ethereum/core/state"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/ethdb"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/circuitcapacitychecker"
|
||||
"github.com/scroll-tech/go-ethereum/trie"
|
||||
)
|
||||
|
||||
|
|
@ -35,15 +40,25 @@ type BlockValidator struct {
|
|||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
engine consensus.Engine // Consensus engine used for validating
|
||||
|
||||
// circuit capacity checker related fields
|
||||
checkCircuitCapacity bool // whether enable circuit capacity check
|
||||
db ethdb.Database // db to store row consumption
|
||||
cMu sync.Mutex // mutex for circuit capacity checker
|
||||
circuitCapacityChecker *circuitcapacitychecker.CircuitCapacityChecker // circuit capacity checker instance
|
||||
}
|
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator {
|
||||
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine, db ethdb.Database, checkCircuitCapacity bool) *BlockValidator {
|
||||
validator := &BlockValidator{
|
||||
config: config,
|
||||
engine: engine,
|
||||
bc: blockchain,
|
||||
checkCircuitCapacity: checkCircuitCapacity,
|
||||
db: db,
|
||||
circuitCapacityChecker: circuitcapacitychecker.NewCircuitCapacityChecker(),
|
||||
}
|
||||
log.Info("created new BlockValidator", "CircuitCapacityChecker ID", validator.circuitCapacityChecker.ID)
|
||||
return validator
|
||||
}
|
||||
|
||||
|
|
@ -79,18 +94,34 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
|||
}
|
||||
return consensus.ErrPrunedAncestor
|
||||
}
|
||||
return v.ValidateL1Messages(block)
|
||||
if err := v.ValidateL1Messages(block); err != nil {
|
||||
return err
|
||||
}
|
||||
if v.checkCircuitCapacity {
|
||||
// if a block's RowConsumption has been stored, which means it has been processed before,
|
||||
// (e.g., in miner/worker.go or in insertChain),
|
||||
// we simply skip its calculation and validation
|
||||
if rawdb.ReadBlockRowConsumption(v.db, block.Hash()) != nil {
|
||||
return nil
|
||||
}
|
||||
rowConsumption, err := v.validateCircuitRowConsumption(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawdb.WriteBlockRowConsumption(v.db, block.Hash(), rowConsumption)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateL1Messages validates L1 messages contained in a block.
|
||||
// We check the following conditions:
|
||||
// - L1 messages are in a contiguous section at the front of the block.
|
||||
// - The first L1 message's QueueIndex is right after the last L1 message included in the chain.
|
||||
// - L1 messages follow the QueueIndex order. No L1 message is skipped.
|
||||
// - L1 messages follow the QueueIndex order.
|
||||
// - The L1 messages included in the block match the node's view of the L1 ledger.
|
||||
func (v *BlockValidator) ValidateL1Messages(block *types.Block) error {
|
||||
// no further processing if the block contains no L1 messages
|
||||
if block.L1MessageCount() == 0 {
|
||||
// skip DB read if the block contains no L1 messages
|
||||
if !block.ContainsL1Messages() {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -120,16 +151,30 @@ func (v *BlockValidator) ValidateL1Messages(block *types.Block) error {
|
|||
return consensus.ErrInvalidL1MessageOrder
|
||||
}
|
||||
|
||||
// check queue index
|
||||
// TODO: account for skipped messages here
|
||||
if tx.AsL1MessageTx().QueueIndex != queueIndex {
|
||||
// queue index cannot decrease
|
||||
txQueueIndex := tx.AsL1MessageTx().QueueIndex
|
||||
|
||||
if txQueueIndex < queueIndex {
|
||||
return consensus.ErrInvalidL1MessageOrder
|
||||
}
|
||||
|
||||
queueIndex += 1
|
||||
// skipped messages
|
||||
// TODO: consider verifying that skipped messages overflow
|
||||
for index := queueIndex; index < txQueueIndex; index++ {
|
||||
log.Debug("Skipped L1 message", "block", block.Hash().String(), "queueIndex", index)
|
||||
|
||||
if exists := it.Next(); !exists {
|
||||
// we'll reprocess this block at a later time
|
||||
// the message in this block is not available in our local db.
|
||||
// we'll reprocess this block at a later time.
|
||||
return consensus.ErrMissingL1MessageData
|
||||
}
|
||||
}
|
||||
|
||||
queueIndex = txQueueIndex + 1
|
||||
|
||||
if exists := it.Next(); !exists {
|
||||
// the message in this block is not available in our local db.
|
||||
// we'll reprocess this block at a later time.
|
||||
return consensus.ErrMissingL1MessageData
|
||||
}
|
||||
|
||||
|
|
@ -202,3 +247,35 @@ func CalcGasLimit(parentGasLimit, desiredLimit uint64) uint64 {
|
|||
}
|
||||
return limit
|
||||
}
|
||||
|
||||
func (v *BlockValidator) createTraceEnv(block *types.Block) (*TraceEnv, error) {
|
||||
parent := v.bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, errors.New("validateCircuitRowConsumption: no parent block found")
|
||||
}
|
||||
|
||||
statedb, err := v.bc.StateAt(parent.Hash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTraceEnv(v.config, v.bc, v.engine, statedb, parent, block)
|
||||
}
|
||||
|
||||
func (v *BlockValidator) validateCircuitRowConsumption(block *types.Block) (*types.RowConsumption, error) {
|
||||
env, err := v.createTraceEnv(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
traces, err := env.GetBlockTrace(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.cMu.Lock()
|
||||
defer v.cMu.Unlock()
|
||||
|
||||
v.circuitCapacityChecker.Reset()
|
||||
return v.circuitCapacityChecker.ApplyBlock(traces)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func TestHeaderVerification(t *testing.T) {
|
|||
headers[i] = block.Header()
|
||||
}
|
||||
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
|
|
@ -106,11 +106,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
|||
var results <-chan error
|
||||
|
||||
if valid {
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
chain.Stop()
|
||||
} else {
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil, false)
|
||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
chain.Stop()
|
||||
}
|
||||
|
|
@ -173,7 +173,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
|||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// Start the verifications and immediately abort
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil)
|
||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ type BlockChain struct {
|
|||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialises the default Ethereum Validator and
|
||||
// Processor.
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool, txLookupLimit *uint64) (*BlockChain, error) {
|
||||
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool, txLookupLimit *uint64, checkCircuitCapacity bool) (*BlockChain, error) {
|
||||
if cacheConfig == nil {
|
||||
cacheConfig = defaultCacheConfig
|
||||
}
|
||||
|
|
@ -263,7 +263,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
engine: engine,
|
||||
vmConfig: vmConfig,
|
||||
}
|
||||
bc.validator = NewBlockValidator(chainConfig, bc, engine)
|
||||
bc.validator = NewBlockValidator(chainConfig, bc, engine, db, checkCircuitCapacity)
|
||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
|
||||
bc.processor = NewStateProcessor(chainConfig, bc, engine)
|
||||
|
||||
|
|
@ -1182,10 +1182,14 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e
|
|||
rawdb.WriteBlock(batch, block)
|
||||
|
||||
queueIndex := rawdb.ReadFirstQueueIndexNotInL2Block(bc.db, block.ParentHash())
|
||||
if queueIndex != nil {
|
||||
|
||||
// note: we can insert blocks with header-only ancestors here,
|
||||
// so queueIndex might not yet be available in DB.
|
||||
rawdb.WriteFirstQueueIndexNotInL2Block(batch, block.Hash(), *queueIndex+uint64(block.L1MessageCount()))
|
||||
if queueIndex != nil {
|
||||
// do not overwrite the index written by the miner worker
|
||||
if index := rawdb.ReadFirstQueueIndexNotInL2Block(bc.db, block.Hash()); index == nil {
|
||||
rawdb.WriteFirstQueueIndexNotInL2Block(batch, block.Hash(), *queueIndex+uint64(block.NumL1MessagesProcessed(*queueIndex)))
|
||||
}
|
||||
}
|
||||
|
||||
if err := batch.Write(); err != nil {
|
||||
|
|
@ -1249,7 +1253,10 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
// so the parent will always be inserted first.
|
||||
log.Crit("Queue index in DB is nil", "parent", block.ParentHash(), "hash", block.Hash())
|
||||
}
|
||||
rawdb.WriteFirstQueueIndexNotInL2Block(blockBatch, block.Hash(), *queueIndex+uint64(block.L1MessageCount()))
|
||||
// do not overwrite the index written by the miner worker
|
||||
if index := rawdb.ReadFirstQueueIndexNotInL2Block(bc.db, block.Hash()); index == nil {
|
||||
rawdb.WriteFirstQueueIndexNotInL2Block(blockBatch, block.Hash(), *queueIndex+uint64(block.NumL1MessagesProcessed(*queueIndex)))
|
||||
}
|
||||
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to write block into disk", "err", err)
|
||||
|
|
|
|||
|
|
@ -1783,7 +1783,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
|||
config.SnapshotLimit = 256
|
||||
config.SnapshotWait = true
|
||||
}
|
||||
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
|
|
@ -1836,7 +1836,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
|||
}
|
||||
defer db.Close()
|
||||
|
||||
chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -1907,7 +1907,7 @@ func TestIssue23496(t *testing.T) {
|
|||
SnapshotWait: true,
|
||||
}
|
||||
)
|
||||
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
|
|
@ -1951,7 +1951,7 @@ func TestIssue23496(t *testing.T) {
|
|||
}
|
||||
defer db.Close()
|
||||
|
||||
chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1982,7 +1982,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
|
|||
config.SnapshotLimit = 256
|
||||
config.SnapshotWait = true
|
||||
}
|
||||
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
|
|||
// will happen during the block insertion.
|
||||
cacheConfig = defaultCacheConfig
|
||||
)
|
||||
chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
|
|
@ -223,7 +223,7 @@ func (snaptest *snapshotTest) test(t *testing.T) {
|
|||
|
||||
// Restart the chain normally
|
||||
chain.Stop()
|
||||
newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -259,13 +259,13 @@ func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
|||
// the crash, we do restart twice here: one after the crash and one
|
||||
// after the normal stop. It's used to ensure the broken snapshot
|
||||
// can be detected all the time.
|
||||
newchain, err := NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
newchain.Stop()
|
||||
|
||||
newchain, err = NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = NewBlockChain(newdb, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -301,7 +301,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
|||
TrieTimeLimit: 5 * time.Minute,
|
||||
SnapshotLimit: 0,
|
||||
}
|
||||
newchain, err := NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, cacheConfig, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -309,7 +309,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
|||
newchain.Stop()
|
||||
|
||||
// Restart the chain with enabling the snapshot
|
||||
newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -337,7 +337,7 @@ func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
|
|||
chain.SetHead(snaptest.setHead)
|
||||
chain.Stop()
|
||||
|
||||
newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -368,7 +368,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
|
|||
// and state committed.
|
||||
chain.Stop()
|
||||
|
||||
newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -385,7 +385,7 @@ func (snaptest *restartCrashSnapshotTest) test(t *testing.T) {
|
|||
// journal and latest state will be committed
|
||||
|
||||
// Restart the chain after the crash
|
||||
newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -420,7 +420,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
|||
TrieTimeLimit: 5 * time.Minute,
|
||||
SnapshotLimit: 0,
|
||||
}
|
||||
newchain, err := NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err := NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
@ -436,13 +436,13 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
|||
SnapshotLimit: 256,
|
||||
SnapshotWait: false, // Don't wait rebuild
|
||||
}
|
||||
newchain, err = NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = NewBlockChain(snaptest.db, config, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
// Simulate the blockchain crash.
|
||||
|
||||
newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil)
|
||||
newchain, err = NewBlockChain(snaptest.db, nil, params.AllEthashProtocolChanges, snaptest.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to recreate chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B
|
|||
)
|
||||
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, false)
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
return db, blockchain, nil
|
||||
|
|
@ -524,7 +524,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
|
|||
blockchain.Stop()
|
||||
|
||||
// Create a new BlockChain and check that it rolled back the state.
|
||||
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
|
|
@ -637,7 +637,7 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
// Import the chain as an archive node for the comparison baseline
|
||||
archiveDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(archiveDb)
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer archive.Stop()
|
||||
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
|
|
@ -646,7 +646,7 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
// Fast import the chain as a non-archive node to test
|
||||
fastDb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(fastDb)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer fast.Stop()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
|
|
@ -670,7 +670,7 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
gspec.MustCommit(ancientDb)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer ancient.Stop()
|
||||
|
||||
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
|
||||
|
|
@ -792,7 +792,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
archiveCaching := *defaultCacheConfig
|
||||
archiveCaching.TrieDirtyDisabled = true
|
||||
|
||||
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
if n, err := archive.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("failed to process block %d: %v", n, err)
|
||||
}
|
||||
|
|
@ -805,7 +805,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||
fastDb, delfn := makeDb()
|
||||
defer delfn()
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer fast.Stop()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
|
|
@ -825,7 +825,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
// Import the chain as a ancient-first node and ensure all pointers are updated
|
||||
ancientDb, delfn := makeDb()
|
||||
defer delfn()
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer ancient.Stop()
|
||||
|
||||
if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
|
||||
|
|
@ -844,7 +844,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
// Import the chain as a light node and ensure all pointers are updated
|
||||
lightDb, delfn := makeDb()
|
||||
defer delfn()
|
||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||
}
|
||||
|
|
@ -913,7 +913,7 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
}
|
||||
})
|
||||
// Import the chain. This runs all block validation rules.
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||
}
|
||||
|
|
@ -983,7 +983,7 @@ func TestLogReorgs(t *testing.T) {
|
|||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer blockchain.Stop()
|
||||
|
||||
rmLogsCh := make(chan RemovedLogsEvent)
|
||||
|
|
@ -1036,7 +1036,7 @@ func TestLogRebirth(t *testing.T) {
|
|||
genesis = gspec.MustCommit(db)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
engine = ethash.NewFaker()
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, false)
|
||||
)
|
||||
|
||||
defer blockchain.Stop()
|
||||
|
|
@ -1099,7 +1099,7 @@ func TestSideLogRebirth(t *testing.T) {
|
|||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
)
|
||||
|
||||
defer blockchain.Stop()
|
||||
|
|
@ -1174,7 +1174,7 @@ func TestReorgSideEvent(t *testing.T) {
|
|||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer blockchain.Stop()
|
||||
|
||||
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
|
||||
|
|
@ -1306,7 +1306,7 @@ func TestEIP155Transition(t *testing.T) {
|
|||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer blockchain.Stop()
|
||||
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
|
||||
|
|
@ -1414,7 +1414,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
|||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer blockchain.Stop()
|
||||
|
||||
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
|
||||
|
|
@ -1489,7 +1489,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1533,7 +1533,7 @@ func TestTrieForkGC(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1572,7 +1572,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1633,7 +1633,7 @@ func TestBlockchainRecovery(t *testing.T) {
|
|||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
gspec.MustCommit(ancientDb)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
|
|
@ -1653,7 +1653,7 @@ func TestBlockchainRecovery(t *testing.T) {
|
|||
rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
|
||||
|
||||
// Reopen broken blockchain again
|
||||
ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer ancient.Stop()
|
||||
if num := ancient.CurrentBlock().NumberU64(); num != 0 {
|
||||
t.Errorf("head block mismatch: have #%v, want #%v", num, 0)
|
||||
|
|
@ -1705,7 +1705,7 @@ func TestInsertReceiptChainRollback(t *testing.T) {
|
|||
}
|
||||
gspec := Genesis{Config: params.AllEthashProtocolChanges}
|
||||
gspec.MustCommit(ancientDb)
|
||||
ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer ancientChain.Stop()
|
||||
|
||||
// Import the canonical header chain.
|
||||
|
|
@ -1766,7 +1766,7 @@ func TestLowDiffLongChain(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1813,7 +1813,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
|||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil)
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -1910,7 +1910,7 @@ func testInsertKnownChainData(t *testing.T, typ string) {
|
|||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(chaindb)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2019,7 +2019,7 @@ func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyCha
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2212,7 +2212,7 @@ func TestTransactionIndices(t *testing.T) {
|
|||
|
||||
// Import all blocks into ancient db
|
||||
l := uint64(0)
|
||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2237,7 +2237,7 @@ func TestTransactionIndices(t *testing.T) {
|
|||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
gspec.MustCommit(ancientDb)
|
||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2261,7 +2261,7 @@ func TestTransactionIndices(t *testing.T) {
|
|||
limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */}
|
||||
tails := []uint64{0, 67 /* 130 - 64 + 1 */, 100 /* 131 - 32 + 1 */, 69 /* 132 - 64 + 1 */, 0}
|
||||
for i, l := range limit {
|
||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2339,7 +2339,7 @@ func TestSkipStaleTxIndicesInFastSync(t *testing.T) {
|
|||
|
||||
// Import all blocks into ancient db, only HEAD-32 indices are kept.
|
||||
l := uint64(32)
|
||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l)
|
||||
chain, err := NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, &l, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2403,7 +2403,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2485,7 +2485,7 @@ func TestSideImportPrunedBlocks(t *testing.T) {
|
|||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil)
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2625,7 +2625,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2730,7 +2730,7 @@ func TestInitThenFailCreateContract(t *testing.T) {
|
|||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
|
||||
//Debug: true,
|
||||
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
||||
}, nil, nil)
|
||||
}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2817,7 +2817,7 @@ func TestEIP2718Transition(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -2912,7 +2912,7 @@ func TestEIP1559Transition(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -3036,7 +3036,7 @@ func TestPoseidonCodeHash(t *testing.T) {
|
|||
genesis = gspec.MustCommit(db)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
engine = ethash.NewFaker()
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, false)
|
||||
)
|
||||
|
||||
defer blockchain.Stop()
|
||||
|
|
@ -3150,7 +3150,7 @@ func TestFeeVault(t *testing.T) {
|
|||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
@ -3202,7 +3202,7 @@ func TestTransactionCountLimit(t *testing.T) {
|
|||
}
|
||||
|
||||
// Initialize blockchain
|
||||
blockchain, err := NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil)
|
||||
blockchain, err := NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
|
|
@ -3266,7 +3266,7 @@ func TestInsertBlocksWithL1Messages(t *testing.T) {
|
|||
rawdb.WriteL1Messages(db, msgs)
|
||||
|
||||
// initialize blockchain
|
||||
blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil, false)
|
||||
defer blockchain.Stop()
|
||||
|
||||
// generate blocks with 1 L1 message in each
|
||||
|
|
@ -3302,6 +3302,42 @@ func TestInsertBlocksWithL1Messages(t *testing.T) {
|
|||
queueIndex = rawdb.ReadFirstQueueIndexNotInL2Block(db, blocks[len(blocks)-1].Hash())
|
||||
assert.NotNil(t, queueIndex)
|
||||
assert.Equal(t, uint64(len(msgs)), *queueIndex)
|
||||
|
||||
// generate block with messages #1 and #2 skipped
|
||||
blocks, _ = GenerateChain(config, genesis, engine, db, 1, func(_ int, b *BlockGen) {
|
||||
tx := types.NewTx(&msgs[0])
|
||||
b.AddTxWithChain(blockchain, tx)
|
||||
tx = types.NewTx(&msgs[3])
|
||||
b.AddTxWithChain(blockchain, tx)
|
||||
})
|
||||
|
||||
// insert blocks, validation should pass
|
||||
index, err = blockchain.InsertChain(blocks)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, index)
|
||||
|
||||
// L1 message DB should be updated
|
||||
queueIndex = rawdb.ReadFirstQueueIndexNotInL2Block(db, blocks[0].Hash())
|
||||
assert.NotNil(t, queueIndex)
|
||||
assert.Equal(t, uint64(len(msgs)), *queueIndex)
|
||||
|
||||
// generate block with messages #0 and #1 skipped
|
||||
blocks, _ = GenerateChain(config, genesis, engine, db, 1, func(_ int, b *BlockGen) {
|
||||
tx := types.NewTx(&msgs[2])
|
||||
b.AddTxWithChain(blockchain, tx)
|
||||
tx = types.NewTx(&msgs[3])
|
||||
b.AddTxWithChain(blockchain, tx)
|
||||
})
|
||||
|
||||
// insert blocks, validation should pass
|
||||
index, err = blockchain.InsertChain(blocks)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, index)
|
||||
|
||||
// L1 message DB should be updated
|
||||
queueIndex = rawdb.ReadFirstQueueIndexNotInL2Block(db, blocks[0].Hash())
|
||||
assert.NotNil(t, queueIndex)
|
||||
assert.Equal(t, uint64(len(msgs)), *queueIndex)
|
||||
}
|
||||
|
||||
// TestL1MessageValidationFailure tests that the chain rejects blocks with incorrect L1MessageTx transactions.
|
||||
|
|
@ -3336,7 +3372,7 @@ func TestL1MessageValidationFailure(t *testing.T) {
|
|||
rawdb.WriteL1Messages(db, msgs)
|
||||
|
||||
// initialize blockchain
|
||||
blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil, false)
|
||||
defer blockchain.Stop()
|
||||
|
||||
generateBlock := func(txs []*types.Transaction) ([]*types.Block, []types.Receipts) {
|
||||
|
|
@ -3347,24 +3383,10 @@ func TestL1MessageValidationFailure(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// skip #0
|
||||
blocks, _ := generateBlock([]*types.Transaction{types.NewTx(&msgs[1])})
|
||||
index, err := blockchain.InsertChain(blocks)
|
||||
assert.Equal(t, 0, index)
|
||||
assert.Equal(t, consensus.ErrInvalidL1MessageOrder, err)
|
||||
assert.Equal(t, big.NewInt(0), blockchain.CurrentBlock().Number())
|
||||
|
||||
// skip #1
|
||||
blocks, _ = generateBlock([]*types.Transaction{types.NewTx(&msgs[0]), types.NewTx(&msgs[2])})
|
||||
index, err = blockchain.InsertChain(blocks)
|
||||
assert.Equal(t, 0, index)
|
||||
assert.Equal(t, consensus.ErrInvalidL1MessageOrder, err)
|
||||
assert.Equal(t, big.NewInt(0), blockchain.CurrentBlock().Number())
|
||||
|
||||
// L2 tx precedes L1 message tx
|
||||
tx, _ := types.SignTx(types.NewTransaction(0, common.Address{0x00}, new(big.Int), params.TxGas, genspec.BaseFee, nil), signer, key)
|
||||
blocks, _ = generateBlock([]*types.Transaction{tx, types.NewTx(&msgs[0])})
|
||||
index, err = blockchain.InsertChain(blocks)
|
||||
blocks, _ := generateBlock([]*types.Transaction{tx, types.NewTx(&msgs[0])})
|
||||
index, err := blockchain.InsertChain(blocks)
|
||||
assert.Equal(t, 0, index)
|
||||
assert.Equal(t, consensus.ErrInvalidL1MessageOrder, err)
|
||||
assert.Equal(t, big.NewInt(0), blockchain.CurrentBlock().Number())
|
||||
|
|
@ -3425,7 +3447,7 @@ func TestBlockPayloadSizeLimit(t *testing.T) {
|
|||
}
|
||||
|
||||
// Initialize blockchain
|
||||
blockchain, err := NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil)
|
||||
blockchain, err := NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new chain manager: %v", err)
|
||||
}
|
||||
|
|
@ -3537,7 +3559,7 @@ func TestEIP3651(t *testing.T) {
|
|||
}
|
||||
b.AddTx(tx)
|
||||
})
|
||||
chain, err := NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ func ExampleGenerateChain() {
|
|||
})
|
||||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer blockchain.Stop()
|
||||
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
proConf.DAOForkBlock = forkBlock
|
||||
proConf.DAOForkSupport = true
|
||||
|
||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer proBc.Stop()
|
||||
|
||||
conDb := rawdb.NewMemoryDatabase()
|
||||
|
|
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
conConf.DAOForkBlock = forkBlock
|
||||
conConf.DAOForkSupport = false
|
||||
|
||||
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer conBc.Stop()
|
||||
|
||||
if _, err := proBc.InsertChain(prefix); err != nil {
|
||||
|
|
@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
||||
|
|
@ -94,7 +94,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
||||
|
|
@ -120,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
|
||||
|
|
@ -140,7 +140,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ func TestSetupGenesis(t *testing.T) {
|
|||
// Advance to block #4, past the homestead transition block of customg.
|
||||
genesis := oldcustomg.MustCommit(db)
|
||||
|
||||
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil, false)
|
||||
defer bc.Stop()
|
||||
|
||||
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ import (
|
|||
)
|
||||
|
||||
// WriteBlockRowConsumption writes a RowConsumption of the block to the database.
|
||||
func WriteBlockRowConsumption(db ethdb.KeyValueWriter, l2BlockHash common.Hash, rc types.RowConsumption) {
|
||||
func WriteBlockRowConsumption(db ethdb.KeyValueWriter, l2BlockHash common.Hash, rc *types.RowConsumption) {
|
||||
if rc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
bytes, err := rlp.EncodeToBytes(&rc)
|
||||
if err != nil {
|
||||
log.Crit("Failed to RLP encode RowConsumption ", "err", err)
|
||||
|
|
|
|||
|
|
@ -11,9 +11,12 @@ import (
|
|||
|
||||
func TestReadBlockRowConsumption(t *testing.T) {
|
||||
l2BlockHash := common.BigToHash(big.NewInt(10))
|
||||
rc := types.RowConsumption{types.SubCircuitRowConsumption{CircuitName: "aa", Rows: 12}, types.SubCircuitRowConsumption{CircuitName: "bb", Rows: 100}}
|
||||
rc := types.RowConsumption{
|
||||
types.SubCircuitRowUsage{Name: "aa", RowNumber: 12},
|
||||
types.SubCircuitRowUsage{Name: "bb", RowNumber: 100},
|
||||
}
|
||||
db := NewMemoryDatabase()
|
||||
WriteBlockRowConsumption(db, l2BlockHash, rc)
|
||||
WriteBlockRowConsumption(db, l2BlockHash, &rc)
|
||||
got := ReadBlockRowConsumption(db, l2BlockHash)
|
||||
if got == nil || !reflect.DeepEqual(rc, *got) {
|
||||
t.Fatal("RowConsumption mismatch", "expected", rc, "got", got)
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
|
|
@ -246,7 +246,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
for i, tt := range []struct {
|
||||
|
|
@ -286,7 +286,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
for i, tt := range []struct {
|
||||
|
|
@ -341,7 +341,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
||||
smallInitCode = [320]byte{}
|
||||
)
|
||||
|
|
|
|||
493
core/trace.go
Normal file
493
core/trace.go
Normal file
|
|
@ -0,0 +1,493 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/consensus"
|
||||
"github.com/scroll-tech/go-ethereum/core/state"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/fees"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/withdrawtrie"
|
||||
"github.com/scroll-tech/go-ethereum/trie/zkproof"
|
||||
)
|
||||
|
||||
type TraceEnv struct {
|
||||
logConfig *vm.LogConfig
|
||||
chainConfig *params.ChainConfig
|
||||
|
||||
coinbase common.Address
|
||||
|
||||
// rMu lock is used to protect txs executed in parallel.
|
||||
signer types.Signer
|
||||
state *state.StateDB
|
||||
blockCtx vm.BlockContext
|
||||
|
||||
// pMu lock is used to protect Proofs' read and write mutual exclusion,
|
||||
// since txs are executed in parallel, so this lock is required.
|
||||
pMu sync.Mutex
|
||||
// sMu is required because of txs are executed in parallel,
|
||||
// this lock is used to protect StorageTrace's read and write mutual exclusion.
|
||||
sMu sync.Mutex
|
||||
*types.StorageTrace
|
||||
TxStorageTraces []*types.StorageTrace
|
||||
// zktrie tracer is used for zktrie storage to build additional deletion proof
|
||||
ZkTrieTracer map[string]state.ZktrieProofTracer
|
||||
ExecutionResults []*types.ExecutionResult
|
||||
}
|
||||
|
||||
// Context is the same as Context in eth/tracers/tracers.go
|
||||
type Context struct {
|
||||
BlockHash common.Hash
|
||||
TxIndex int
|
||||
TxHash common.Hash
|
||||
}
|
||||
|
||||
// txTraceTask is the same as txTraceTask in eth/tracers/api.go
|
||||
type txTraceTask struct {
|
||||
statedb *state.StateDB
|
||||
index int
|
||||
}
|
||||
|
||||
func CreateTraceEnv(chainConfig *params.ChainConfig, chainContext ChainContext, engine consensus.Engine, statedb *state.StateDB, parent *types.Block, block *types.Block) (*TraceEnv, error) {
|
||||
var coinbase common.Address
|
||||
var err error
|
||||
if chainConfig.Scroll.FeeVaultEnabled() {
|
||||
coinbase = *chainConfig.Scroll.FeeVaultAddress
|
||||
} else {
|
||||
coinbase, err = engine.Author(block.Header())
|
||||
if err != nil {
|
||||
log.Warn("recover coinbase in CreateTraceEnv fail. using zero-address", "err", err, "blockNumber", block.Header().Number, "headerHash", block.Header().Hash())
|
||||
}
|
||||
}
|
||||
|
||||
env := &TraceEnv{
|
||||
logConfig: &vm.LogConfig{
|
||||
EnableMemory: false,
|
||||
EnableReturnData: true,
|
||||
},
|
||||
chainConfig: chainConfig,
|
||||
coinbase: coinbase,
|
||||
signer: types.MakeSigner(chainConfig, block.Number()),
|
||||
state: statedb,
|
||||
blockCtx: NewEVMBlockContext(block.Header(), chainContext, nil),
|
||||
StorageTrace: &types.StorageTrace{
|
||||
RootBefore: parent.Root(),
|
||||
RootAfter: block.Root(),
|
||||
Proofs: make(map[string][]hexutil.Bytes),
|
||||
StorageProofs: make(map[string]map[string][]hexutil.Bytes),
|
||||
},
|
||||
ZkTrieTracer: make(map[string]state.ZktrieProofTracer),
|
||||
ExecutionResults: make([]*types.ExecutionResult, block.Transactions().Len()),
|
||||
TxStorageTraces: make([]*types.StorageTrace, block.Transactions().Len()),
|
||||
}
|
||||
|
||||
key := coinbase.String()
|
||||
if _, exist := env.Proofs[key]; !exist {
|
||||
proof, err := env.state.GetProof(coinbase)
|
||||
if err != nil {
|
||||
log.Error("Proof for coinbase not available", "coinbase", coinbase, "error", err)
|
||||
// but we still mark the proofs map with nil array
|
||||
}
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.Proofs[key] = wrappedProof
|
||||
}
|
||||
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error) {
|
||||
// Execute all the transaction contained within the block concurrently
|
||||
var (
|
||||
txs = block.Transactions()
|
||||
pend = new(sync.WaitGroup)
|
||||
jobs = make(chan *txTraceTask, len(txs))
|
||||
errCh = make(chan error, 1)
|
||||
)
|
||||
threads := runtime.NumCPU()
|
||||
if threads > len(txs) {
|
||||
threads = len(txs)
|
||||
}
|
||||
for th := 0; th < threads; th++ {
|
||||
pend.Add(1)
|
||||
go func() {
|
||||
defer pend.Done()
|
||||
// Fetch and execute the next transaction trace tasks
|
||||
for task := range jobs {
|
||||
if err := env.getTxResult(task.statedb, task.index, block); err != nil {
|
||||
select {
|
||||
case errCh <- err:
|
||||
default:
|
||||
}
|
||||
log.Error("failed to trace tx", "txHash", txs[task.index].Hash().String())
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Feed the transactions into the tracers and return
|
||||
var failed error
|
||||
for i, tx := range txs {
|
||||
// Send the trace task over for execution
|
||||
jobs <- &txTraceTask{statedb: env.state.Copy(), index: i}
|
||||
|
||||
// Generate the next state snapshot fast without tracing
|
||||
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
|
||||
env.state.Prepare(tx.Hash(), i)
|
||||
vmenv := vm.NewEVM(env.blockCtx, NewEVMTxContext(msg), env.state, env.chainConfig, vm.Config{})
|
||||
l1DataFee, err := fees.CalculateL1DataFee(tx, env.state)
|
||||
if err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
if _, err = ApplyMessage(vmenv, msg, new(GasPool).AddGas(msg.Gas()), l1DataFee); err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
// we'd better don't finalise
|
||||
// env.state.Finalise(vmenv.chainConfig().IsEIP158(block.Number()))
|
||||
}
|
||||
close(jobs)
|
||||
pend.Wait()
|
||||
|
||||
// after all tx has been traced, collect "deletion proof" for zktrie
|
||||
for _, tracer := range env.ZkTrieTracer {
|
||||
delProofs, err := tracer.GetDeletionProofs()
|
||||
if err != nil {
|
||||
log.Error("deletion proof failure", "error", err)
|
||||
} else {
|
||||
for _, proof := range delProofs {
|
||||
env.DeletionProofs = append(env.DeletionProofs, proof)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build dummy per-tx deletion proof
|
||||
for _, txStorageTrace := range env.TxStorageTraces {
|
||||
if txStorageTrace != nil {
|
||||
txStorageTrace.DeletionProofs = env.DeletionProofs
|
||||
}
|
||||
}
|
||||
|
||||
// If execution failed in between, abort
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return nil, err
|
||||
default:
|
||||
if failed != nil {
|
||||
return nil, failed
|
||||
}
|
||||
}
|
||||
|
||||
return env.fillBlockTrace(block)
|
||||
}
|
||||
|
||||
func (env *TraceEnv) getTxResult(state *state.StateDB, index int, block *types.Block) error {
|
||||
tx := block.Transactions()[index]
|
||||
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
|
||||
from, _ := types.Sender(env.signer, tx)
|
||||
to := tx.To()
|
||||
|
||||
txctx := &Context{
|
||||
BlockHash: block.TxHash(),
|
||||
TxIndex: index,
|
||||
TxHash: tx.Hash(),
|
||||
}
|
||||
|
||||
sender := &types.AccountWrapper{
|
||||
Address: from,
|
||||
Nonce: state.GetNonce(from),
|
||||
Balance: (*hexutil.Big)(state.GetBalance(from)),
|
||||
KeccakCodeHash: state.GetKeccakCodeHash(from),
|
||||
PoseidonCodeHash: state.GetPoseidonCodeHash(from),
|
||||
CodeSize: state.GetCodeSize(from),
|
||||
}
|
||||
var receiver *types.AccountWrapper
|
||||
if to != nil {
|
||||
receiver = &types.AccountWrapper{
|
||||
Address: *to,
|
||||
Nonce: state.GetNonce(*to),
|
||||
Balance: (*hexutil.Big)(state.GetBalance(*to)),
|
||||
KeccakCodeHash: state.GetKeccakCodeHash(*to),
|
||||
PoseidonCodeHash: state.GetPoseidonCodeHash(*to),
|
||||
CodeSize: state.GetCodeSize(*to),
|
||||
}
|
||||
}
|
||||
|
||||
tracer := vm.NewStructLogger(env.logConfig)
|
||||
// Run the transaction with tracing enabled.
|
||||
vmenv := vm.NewEVM(env.blockCtx, NewEVMTxContext(msg), state, env.chainConfig, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
|
||||
|
||||
// Call Prepare to clear out the statedb access list
|
||||
state.Prepare(txctx.TxHash, txctx.TxIndex)
|
||||
|
||||
// Computes the new state by applying the given message.
|
||||
l1DataFee, err := fees.CalculateL1DataFee(tx, state)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tracing failed: %w", err)
|
||||
}
|
||||
result, err := ApplyMessage(vmenv, msg, new(GasPool).AddGas(msg.Gas()), l1DataFee)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tracing failed: %w", err)
|
||||
}
|
||||
// If the result contains a revert reason, return it.
|
||||
returnVal := result.Return()
|
||||
if len(result.Revert()) > 0 {
|
||||
returnVal = result.Revert()
|
||||
}
|
||||
|
||||
createdAcc := tracer.CreatedAccount()
|
||||
var after []*types.AccountWrapper
|
||||
if to == nil {
|
||||
if createdAcc == nil {
|
||||
return errors.New("unexpected tx: address for created contract unavailable")
|
||||
}
|
||||
to = &createdAcc.Address
|
||||
}
|
||||
// collect affected account after tx being applied
|
||||
for _, acc := range []common.Address{from, *to, env.coinbase} {
|
||||
after = append(after, &types.AccountWrapper{
|
||||
Address: acc,
|
||||
Nonce: state.GetNonce(acc),
|
||||
Balance: (*hexutil.Big)(state.GetBalance(acc)),
|
||||
KeccakCodeHash: state.GetKeccakCodeHash(acc),
|
||||
PoseidonCodeHash: state.GetPoseidonCodeHash(acc),
|
||||
CodeSize: state.GetCodeSize(acc),
|
||||
})
|
||||
}
|
||||
|
||||
txStorageTrace := &types.StorageTrace{
|
||||
Proofs: make(map[string][]hexutil.Bytes),
|
||||
StorageProofs: make(map[string]map[string][]hexutil.Bytes),
|
||||
}
|
||||
// still we have no state root for per tx, only set the head and tail
|
||||
if index == 0 {
|
||||
txStorageTrace.RootBefore = state.GetRootHash()
|
||||
} else if index == len(block.Transactions())-1 {
|
||||
txStorageTrace.RootAfter = block.Root()
|
||||
}
|
||||
|
||||
// merge required proof data
|
||||
proofAccounts := tracer.UpdatedAccounts()
|
||||
proofAccounts[vmenv.FeeRecipient()] = struct{}{}
|
||||
for addr := range proofAccounts {
|
||||
addrStr := addr.String()
|
||||
|
||||
env.pMu.Lock()
|
||||
checkedProof, existed := env.Proofs[addrStr]
|
||||
if existed {
|
||||
txStorageTrace.Proofs[addrStr] = checkedProof
|
||||
}
|
||||
env.pMu.Unlock()
|
||||
if existed {
|
||||
continue
|
||||
}
|
||||
proof, err := state.GetProof(addr)
|
||||
if err != nil {
|
||||
log.Error("Proof not available", "address", addrStr, "error", err)
|
||||
// but we still mark the proofs map with nil array
|
||||
}
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.pMu.Lock()
|
||||
env.Proofs[addrStr] = wrappedProof
|
||||
txStorageTrace.Proofs[addrStr] = wrappedProof
|
||||
env.pMu.Unlock()
|
||||
}
|
||||
|
||||
proofStorages := tracer.UpdatedStorages()
|
||||
for addr, keys := range proofStorages {
|
||||
if _, existed := txStorageTrace.StorageProofs[addr.String()]; !existed {
|
||||
txStorageTrace.StorageProofs[addr.String()] = make(map[string][]hexutil.Bytes)
|
||||
}
|
||||
|
||||
env.sMu.Lock()
|
||||
trie, err := state.GetStorageTrieForProof(addr)
|
||||
if err != nil {
|
||||
// but we still continue to next address
|
||||
log.Error("Storage trie not available", "error", err, "address", addr)
|
||||
env.sMu.Unlock()
|
||||
continue
|
||||
}
|
||||
zktrieTracer := state.NewProofTracer(trie)
|
||||
env.sMu.Unlock()
|
||||
|
||||
for key, values := range keys {
|
||||
addrStr := addr.String()
|
||||
keyStr := key.String()
|
||||
isDelete := bytes.Equal(values.Bytes(), common.Hash{}.Bytes())
|
||||
|
||||
txm := txStorageTrace.StorageProofs[addrStr]
|
||||
env.sMu.Lock()
|
||||
m, existed := env.StorageProofs[addrStr]
|
||||
if !existed {
|
||||
m = make(map[string][]hexutil.Bytes)
|
||||
env.StorageProofs[addrStr] = m
|
||||
if zktrieTracer.Available() {
|
||||
env.ZkTrieTracer[addrStr] = state.NewProofTracer(trie)
|
||||
}
|
||||
} else if proof, existed := m[keyStr]; existed {
|
||||
txm[keyStr] = proof
|
||||
// still need to touch tracer for deletion
|
||||
if isDelete && zktrieTracer.Available() {
|
||||
env.ZkTrieTracer[addrStr].MarkDeletion(key)
|
||||
}
|
||||
env.sMu.Unlock()
|
||||
continue
|
||||
}
|
||||
env.sMu.Unlock()
|
||||
|
||||
var proof [][]byte
|
||||
var err error
|
||||
if zktrieTracer.Available() {
|
||||
proof, err = state.GetSecureTrieProof(zktrieTracer, key)
|
||||
} else {
|
||||
proof, err = state.GetSecureTrieProof(trie, key)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Storage proof not available", "error", err, "address", addrStr, "key", keyStr)
|
||||
// but we still mark the proofs map with nil array
|
||||
}
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.sMu.Lock()
|
||||
txm[keyStr] = wrappedProof
|
||||
m[keyStr] = wrappedProof
|
||||
if zktrieTracer.Available() {
|
||||
if isDelete {
|
||||
zktrieTracer.MarkDeletion(key)
|
||||
}
|
||||
env.ZkTrieTracer[addrStr].Merge(zktrieTracer)
|
||||
}
|
||||
env.sMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
env.ExecutionResults[index] = &types.ExecutionResult{
|
||||
From: sender,
|
||||
To: receiver,
|
||||
AccountCreated: createdAcc,
|
||||
AccountsAfter: after,
|
||||
L1DataFee: (*hexutil.Big)(result.L1DataFee),
|
||||
Gas: result.UsedGas,
|
||||
Failed: result.Failed(),
|
||||
ReturnValue: fmt.Sprintf("%x", returnVal),
|
||||
StructLogs: vm.FormatLogs(tracer.StructLogs()),
|
||||
}
|
||||
env.TxStorageTraces[index] = txStorageTrace
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fillBlockTrace content after all the txs are finished running.
|
||||
func (env *TraceEnv) fillBlockTrace(block *types.Block) (*types.BlockTrace, error) {
|
||||
statedb := env.state
|
||||
|
||||
txs := make([]*types.TransactionData, block.Transactions().Len())
|
||||
for i, tx := range block.Transactions() {
|
||||
txs[i] = types.NewTransactionData(tx, block.NumberU64(), env.chainConfig)
|
||||
}
|
||||
|
||||
intrinsicStorageProofs := map[common.Address][]common.Hash{
|
||||
rcfg.L2MessageQueueAddress: {rcfg.WithdrawTrieRootSlot},
|
||||
rcfg.L1GasPriceOracleAddress: {
|
||||
rcfg.L1BaseFeeSlot,
|
||||
rcfg.OverheadSlot,
|
||||
rcfg.ScalarSlot,
|
||||
},
|
||||
}
|
||||
|
||||
for addr, storages := range intrinsicStorageProofs {
|
||||
if _, existed := env.Proofs[addr.String()]; !existed {
|
||||
if proof, err := statedb.GetProof(addr); err != nil {
|
||||
log.Error("Proof for intrinstic address not available", "error", err, "address", addr)
|
||||
} else {
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.Proofs[addr.String()] = wrappedProof
|
||||
}
|
||||
}
|
||||
|
||||
if _, existed := env.StorageProofs[addr.String()]; !existed {
|
||||
env.StorageProofs[addr.String()] = make(map[string][]hexutil.Bytes)
|
||||
}
|
||||
|
||||
for _, slot := range storages {
|
||||
if _, existed := env.StorageProofs[addr.String()][slot.String()]; !existed {
|
||||
if trie, err := statedb.GetStorageTrieForProof(addr); err != nil {
|
||||
log.Error("Storage proof for intrinstic address not available", "error", err, "address", addr)
|
||||
} else if proof, _ := statedb.GetSecureTrieProof(trie, slot); err != nil {
|
||||
log.Error("Get storage proof for intrinstic address failed", "error", err, "address", addr, "slot", slot)
|
||||
} else {
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.StorageProofs[addr.String()][slot.String()] = wrappedProof
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blockTrace := &types.BlockTrace{
|
||||
ChainID: env.chainConfig.ChainID.Uint64(),
|
||||
Version: params.ArchiveVersion(params.CommitHash),
|
||||
Coinbase: &types.AccountWrapper{
|
||||
Address: env.coinbase,
|
||||
Nonce: statedb.GetNonce(env.coinbase),
|
||||
Balance: (*hexutil.Big)(statedb.GetBalance(env.coinbase)),
|
||||
KeccakCodeHash: statedb.GetKeccakCodeHash(env.coinbase),
|
||||
PoseidonCodeHash: statedb.GetPoseidonCodeHash(env.coinbase),
|
||||
CodeSize: statedb.GetCodeSize(env.coinbase),
|
||||
},
|
||||
Header: block.Header(),
|
||||
StorageTrace: env.StorageTrace,
|
||||
ExecutionResults: env.ExecutionResults,
|
||||
TxStorageTraces: env.TxStorageTraces,
|
||||
Transactions: txs,
|
||||
}
|
||||
|
||||
for i, tx := range block.Transactions() {
|
||||
evmTrace := env.ExecutionResults[i]
|
||||
// probably a Contract Call
|
||||
if len(tx.Data()) != 0 && tx.To() != nil {
|
||||
evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To()))
|
||||
// Get tx.to address's code hash.
|
||||
codeHash := statedb.GetPoseidonCodeHash(*tx.To())
|
||||
evmTrace.PoseidonCodeHash = &codeHash
|
||||
} else if tx.To() == nil { // Contract is created.
|
||||
evmTrace.ByteCode = hexutil.Encode(tx.Data())
|
||||
}
|
||||
}
|
||||
|
||||
// only zktrie model has the ability to get `mptwitness`.
|
||||
if env.chainConfig.Scroll.ZktrieEnabled() {
|
||||
// we use MPTWitnessNothing by default and do not allow switch among MPTWitnessType atm.
|
||||
// MPTWitness will be removed from traces in the future.
|
||||
if err := zkproof.FillBlockTraceForMPTWitness(zkproof.MPTWitnessNothing, blockTrace); err != nil {
|
||||
log.Error("fill mpt witness fail", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
blockTrace.WithdrawTrieRoot = withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, env.state)
|
||||
|
||||
return blockTrace, nil
|
||||
}
|
||||
|
|
@ -397,16 +397,39 @@ func (b *Block) Hash() common.Hash {
|
|||
return v
|
||||
}
|
||||
|
||||
// L1MessageCount returns the number of L1 messages in this block.
|
||||
func (b *Block) L1MessageCount() int {
|
||||
// ContainsL1Messages returns true if this block contains at least one L1 message.
|
||||
func (b *Block) ContainsL1Messages() bool {
|
||||
for _, tx := range b.transactions {
|
||||
if tx.IsL1MessageTx() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NumL1MessagesProcessed returns the number of L1 messages processed in this block.
|
||||
// This count includes both skipped and included messages.
|
||||
// `firstQueueIndex` is the first queue index available for this block to process.
|
||||
func (b *Block) NumL1MessagesProcessed(firstQueueIndex uint64) int {
|
||||
if l1MsgCount := b.l1MsgCount.Load(); l1MsgCount != nil {
|
||||
return l1MsgCount.(int)
|
||||
}
|
||||
count := 0
|
||||
for _, tx := range b.transactions {
|
||||
if tx.IsL1MessageTx() {
|
||||
count += 1
|
||||
|
||||
// find first and last queue index in block
|
||||
var lastQueueIndex *uint64
|
||||
|
||||
for ii, tx := range b.transactions {
|
||||
if !tx.IsL1MessageTx() {
|
||||
break
|
||||
}
|
||||
lastQueueIndex = &b.transactions[ii].AsL1MessageTx().QueueIndex
|
||||
}
|
||||
|
||||
// calculate and cache L1 message count
|
||||
count := 0
|
||||
if lastQueueIndex != nil {
|
||||
// lastQueueIndex is guaranteed to be non-nil in this case
|
||||
count = int(*lastQueueIndex - firstQueueIndex + 1)
|
||||
}
|
||||
b.l1MsgCount.Store(count)
|
||||
return count
|
||||
|
|
@ -414,7 +437,13 @@ func (b *Block) L1MessageCount() int {
|
|||
|
||||
// CountL2Tx returns the number of L2 transactions in this block.
|
||||
func (b *Block) CountL2Tx() int {
|
||||
return len(b.transactions) - b.L1MessageCount()
|
||||
count := 0
|
||||
for _, tx := range b.transactions {
|
||||
if !tx.IsL1MessageTx() {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
type Blocks []*Block
|
||||
|
|
|
|||
|
|
@ -9,37 +9,37 @@ import (
|
|||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
var _ = (*subCircuitRowConsumptionMarshaling)(nil)
|
||||
var _ = (*subCircuitRowUsageMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (s SubCircuitRowConsumption) MarshalJSON() ([]byte, error) {
|
||||
type SubCircuitRowConsumption struct {
|
||||
CircuitName string `json:"circuitName" gencodec:"required"`
|
||||
Rows hexutil.Uint64 `json:"rows" gencodec:"required"`
|
||||
func (s SubCircuitRowUsage) MarshalJSON() ([]byte, error) {
|
||||
type SubCircuitRowUsage struct {
|
||||
Name string `json:"name" gencodec:"required"`
|
||||
RowNumber hexutil.Uint64 `json:"row_number" gencodec:"required"`
|
||||
}
|
||||
var enc SubCircuitRowConsumption
|
||||
enc.CircuitName = s.CircuitName
|
||||
enc.Rows = hexutil.Uint64(s.Rows)
|
||||
var enc SubCircuitRowUsage
|
||||
enc.Name = s.Name
|
||||
enc.RowNumber = hexutil.Uint64(s.RowNumber)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (s *SubCircuitRowConsumption) UnmarshalJSON(input []byte) error {
|
||||
type SubCircuitRowConsumption struct {
|
||||
CircuitName *string `json:"circuitName" gencodec:"required"`
|
||||
Rows *hexutil.Uint64 `json:"rows" gencodec:"required"`
|
||||
func (s *SubCircuitRowUsage) UnmarshalJSON(input []byte) error {
|
||||
type SubCircuitRowUsage struct {
|
||||
Name *string `json:"name" gencodec:"required"`
|
||||
RowNumber *hexutil.Uint64 `json:"row_number" gencodec:"required"`
|
||||
}
|
||||
var dec SubCircuitRowConsumption
|
||||
var dec SubCircuitRowUsage
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.CircuitName == nil {
|
||||
return errors.New("missing required field 'circuitName' for SubCircuitRowConsumption")
|
||||
if dec.Name == nil {
|
||||
return errors.New("missing required field 'name' for SubCircuitRowUsage")
|
||||
}
|
||||
s.CircuitName = *dec.CircuitName
|
||||
if dec.Rows == nil {
|
||||
return errors.New("missing required field 'rows' for SubCircuitRowConsumption")
|
||||
s.Name = *dec.Name
|
||||
if dec.RowNumber == nil {
|
||||
return errors.New("missing required field 'row_number' for SubCircuitRowUsage")
|
||||
}
|
||||
s.Rows = uint64(*dec.Rows)
|
||||
s.RowNumber = uint64(*dec.RowNumber)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,3 +191,12 @@ func NewTransactionData(tx *Transaction, blockNumber uint64, config *params.Chai
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// WrapProof turn the bytes array into proof type (array of hexutil.Bytes)
|
||||
func WrapProof(proofBytes [][]byte) (wrappedProof []hexutil.Bytes) {
|
||||
wrappedProof = make([]hexutil.Bytes, len(proofBytes))
|
||||
for i, bt := range proofBytes {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,21 @@ import (
|
|||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
//go:generate gencodec -type SubCircuitRowConsumption -field-override subCircuitRowConsumptionMarshaling -out gen_row_consumption_json.go
|
||||
type SubCircuitRowConsumption struct {
|
||||
CircuitName string `json:"circuitName" gencodec:"required"`
|
||||
Rows uint64 `json:"rows" gencodec:"required"`
|
||||
type RowUsage struct {
|
||||
IsOk bool `json:"is_ok"`
|
||||
RowNumber uint64 `json:"row_number"`
|
||||
RowUsageDetails []SubCircuitRowUsage `json:"row_usage_details"`
|
||||
}
|
||||
|
||||
type RowConsumption []SubCircuitRowConsumption
|
||||
//go:generate gencodec -type SubCircuitRowUsage -field-override subCircuitRowUsageMarshaling -out gen_row_consumption_json.go
|
||||
type SubCircuitRowUsage struct {
|
||||
Name string `json:"name" gencodec:"required"`
|
||||
RowNumber uint64 `json:"row_number" gencodec:"required"`
|
||||
}
|
||||
|
||||
type RowConsumption []SubCircuitRowUsage
|
||||
|
||||
// field type overrides for gencodec
|
||||
type subCircuitRowConsumptionMarshaling struct {
|
||||
Rows hexutil.Uint64
|
||||
type subCircuitRowUsageMarshaling struct {
|
||||
RowNumber hexutil.Uint64
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client sync_service.EthCl
|
|||
MPTWitness: config.MPTWitness,
|
||||
}
|
||||
)
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, config.CheckCircuitCapacity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,6 +207,9 @@ type Config struct {
|
|||
|
||||
// Trace option
|
||||
MPTWitness int
|
||||
|
||||
// Check circuit capacity in block validator
|
||||
CheckCircuitCapacity bool
|
||||
}
|
||||
|
||||
// CreateConsensusEngine creates a consensus engine for the given chain configuration.
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
|
|||
// Construct testing chain
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.Commit(diskdb)
|
||||
chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true}, &config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true}, &config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create local chain, %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ func testForkIDSplit(t *testing.T, protocol uint) {
|
|||
genesisNoFork = gspecNoFork.MustCommit(dbNoFork)
|
||||
genesisProFork = gspecProFork.MustCommit(dbProFork)
|
||||
|
||||
chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil)
|
||||
chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil)
|
||||
chainNoFork, _ = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil, false)
|
||||
chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil, false)
|
||||
|
||||
blocksNoFork, _ = core.GenerateChain(configNoFork, genesisNoFork, engine, dbNoFork, 2, nil)
|
||||
blocksProFork, _ = core.GenerateChain(configProFork, genesisProFork, engine, dbProFork, 2, nil)
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler {
|
|||
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}},
|
||||
}).MustCommit(db)
|
||||
|
||||
chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
|
||||
bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, nil)
|
||||
if _, err := chain.InsertChain(bs); err != nil {
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ func newTestBackendWithGenerator(blocks int, generator func(int, *core.BlockGen)
|
|||
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100_000_000_000_000_000)}},
|
||||
}).MustCommit(db)
|
||||
|
||||
chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
|
||||
bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, blocks, generator)
|
||||
if _, err := chain.InsertChain(bs); err != nil {
|
||||
|
|
|
|||
|
|
@ -1,55 +1,20 @@
|
|||
package tracers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/common"
|
||||
"github.com/scroll-tech/go-ethereum/common/hexutil"
|
||||
"github.com/scroll-tech/go-ethereum/core"
|
||||
"github.com/scroll-tech/go-ethereum/core/state"
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/core/vm"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/fees"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/withdrawtrie"
|
||||
"github.com/scroll-tech/go-ethereum/rpc"
|
||||
"github.com/scroll-tech/go-ethereum/trie/zkproof"
|
||||
)
|
||||
|
||||
type TraceBlock interface {
|
||||
GetBlockTraceByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (trace *types.BlockTrace, err error)
|
||||
}
|
||||
|
||||
type traceEnv struct {
|
||||
config *TraceConfig
|
||||
|
||||
coinbase common.Address
|
||||
|
||||
// rMu lock is used to protect txs executed in parallel.
|
||||
signer types.Signer
|
||||
state *state.StateDB
|
||||
blockCtx vm.BlockContext
|
||||
|
||||
// pMu lock is used to protect Proofs' read and write mutual exclusion,
|
||||
// since txs are executed in parallel, so this lock is required.
|
||||
pMu sync.Mutex
|
||||
// sMu is required because of txs are executed in parallel,
|
||||
// this lock is used to protect StorageTrace's read and write mutual exclusion.
|
||||
sMu sync.Mutex
|
||||
*types.StorageTrace
|
||||
txStorageTraces []*types.StorageTrace
|
||||
// zktrie tracer is used for zktrie storage to build additional deletion proof
|
||||
zkTrieTracer map[string]state.ZktrieProofTracer
|
||||
executionResults []*types.ExecutionResult
|
||||
}
|
||||
|
||||
// GetBlockTraceByNumberOrHash replays the block and returns the structured BlockTrace by hash or number.
|
||||
func (api *API) GetBlockTraceByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (trace *types.BlockTrace, err error) {
|
||||
var block *types.Block
|
||||
|
|
@ -83,11 +48,11 @@ func (api *API) GetBlockTraceByNumberOrHash(ctx context.Context, blockNrOrHash r
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return api.getBlockTrace(block, env)
|
||||
return env.GetBlockTrace(block)
|
||||
}
|
||||
|
||||
// Make trace environment for current block.
|
||||
func (api *API) createTraceEnv(ctx context.Context, config *TraceConfig, block *types.Block) (*traceEnv, error) {
|
||||
func (api *API) createTraceEnv(ctx context.Context, config *TraceConfig, block *types.Block) (*core.TraceEnv, error) {
|
||||
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -100,431 +65,5 @@ func (api *API) createTraceEnv(ctx context.Context, config *TraceConfig, block *
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get coinbase
|
||||
var coinbase common.Address
|
||||
if api.backend.ChainConfig().Scroll.FeeVaultEnabled() {
|
||||
coinbase = *api.backend.ChainConfig().Scroll.FeeVaultAddress
|
||||
} else {
|
||||
coinbase, err = api.backend.Engine().Author(block.Header())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
env := &traceEnv{
|
||||
config: config,
|
||||
coinbase: coinbase,
|
||||
signer: types.MakeSigner(api.backend.ChainConfig(), block.Number()),
|
||||
state: statedb,
|
||||
blockCtx: core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil),
|
||||
StorageTrace: &types.StorageTrace{
|
||||
RootBefore: parent.Root(),
|
||||
RootAfter: block.Root(),
|
||||
Proofs: make(map[string][]hexutil.Bytes),
|
||||
StorageProofs: make(map[string]map[string][]hexutil.Bytes),
|
||||
},
|
||||
zkTrieTracer: make(map[string]state.ZktrieProofTracer),
|
||||
executionResults: make([]*types.ExecutionResult, block.Transactions().Len()),
|
||||
txStorageTraces: make([]*types.StorageTrace, block.Transactions().Len()),
|
||||
}
|
||||
|
||||
key := coinbase.String()
|
||||
if _, exist := env.Proofs[key]; !exist {
|
||||
proof, err := env.state.GetProof(coinbase)
|
||||
if err != nil {
|
||||
log.Error("Proof for coinbase not available", "coinbase", coinbase, "error", err)
|
||||
// but we still mark the proofs map with nil array
|
||||
}
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.Proofs[key] = wrappedProof
|
||||
}
|
||||
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func (api *API) getBlockTrace(block *types.Block, env *traceEnv) (*types.BlockTrace, error) {
|
||||
// Execute all the transaction contained within the block concurrently
|
||||
var (
|
||||
txs = block.Transactions()
|
||||
pend = new(sync.WaitGroup)
|
||||
jobs = make(chan *txTraceTask, len(txs))
|
||||
errCh = make(chan error, 1)
|
||||
)
|
||||
threads := runtime.NumCPU()
|
||||
if threads > len(txs) {
|
||||
threads = len(txs)
|
||||
}
|
||||
for th := 0; th < threads; th++ {
|
||||
pend.Add(1)
|
||||
go func() {
|
||||
defer pend.Done()
|
||||
// Fetch and execute the next transaction trace tasks
|
||||
for task := range jobs {
|
||||
if err := api.getTxResult(env, task.statedb, task.index, block); err != nil {
|
||||
select {
|
||||
case errCh <- err:
|
||||
default:
|
||||
}
|
||||
log.Error("failed to trace tx", "txHash", txs[task.index].Hash().String())
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Feed the transactions into the tracers and return
|
||||
var failed error
|
||||
for i, tx := range txs {
|
||||
// Send the trace task over for execution
|
||||
jobs <- &txTraceTask{statedb: env.state.Copy(), index: i}
|
||||
|
||||
// Generate the next state snapshot fast without tracing
|
||||
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
|
||||
env.state.Prepare(tx.Hash(), i)
|
||||
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), env.state, api.backend.ChainConfig(), vm.Config{})
|
||||
l1DataFee, err := fees.CalculateL1DataFee(tx, env.state)
|
||||
if err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee); err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
env.state.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
}
|
||||
close(jobs)
|
||||
pend.Wait()
|
||||
|
||||
// after all tx has been traced, collect "deletion proof" for zktrie
|
||||
for _, tracer := range env.zkTrieTracer {
|
||||
delProofs, err := tracer.GetDeletionProofs()
|
||||
if err != nil {
|
||||
log.Error("deletion proof failure", "error", err)
|
||||
} else {
|
||||
for _, proof := range delProofs {
|
||||
env.DeletionProofs = append(env.DeletionProofs, proof)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build dummy per-tx deletion proof
|
||||
for _, txStorageTrace := range env.txStorageTraces {
|
||||
if txStorageTrace != nil {
|
||||
txStorageTrace.DeletionProofs = env.DeletionProofs
|
||||
}
|
||||
}
|
||||
|
||||
// If execution failed in between, abort
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return nil, err
|
||||
default:
|
||||
if failed != nil {
|
||||
return nil, failed
|
||||
}
|
||||
}
|
||||
|
||||
return api.fillBlockTrace(env, block)
|
||||
}
|
||||
|
||||
func (api *API) getTxResult(env *traceEnv, state *state.StateDB, index int, block *types.Block) error {
|
||||
tx := block.Transactions()[index]
|
||||
msg, _ := tx.AsMessage(env.signer, block.BaseFee())
|
||||
from, _ := types.Sender(env.signer, tx)
|
||||
to := tx.To()
|
||||
|
||||
txctx := &Context{
|
||||
BlockHash: block.TxHash(),
|
||||
TxIndex: index,
|
||||
TxHash: tx.Hash(),
|
||||
}
|
||||
|
||||
sender := &types.AccountWrapper{
|
||||
Address: from,
|
||||
Nonce: state.GetNonce(from),
|
||||
Balance: (*hexutil.Big)(state.GetBalance(from)),
|
||||
KeccakCodeHash: state.GetKeccakCodeHash(from),
|
||||
PoseidonCodeHash: state.GetPoseidonCodeHash(from),
|
||||
CodeSize: state.GetCodeSize(from),
|
||||
}
|
||||
var receiver *types.AccountWrapper
|
||||
if to != nil {
|
||||
receiver = &types.AccountWrapper{
|
||||
Address: *to,
|
||||
Nonce: state.GetNonce(*to),
|
||||
Balance: (*hexutil.Big)(state.GetBalance(*to)),
|
||||
KeccakCodeHash: state.GetKeccakCodeHash(*to),
|
||||
PoseidonCodeHash: state.GetPoseidonCodeHash(*to),
|
||||
CodeSize: state.GetCodeSize(*to),
|
||||
}
|
||||
}
|
||||
|
||||
tracer := vm.NewStructLogger(env.config.LogConfig)
|
||||
// Run the transaction with tracing enabled.
|
||||
vmenv := vm.NewEVM(env.blockCtx, core.NewEVMTxContext(msg), state, api.backend.ChainConfig(), vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
|
||||
|
||||
// Call Prepare to clear out the statedb access list
|
||||
state.Prepare(txctx.TxHash, txctx.TxIndex)
|
||||
|
||||
// Computes the new state by applying the given message.
|
||||
l1DataFee, err := fees.CalculateL1DataFee(tx, state)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tracing failed: %w", err)
|
||||
}
|
||||
result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), l1DataFee)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tracing failed: %w", err)
|
||||
}
|
||||
// If the result contains a revert reason, return it.
|
||||
returnVal := result.Return()
|
||||
if len(result.Revert()) > 0 {
|
||||
returnVal = result.Revert()
|
||||
}
|
||||
|
||||
createdAcc := tracer.CreatedAccount()
|
||||
var after []*types.AccountWrapper
|
||||
if to == nil {
|
||||
if createdAcc == nil {
|
||||
return errors.New("unexpected tx: address for created contract unavailable")
|
||||
}
|
||||
to = &createdAcc.Address
|
||||
}
|
||||
// collect affected account after tx being applied
|
||||
for _, acc := range []common.Address{from, *to, env.coinbase} {
|
||||
after = append(after, &types.AccountWrapper{
|
||||
Address: acc,
|
||||
Nonce: state.GetNonce(acc),
|
||||
Balance: (*hexutil.Big)(state.GetBalance(acc)),
|
||||
KeccakCodeHash: state.GetKeccakCodeHash(acc),
|
||||
PoseidonCodeHash: state.GetPoseidonCodeHash(acc),
|
||||
CodeSize: state.GetCodeSize(acc),
|
||||
})
|
||||
}
|
||||
|
||||
txStorageTrace := &types.StorageTrace{
|
||||
Proofs: make(map[string][]hexutil.Bytes),
|
||||
StorageProofs: make(map[string]map[string][]hexutil.Bytes),
|
||||
}
|
||||
// still we have no state root for per tx, only set the head and tail
|
||||
if index == 0 {
|
||||
txStorageTrace.RootBefore = state.GetRootHash()
|
||||
} else if index == len(block.Transactions())-1 {
|
||||
txStorageTrace.RootAfter = block.Root()
|
||||
}
|
||||
|
||||
// merge required proof data
|
||||
proofAccounts := tracer.UpdatedAccounts()
|
||||
proofAccounts[vmenv.FeeRecipient()] = struct{}{}
|
||||
for addr := range proofAccounts {
|
||||
addrStr := addr.String()
|
||||
|
||||
env.pMu.Lock()
|
||||
checkedProof, existed := env.Proofs[addrStr]
|
||||
if existed {
|
||||
txStorageTrace.Proofs[addrStr] = checkedProof
|
||||
}
|
||||
env.pMu.Unlock()
|
||||
if existed {
|
||||
continue
|
||||
}
|
||||
proof, err := state.GetProof(addr)
|
||||
if err != nil {
|
||||
log.Error("Proof not available", "address", addrStr, "error", err)
|
||||
// but we still mark the proofs map with nil array
|
||||
}
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.pMu.Lock()
|
||||
env.Proofs[addrStr] = wrappedProof
|
||||
txStorageTrace.Proofs[addrStr] = wrappedProof
|
||||
env.pMu.Unlock()
|
||||
}
|
||||
|
||||
proofStorages := tracer.UpdatedStorages()
|
||||
for addr, keys := range proofStorages {
|
||||
if _, existed := txStorageTrace.StorageProofs[addr.String()]; !existed {
|
||||
txStorageTrace.StorageProofs[addr.String()] = make(map[string][]hexutil.Bytes)
|
||||
}
|
||||
|
||||
env.sMu.Lock()
|
||||
trie, err := state.GetStorageTrieForProof(addr)
|
||||
if err != nil {
|
||||
// but we still continue to next address
|
||||
log.Error("Storage trie not available", "error", err, "address", addr)
|
||||
env.sMu.Unlock()
|
||||
continue
|
||||
}
|
||||
zktrieTracer := state.NewProofTracer(trie)
|
||||
env.sMu.Unlock()
|
||||
|
||||
for key, values := range keys {
|
||||
addrStr := addr.String()
|
||||
keyStr := key.String()
|
||||
isDelete := bytes.Equal(values.Bytes(), common.Hash{}.Bytes())
|
||||
|
||||
txm := txStorageTrace.StorageProofs[addrStr]
|
||||
env.sMu.Lock()
|
||||
m, existed := env.StorageProofs[addrStr]
|
||||
if !existed {
|
||||
m = make(map[string][]hexutil.Bytes)
|
||||
env.StorageProofs[addrStr] = m
|
||||
if zktrieTracer.Available() {
|
||||
env.zkTrieTracer[addrStr] = state.NewProofTracer(trie)
|
||||
}
|
||||
} else if proof, existed := m[keyStr]; existed {
|
||||
txm[keyStr] = proof
|
||||
// still need to touch tracer for deletion
|
||||
if isDelete && zktrieTracer.Available() {
|
||||
env.zkTrieTracer[addrStr].MarkDeletion(key)
|
||||
}
|
||||
env.sMu.Unlock()
|
||||
continue
|
||||
}
|
||||
env.sMu.Unlock()
|
||||
|
||||
var proof [][]byte
|
||||
var err error
|
||||
if zktrieTracer.Available() {
|
||||
proof, err = state.GetSecureTrieProof(zktrieTracer, key)
|
||||
} else {
|
||||
proof, err = state.GetSecureTrieProof(trie, key)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Storage proof not available", "error", err, "address", addrStr, "key", keyStr)
|
||||
// but we still mark the proofs map with nil array
|
||||
}
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.sMu.Lock()
|
||||
txm[keyStr] = wrappedProof
|
||||
m[keyStr] = wrappedProof
|
||||
if zktrieTracer.Available() {
|
||||
if isDelete {
|
||||
zktrieTracer.MarkDeletion(key)
|
||||
}
|
||||
env.zkTrieTracer[addrStr].Merge(zktrieTracer)
|
||||
}
|
||||
env.sMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
env.executionResults[index] = &types.ExecutionResult{
|
||||
From: sender,
|
||||
To: receiver,
|
||||
AccountCreated: createdAcc,
|
||||
AccountsAfter: after,
|
||||
L1DataFee: (*hexutil.Big)(result.L1DataFee),
|
||||
Gas: result.UsedGas,
|
||||
Failed: result.Failed(),
|
||||
ReturnValue: fmt.Sprintf("%x", returnVal),
|
||||
StructLogs: vm.FormatLogs(tracer.StructLogs()),
|
||||
}
|
||||
env.txStorageTraces[index] = txStorageTrace
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fill blockTrace content after all the txs are finished running.
|
||||
func (api *API) fillBlockTrace(env *traceEnv, block *types.Block) (*types.BlockTrace, error) {
|
||||
statedb := env.state
|
||||
|
||||
txs := make([]*types.TransactionData, block.Transactions().Len())
|
||||
for i, tx := range block.Transactions() {
|
||||
txs[i] = types.NewTransactionData(tx, block.NumberU64(), api.backend.ChainConfig())
|
||||
}
|
||||
|
||||
intrinsicStorageProofs := map[common.Address][]common.Hash{
|
||||
rcfg.L2MessageQueueAddress: {rcfg.WithdrawTrieRootSlot},
|
||||
rcfg.L1GasPriceOracleAddress: {
|
||||
rcfg.L1BaseFeeSlot,
|
||||
rcfg.OverheadSlot,
|
||||
rcfg.ScalarSlot,
|
||||
},
|
||||
}
|
||||
|
||||
for addr, storages := range intrinsicStorageProofs {
|
||||
if _, existed := env.Proofs[addr.String()]; !existed {
|
||||
if proof, err := statedb.GetProof(addr); err != nil {
|
||||
log.Error("Proof for intrinstic address not available", "error", err, "address", addr)
|
||||
} else {
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.Proofs[addr.String()] = wrappedProof
|
||||
}
|
||||
}
|
||||
|
||||
if _, existed := env.StorageProofs[addr.String()]; !existed {
|
||||
env.StorageProofs[addr.String()] = make(map[string][]hexutil.Bytes)
|
||||
}
|
||||
|
||||
for _, slot := range storages {
|
||||
if _, existed := env.StorageProofs[addr.String()][slot.String()]; !existed {
|
||||
if trie, err := statedb.GetStorageTrieForProof(addr); err != nil {
|
||||
log.Error("Storage proof for intrinstic address not available", "error", err, "address", addr)
|
||||
} else if proof, _ := statedb.GetSecureTrieProof(trie, slot); err != nil {
|
||||
log.Error("Get storage proof for intrinstic address failed", "error", err, "address", addr, "slot", slot)
|
||||
} else {
|
||||
wrappedProof := make([]hexutil.Bytes, len(proof))
|
||||
for i, bt := range proof {
|
||||
wrappedProof[i] = bt
|
||||
}
|
||||
env.StorageProofs[addr.String()][slot.String()] = wrappedProof
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blockTrace := &types.BlockTrace{
|
||||
ChainID: api.backend.ChainConfig().ChainID.Uint64(),
|
||||
Version: params.ArchiveVersion(params.CommitHash),
|
||||
Coinbase: &types.AccountWrapper{
|
||||
Address: env.coinbase,
|
||||
Nonce: statedb.GetNonce(env.coinbase),
|
||||
Balance: (*hexutil.Big)(statedb.GetBalance(env.coinbase)),
|
||||
KeccakCodeHash: statedb.GetKeccakCodeHash(env.coinbase),
|
||||
PoseidonCodeHash: statedb.GetPoseidonCodeHash(env.coinbase),
|
||||
CodeSize: statedb.GetCodeSize(env.coinbase),
|
||||
},
|
||||
Header: block.Header(),
|
||||
StorageTrace: env.StorageTrace,
|
||||
ExecutionResults: env.executionResults,
|
||||
TxStorageTraces: env.txStorageTraces,
|
||||
Transactions: txs,
|
||||
}
|
||||
|
||||
for i, tx := range block.Transactions() {
|
||||
evmTrace := env.executionResults[i]
|
||||
// probably a Contract Call
|
||||
if len(tx.Data()) != 0 && tx.To() != nil {
|
||||
evmTrace.ByteCode = hexutil.Encode(statedb.GetCode(*tx.To()))
|
||||
// Get tx.to address's code hash.
|
||||
codeHash := statedb.GetPoseidonCodeHash(*tx.To())
|
||||
evmTrace.PoseidonCodeHash = &codeHash
|
||||
} else if tx.To() == nil { // Contract is created.
|
||||
evmTrace.ByteCode = hexutil.Encode(tx.Data())
|
||||
}
|
||||
}
|
||||
|
||||
// only zktrie model has the ability to get `mptwitness`.
|
||||
if api.backend.ChainConfig().Scroll.ZktrieEnabled() {
|
||||
if err := zkproof.FillBlockTraceForMPTWitness(zkproof.MPTWitnessType(api.backend.CacheConfig().MPTWitness), blockTrace); err != nil {
|
||||
log.Error("fill mpt witness fail", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
blockTrace.WithdrawTrieRoot = withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, env.state)
|
||||
|
||||
return blockTrace, nil
|
||||
return core.CreateTraceEnv(api.backend.ChainConfig(), api.chainContext(ctx), api.backend.Engine(), statedb, parent, block)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i
|
|||
SnapshotLimit: 0,
|
||||
TrieDirtyDisabled: true, // Archive mode
|
||||
}
|
||||
chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(backend.chaindb, cacheConfig, backend.chainConfig, backend.engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
|
|||
)
|
||||
gspec.MustCommit(ldb)
|
||||
// Assemble the test environment
|
||||
blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, false)
|
||||
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestNodeIterator(t *testing.T) {
|
|||
genesis = gspec.MustCommit(fulldb)
|
||||
)
|
||||
gspec.MustCommit(lightdb)
|
||||
blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, false)
|
||||
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
panic(err)
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ func TestTxPool(t *testing.T) {
|
|||
)
|
||||
gspec.MustCommit(ldb)
|
||||
// Assemble the test environment
|
||||
blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil, false)
|
||||
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
panic(err)
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux) {
|
|||
// Create consensus engine
|
||||
engine := clique.New(chainConfig.Clique, chainDB)
|
||||
// Create Ethereum backend
|
||||
bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil)
|
||||
bc, err := core.NewBlockChain(chainDB, nil, chainConfig, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("can't create new chain %v", err)
|
||||
}
|
||||
|
|
|
|||
120
miner/worker.go
120
miner/worker.go
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/scroll-tech/go-ethereum/event"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
"github.com/scroll-tech/go-ethereum/params"
|
||||
"github.com/scroll-tech/go-ethereum/rollup/circuitcapacitychecker"
|
||||
"github.com/scroll-tech/go-ethereum/trie"
|
||||
)
|
||||
|
||||
|
|
@ -95,6 +96,11 @@ type environment struct {
|
|||
header *types.Header
|
||||
txs []*types.Transaction
|
||||
receipts []*types.Receipt
|
||||
|
||||
// circuit capacity check related fields
|
||||
traceEnv *core.TraceEnv // env for tracing
|
||||
accRows *types.RowConsumption // accumulated row consumption for a block
|
||||
maxL1Index uint64 // maximum L1 index included or skipped
|
||||
}
|
||||
|
||||
// task contains all information for consensus engine sealing and result submitting.
|
||||
|
|
@ -103,6 +109,8 @@ type task struct {
|
|||
state *state.StateDB
|
||||
block *types.Block
|
||||
createdAt time.Time
|
||||
accRows *types.RowConsumption // accumulated row consumption in the circuit side
|
||||
maxL1Index uint64 // maximum L1 index included or skipped
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -190,6 +198,8 @@ type worker struct {
|
|||
// External functions
|
||||
isLocalBlock func(block *types.Block) bool // Function used to determine whether the specified block is mined by local miner.
|
||||
|
||||
circuitCapacityChecker *circuitcapacitychecker.CircuitCapacityChecker
|
||||
|
||||
// Test hooks
|
||||
newTaskHook func(*task) // Method to call upon receiving a new sealing task.
|
||||
skipSealHook func(*task) bool // Method to decide whether skipping the sealing.
|
||||
|
|
@ -221,7 +231,9 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
|
|||
startCh: make(chan struct{}, 1),
|
||||
resubmitIntervalCh: make(chan time.Duration),
|
||||
resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
|
||||
circuitCapacityChecker: circuitcapacitychecker.NewCircuitCapacityChecker(),
|
||||
}
|
||||
log.Info("created new worker", "CircuitCapacityChecker ID", worker.circuitCapacityChecker.ID)
|
||||
|
||||
// Subscribe NewTxsEvent for tx pool
|
||||
worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)
|
||||
|
|
@ -625,6 +637,14 @@ func (w *worker) taskLoop() {
|
|||
w.pendingMu.Lock()
|
||||
delete(w.pendingTasks, sealHash)
|
||||
w.pendingMu.Unlock()
|
||||
} else {
|
||||
// Store highest L1 queue index processed by this block. This includes both
|
||||
// included and skipped messages. This way, if a block only skips messages,
|
||||
// we won't reprocess the same messages from the next block.
|
||||
rawdb.WriteFirstQueueIndexNotInL2Block(w.eth.ChainDb(), sealHash, task.maxL1Index+1)
|
||||
|
||||
// Store circuit row consumption.
|
||||
rawdb.WriteBlockRowConsumption(w.eth.ChainDb(), sealHash, task.accRows)
|
||||
}
|
||||
case <-w.exitCh:
|
||||
interrupt()
|
||||
|
|
@ -714,6 +734,15 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
traceEnv, err := core.CreateTraceEnv(w.chainConfig, w.chain, w.engine, state, parent,
|
||||
// new block with a placeholder tx, for traceEnv's ExecutionResults length & TxStorageTraces length
|
||||
types.NewBlockWithHeader(header).WithBody([]*types.Transaction{types.NewTx(&types.LegacyTx{})}, nil),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
state.StartPrefetcher("miner")
|
||||
|
||||
env := &environment{
|
||||
|
|
@ -723,6 +752,7 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
|
|||
family: mapset.NewSet(),
|
||||
uncles: mapset.NewSet(),
|
||||
header: header,
|
||||
traceEnv: traceEnv,
|
||||
}
|
||||
// when 08 is processed ancestors contain 07 (quick block)
|
||||
for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) {
|
||||
|
|
@ -736,6 +766,7 @@ func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
|
|||
env.tcount = 0
|
||||
env.blockSize = 0
|
||||
env.l1TxCount = 0
|
||||
env.maxL1Index = 0
|
||||
|
||||
// Swap out the old work with the new one, terminating any leftover prefetcher
|
||||
// processes in the mean time and starting a new one.
|
||||
|
|
@ -802,21 +833,44 @@ func (w *worker) updateSnapshot() {
|
|||
func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {
|
||||
snap := w.current.state.Snapshot()
|
||||
|
||||
// has to check circuit capacity before `core.ApplyTransaction`,
|
||||
// because if the tx can be successfully executed but circuit capacity overflows, it will be inconvenient to revert
|
||||
traces, err := w.current.traceEnv.GetBlockTrace(
|
||||
types.NewBlockWithHeader(w.current.header).WithBody([]*types.Transaction{tx}, nil),
|
||||
)
|
||||
if err != nil {
|
||||
// `w.current.traceEnv.State` & `w.current.state` share a same pointer to the state, so only need to revert `w.current.state`
|
||||
w.current.state.RevertToSnapshot(snap)
|
||||
return nil, err
|
||||
}
|
||||
accRows, err := w.circuitCapacityChecker.ApplyTransaction(traces)
|
||||
if err != nil {
|
||||
// `w.current.traceEnv.State` & `w.current.state` share a same pointer to the state, so only need to revert `w.current.state`
|
||||
w.current.state.RevertToSnapshot(snap)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// revert to snapshot for calling `core.ApplyMessage` again, (both `traceEnv.GetBlockTrace` & `core.ApplyTransaction` will call `core.ApplyMessage`)
|
||||
w.current.state.RevertToSnapshot(snap)
|
||||
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig())
|
||||
if err != nil {
|
||||
w.current.state.RevertToSnapshot(snap)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.current.txs = append(w.current.txs, tx)
|
||||
w.current.receipts = append(w.current.receipts, receipt)
|
||||
w.current.accRows = accRows
|
||||
|
||||
return receipt.Logs, nil
|
||||
}
|
||||
|
||||
func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool {
|
||||
func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) (bool, bool) {
|
||||
var circuitCapacityReached bool
|
||||
|
||||
// Short circuit if current is nil
|
||||
if w.current == nil {
|
||||
return true
|
||||
return true, circuitCapacityReached
|
||||
}
|
||||
|
||||
gasLimit := w.current.header.GasLimit
|
||||
|
|
@ -826,6 +880,7 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
|
|||
|
||||
var coalescedLogs []*types.Log
|
||||
|
||||
loop:
|
||||
for {
|
||||
// In the following three cases, we will interrupt the execution of the transaction.
|
||||
// (1) new head block event arrival, the interrupt signal is 1
|
||||
|
|
@ -845,7 +900,7 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
|
|||
inc: true,
|
||||
}
|
||||
}
|
||||
return atomic.LoadInt32(interrupt) == commitInterruptNewHead
|
||||
return atomic.LoadInt32(interrupt) == commitInterruptNewHead, circuitCapacityReached
|
||||
}
|
||||
// If we have collected enough transactions then we're done
|
||||
if !w.chainConfig.Scroll.IsValidL2TxCount(w.current.tcount - w.current.l1TxCount + 1) {
|
||||
|
|
@ -905,6 +960,7 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
|
|||
coalescedLogs = append(coalescedLogs, logs...)
|
||||
if tx.IsL1MessageTx() {
|
||||
w.current.l1TxCount++
|
||||
w.current.maxL1Index = tx.AsL1MessageTx().QueueIndex
|
||||
}
|
||||
w.current.tcount++
|
||||
w.current.blockSize += tx.Size()
|
||||
|
|
@ -915,6 +971,43 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
|
|||
log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
|
||||
txs.Pop()
|
||||
|
||||
case errors.Is(err, circuitcapacitychecker.ErrBlockRowConsumptionOverflow):
|
||||
// Circuit capacity check: circuit capacity limit reached in a block,
|
||||
// don't pop or shift, just quit the loop immediately;
|
||||
// though it might still be possible to add some "smaller" txs,
|
||||
// but it's a trade-off between tracing overhead & block usage rate
|
||||
log.Trace("Circuit capacity limit reached in a block", "acc_rows", w.current.accRows, "tx", tx.Hash())
|
||||
circuitCapacityReached = true
|
||||
break loop
|
||||
|
||||
case (errors.Is(err, circuitcapacitychecker.ErrTxRowConsumptionOverflow) && tx.IsL1MessageTx()):
|
||||
// Circuit capacity check: L1MessageTx row consumption too high, shift to the next from the account,
|
||||
// because we shouldn't skip the entire txs from the same account.
|
||||
// This is also useful for skipping "problematic" L1MessageTxs.
|
||||
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||
log.Trace("Circuit capacity limit reached for a single tx", "tx", tx.Hash(), "queueIndex", queueIndex)
|
||||
w.current.maxL1Index = queueIndex
|
||||
txs.Shift()
|
||||
|
||||
case (errors.Is(err, circuitcapacitychecker.ErrTxRowConsumptionOverflow) && !tx.IsL1MessageTx()):
|
||||
// Circuit capacity check: L2MessageTx row consumption too high, skip the account.
|
||||
// This is also useful for skipping "problematic" L2MessageTxs.
|
||||
log.Trace("Circuit capacity limit reached for a single tx", "tx", tx.Hash())
|
||||
txs.Pop()
|
||||
|
||||
case (errors.Is(err, circuitcapacitychecker.ErrUnknown) && tx.IsL1MessageTx()):
|
||||
// Circuit capacity check: unknown circuit capacity checker error for L1MessageTx,
|
||||
// shift to the next from the account because we shouldn't skip the entire txs from the same account
|
||||
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||
log.Trace("Unknown circuit capacity checker error for L1MessageTx", "tx", tx.Hash(), "queueIndex", queueIndex)
|
||||
w.current.maxL1Index = queueIndex
|
||||
txs.Shift()
|
||||
|
||||
case (errors.Is(err, circuitcapacitychecker.ErrUnknown) && !tx.IsL1MessageTx()):
|
||||
// Circuit capacity check: unknown circuit capacity checker error for L2MessageTx, skip the account
|
||||
log.Trace("Unknown circuit capacity checker error for L2MessageTx", "tx", tx.Hash())
|
||||
txs.Pop()
|
||||
|
||||
default:
|
||||
// Strange error, discard the transaction and get the next in line (note, the
|
||||
// nonce-too-high clause will prevent us from executing in vain).
|
||||
|
|
@ -943,7 +1036,7 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
|
|||
if interrupt != nil {
|
||||
w.resubmitAdjustCh <- &intervalAdjust{inc: false}
|
||||
}
|
||||
return false
|
||||
return false, circuitCapacityReached
|
||||
}
|
||||
|
||||
func (w *worker) collectPendingL1Messages() []types.L1MessageTx {
|
||||
|
|
@ -964,6 +1057,7 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
|
|||
|
||||
tstart := time.Now()
|
||||
parent := w.chain.CurrentBlock()
|
||||
w.circuitCapacityChecker.Reset()
|
||||
|
||||
if parent.Time() >= uint64(timestamp) {
|
||||
timestamp = int64(parent.Time() + 1)
|
||||
|
|
@ -1092,22 +1186,28 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
|
|||
localTxs[account] = txs
|
||||
}
|
||||
}
|
||||
var skipCommit, circuitCapacityReached bool
|
||||
if w.chainConfig.Scroll.ShouldIncludeL1Messages() && len(l1Txs) > 0 {
|
||||
log.Trace("Processing L1 messages for inclusion", "count", pendingL1Txs)
|
||||
txs := types.NewTransactionsByPriceAndNonce(w.current.signer, l1Txs, header.BaseFee)
|
||||
if w.commitTransactions(txs, w.coinbase, interrupt) {
|
||||
skipCommit, circuitCapacityReached = w.commitTransactions(txs, w.coinbase, interrupt)
|
||||
if skipCommit {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(localTxs) > 0 {
|
||||
if len(localTxs) > 0 && !circuitCapacityReached {
|
||||
txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs, header.BaseFee)
|
||||
if w.commitTransactions(txs, w.coinbase, interrupt) {
|
||||
skipCommit, circuitCapacityReached = w.commitTransactions(txs, w.coinbase, interrupt)
|
||||
if skipCommit {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(remoteTxs) > 0 {
|
||||
if len(remoteTxs) > 0 && !circuitCapacityReached {
|
||||
txs := types.NewTransactionsByPriceAndNonce(w.current.signer, remoteTxs, header.BaseFee)
|
||||
if w.commitTransactions(txs, w.coinbase, interrupt) {
|
||||
// don't need to get `circuitCapacityReached` here because we don't have further `commitTransactions`
|
||||
// after this one, and if we assign it won't take effect (`ineffassign`)
|
||||
skipCommit, _ = w.commitTransactions(txs, w.coinbase, interrupt)
|
||||
if skipCommit {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -1135,7 +1235,7 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
|
|||
interval()
|
||||
}
|
||||
select {
|
||||
case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now()}:
|
||||
case w.taskCh <- &task{receipts: receipts, state: s, block: block, createdAt: time.Now(), accRows: w.current.accRows, maxL1Index: w.current.maxL1Index}:
|
||||
w.unconfirmed.Shift(block.NumberU64() - 1)
|
||||
log.Info("Commit new mining work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
||||
"uncles", len(uncles), "txs", w.current.tcount,
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
|
|||
|
||||
chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true})}, nil, nil)
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true})}, nil, nil, false)
|
||||
txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)
|
||||
|
||||
// Generate a small n-block chain and an uncle block for it
|
||||
|
|
@ -241,7 +241,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
|||
b.genesis.MustCommit(db2)
|
||||
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil)
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
|
|
@ -571,7 +571,7 @@ func testGenerateBlockWithL1Msg(t *testing.T, isClique bool) {
|
|||
b.genesis.MustCommit(db)
|
||||
chain, _ := core.NewBlockChain(db, nil, b.chain.Config(), engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil)
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
|
|
@ -637,7 +637,7 @@ func TestExcludeL1MsgFromTxlimit(t *testing.T) {
|
|||
b.genesis.MustCommit(db)
|
||||
chain, _ := core.NewBlockChain(db, nil, b.chain.Config(), engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil)
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
|
|
@ -698,7 +698,7 @@ func TestL1MsgCorrectOrder(t *testing.T) {
|
|||
b.genesis.MustCommit(db)
|
||||
chain, _ := core.NewBlockChain(db, nil, b.chain.Config(), engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil)
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
const (
|
||||
VersionMajor = 4 // Major version component of the current release
|
||||
VersionMinor = 3 // Minor version component of the current release
|
||||
VersionPatch = 0 // Patch version component of the current release
|
||||
VersionPatch = 1 // Patch version component of the current release
|
||||
VersionMeta = "sepolia" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
|
|
|||
141
rollup/circuitcapacitychecker/impl.go
Normal file
141
rollup/circuitcapacitychecker/impl.go
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
//go:build circuit_capacity_checker
|
||||
|
||||
package circuitcapacitychecker
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lm -ldl -lzkp -lzktrie
|
||||
#include <stdlib.h>
|
||||
#include "./libzkp/libzkp.h"
|
||||
*/
|
||||
import "C" //nolint:typecheck
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
"github.com/scroll-tech/go-ethereum/log"
|
||||
)
|
||||
|
||||
// mutex for concurrent CircuitCapacityChecker creations
|
||||
var creationMu sync.Mutex
|
||||
|
||||
func init() {
|
||||
C.init()
|
||||
}
|
||||
|
||||
type CircuitCapacityChecker struct {
|
||||
// mutex for each CircuitCapacityChecker itself
|
||||
sync.Mutex
|
||||
ID uint64
|
||||
}
|
||||
|
||||
func NewCircuitCapacityChecker() *CircuitCapacityChecker {
|
||||
creationMu.Lock()
|
||||
defer creationMu.Unlock()
|
||||
|
||||
id := C.new_circuit_capacity_checker()
|
||||
return &CircuitCapacityChecker{ID: uint64(id)}
|
||||
}
|
||||
|
||||
func (ccc *CircuitCapacityChecker) Reset() {
|
||||
ccc.Lock()
|
||||
defer ccc.Unlock()
|
||||
|
||||
C.reset_circuit_capacity_checker(C.uint64_t(ccc.ID))
|
||||
}
|
||||
|
||||
func (ccc *CircuitCapacityChecker) ApplyTransaction(traces *types.BlockTrace) (*types.RowConsumption, error) {
|
||||
ccc.Lock()
|
||||
defer ccc.Unlock()
|
||||
|
||||
if len(traces.Transactions) != 1 || len(traces.ExecutionResults) != 1 || len(traces.TxStorageTraces) != 1 {
|
||||
log.Error("malformatted BlockTrace in ApplyTransaction", "id", ccc.ID,
|
||||
"len(traces.Transactions)", len(traces.Transactions),
|
||||
"len(traces.ExecutionResults)", len(traces.ExecutionResults),
|
||||
"len(traces.TxStorageTraces)", len(traces.TxStorageTraces),
|
||||
"err", "length of Transactions, or ExecutionResults, or TxStorageTraces, is not equal to 1")
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
|
||||
tracesByt, err := json.Marshal(traces)
|
||||
if err != nil {
|
||||
log.Error("fail to json marshal traces in ApplyTransaction", "id", ccc.ID, "TxHash", traces.Transactions[0].TxHash, "err", err)
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
|
||||
tracesStr := C.CString(string(tracesByt))
|
||||
defer func() {
|
||||
C.free(unsafe.Pointer(tracesStr))
|
||||
}()
|
||||
|
||||
log.Debug("start to check circuit capacity for tx", "id", ccc.ID, "TxHash", traces.Transactions[0].TxHash)
|
||||
rawResult := C.apply_tx(C.uint64_t(ccc.ID), tracesStr)
|
||||
log.Debug("check circuit capacity for tx done", "id", ccc.ID, "TxHash", traces.Transactions[0].TxHash)
|
||||
|
||||
result := &WrappedRowUsage{}
|
||||
if err = json.Unmarshal([]byte(C.GoString(rawResult)), result); err != nil {
|
||||
log.Error("fail to json unmarshal apply_tx result", "id", ccc.ID, "TxHash", traces.Transactions[0].TxHash, "err", err)
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
|
||||
if result.Error != "" {
|
||||
log.Error("fail to apply_tx in CircuitCapacityChecker", "id", ccc.ID, "TxHash", traces.Transactions[0].TxHash, "err", result.Error)
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
if result.TxRowUsage == nil || result.AccRowUsage == nil {
|
||||
log.Error("fail to apply_tx in CircuitCapacityChecker",
|
||||
"id", ccc.ID, "TxHash", traces.Transactions[0].TxHash,
|
||||
"len(result.TxRowUsage)", len(result.TxRowUsage),
|
||||
"len(result.AccRowUsage)", len(result.AccRowUsage),
|
||||
"err", "TxRowUsage or AccRowUsage is empty unexpectedly")
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
if !result.TxRowUsage.IsOk {
|
||||
return nil, ErrTxRowConsumptionOverflow
|
||||
}
|
||||
if !result.AccRowUsage.IsOk {
|
||||
return nil, ErrBlockRowConsumptionOverflow
|
||||
}
|
||||
return (*types.RowConsumption)(&result.AccRowUsage.RowUsageDetails), nil
|
||||
}
|
||||
|
||||
func (ccc *CircuitCapacityChecker) ApplyBlock(traces *types.BlockTrace) (*types.RowConsumption, error) {
|
||||
ccc.Lock()
|
||||
defer ccc.Unlock()
|
||||
|
||||
tracesByt, err := json.Marshal(traces)
|
||||
if err != nil {
|
||||
log.Error("fail to json marshal traces in ApplyBlock", "id", ccc.ID, "blockNumber", traces.Header.Number, "blockHash", traces.Header.Hash(), "err", err)
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
|
||||
tracesStr := C.CString(string(tracesByt))
|
||||
defer func() {
|
||||
C.free(unsafe.Pointer(tracesStr))
|
||||
}()
|
||||
|
||||
log.Debug("start to check circuit capacity for block", "id", ccc.ID, "blockNumber", traces.Header.Number, "blockHash", traces.Header.Hash())
|
||||
rawResult := C.apply_block(C.uint64_t(ccc.ID), tracesStr)
|
||||
log.Debug("check circuit capacity for block done", "id", ccc.ID, "blockNumber", traces.Header.Number, "blockHash", traces.Header.Hash())
|
||||
|
||||
result := &WrappedRowUsage{}
|
||||
if err = json.Unmarshal([]byte(C.GoString(rawResult)), result); err != nil {
|
||||
log.Error("fail to json unmarshal apply_block result", "id", ccc.ID, "blockNumber", traces.Header.Number, "blockHash", traces.Header.Hash(), "err", err)
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
|
||||
if result.Error != "" {
|
||||
log.Error("fail to apply_block in CircuitCapacityChecker", "id", ccc.ID, "blockNumber", traces.Header.Number, "blockHash", traces.Header.Hash(), "err", result.Error)
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
if result.AccRowUsage == nil {
|
||||
log.Error("fail to apply_block in CircuitCapacityChecker", "id", ccc.ID, "blockNumber", traces.Header.Number, "blockHash", traces.Header.Hash(), "err", "AccRowUsage is empty unexpectedly")
|
||||
return nil, ErrUnknown
|
||||
}
|
||||
if !result.AccRowUsage.IsOk {
|
||||
return nil, ErrBlockRowConsumptionOverflow
|
||||
}
|
||||
return (*types.RowConsumption)(&result.AccRowUsage.RowUsageDetails), nil
|
||||
}
|
||||
3
rollup/circuitcapacitychecker/libzkp/.gitignore
vendored
Normal file
3
rollup/circuitcapacitychecker/libzkp/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
target/
|
||||
*.a
|
||||
*.so
|
||||
4504
rollup/circuitcapacitychecker/libzkp/Cargo.lock
generated
Normal file
4504
rollup/circuitcapacitychecker/libzkp/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
39
rollup/circuitcapacitychecker/libzkp/Cargo.toml
Normal file
39
rollup/circuitcapacitychecker/libzkp/Cargo.toml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
[package]
|
||||
name = "zkp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[patch."https://github.com/privacy-scaling-explorations/halo2.git"]
|
||||
halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" }
|
||||
[patch."https://github.com/privacy-scaling-explorations/poseidon.git"]
|
||||
poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "scroll-dev-0220" }
|
||||
[patch."https://github.com/privacy-scaling-explorations/halo2wrong.git"]
|
||||
halo2wrong = { git = "https://github.com/scroll-tech/halo2wrong.git", branch = "halo2-ecc-snark-verifier-0323" }
|
||||
maingate = { git = "https://github.com/scroll-tech/halo2wrong", branch = "halo2-ecc-snark-verifier-0323" }
|
||||
[patch."https://github.com/privacy-scaling-explorations/halo2curves.git"]
|
||||
halo2curves = { git = "https://github.com/scroll-tech/halo2curves.git", branch = "0.3.1-derive-serde" }
|
||||
|
||||
[dependencies]
|
||||
prover = { git = "https://github.com/scroll-tech/scroll-prover", rev="26e9cf7" }
|
||||
types = { git = "https://github.com/scroll-tech/scroll-prover", rev="26e9cf7" }
|
||||
|
||||
log = "0.4"
|
||||
env_logger = "0.9.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0.66"
|
||||
libc = "0.2"
|
||||
once_cell = "1.8.0"
|
||||
|
||||
|
||||
[profile.test]
|
||||
opt-level = 3
|
||||
debug-assertions = true
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
debug-assertions = true
|
||||
10
rollup/circuitcapacitychecker/libzkp/Makefile
Normal file
10
rollup/circuitcapacitychecker/libzkp/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.PHONY: clean libzkp
|
||||
|
||||
clean:
|
||||
rm -f *.a *.so
|
||||
cargo clean
|
||||
|
||||
libzkp:
|
||||
cargo build --release
|
||||
cp $(PWD)/target/release/libzkp.so $(PWD)/
|
||||
find $(PWD)/target | grep libzktrie.so | xargs -I{} cp {} $(PWD)/
|
||||
6
rollup/circuitcapacitychecker/libzkp/libzkp.h
Normal file
6
rollup/circuitcapacitychecker/libzkp/libzkp.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#include<stdint.h>
|
||||
void init();
|
||||
uint64_t new_circuit_capacity_checker();
|
||||
void reset_circuit_capacity_checker(uint64_t id);
|
||||
char* apply_tx(uint64_t id, char *tx_traces);
|
||||
char* apply_block(uint64_t id, char *block_trace);
|
||||
1
rollup/circuitcapacitychecker/libzkp/rust-toolchain
Normal file
1
rollup/circuitcapacitychecker/libzkp/rust-toolchain
Normal file
|
|
@ -0,0 +1 @@
|
|||
nightly-2022-12-10
|
||||
190
rollup/circuitcapacitychecker/libzkp/src/lib.rs
Normal file
190
rollup/circuitcapacitychecker/libzkp/src/lib.rs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#![feature(once_cell)]
|
||||
|
||||
pub mod checker {
|
||||
use crate::utils::{c_char_to_vec, vec_to_c_char};
|
||||
use libc::c_char;
|
||||
use prover::zkevm::{CircuitCapacityChecker, RowUsage};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::cell::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::panic;
|
||||
use std::ptr::null;
|
||||
use types::eth::BlockTrace;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct RowUsageResult {
|
||||
pub acc_row_usage: Option<RowUsage>,
|
||||
pub tx_row_usage: Option<RowUsage>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
static mut CHECKERS: OnceCell<HashMap<u64, CircuitCapacityChecker>> = OnceCell::new();
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn init() {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug"))
|
||||
.format_timestamp_millis()
|
||||
.init();
|
||||
let checkers = HashMap::new();
|
||||
CHECKERS
|
||||
.set(checkers)
|
||||
.expect("circuit capacity checker initialized twice");
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn new_circuit_capacity_checker() -> u64 {
|
||||
let checkers = CHECKERS
|
||||
.get_mut()
|
||||
.expect("fail to get circuit capacity checkers map in new_circuit_capacity_checker");
|
||||
let id = checkers.len() as u64;
|
||||
let checker = CircuitCapacityChecker::new();
|
||||
checkers.insert(id, checker);
|
||||
id
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn reset_circuit_capacity_checker(id: u64) {
|
||||
CHECKERS
|
||||
.get_mut()
|
||||
.expect("fail to get circuit capacity checkers map in reset_circuit_capacity_checker")
|
||||
.get_mut(&id)
|
||||
.unwrap_or_else(|| panic!("fail to get circuit capacity checker (id: {id:?}) in reset_circuit_capacity_checker"))
|
||||
.reset()
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn apply_tx(id: u64, tx_traces: *const c_char) -> *const c_char {
|
||||
let result = panic::catch_unwind(|| {
|
||||
let tx_traces_vec = c_char_to_vec(tx_traces);
|
||||
let traces = serde_json::from_slice::<BlockTrace>(&tx_traces_vec)
|
||||
.unwrap_or_else(|_| panic!("id: {id:?}, fail to deserialize tx_traces"));
|
||||
if traces.transactions.len() != 1 {
|
||||
panic!("traces.transactions.len() != 1")
|
||||
} else if traces.execution_results.len() != 1 {
|
||||
panic!("traces.execution_results.len() != 1")
|
||||
} else if traces.tx_storage_trace.len() != 1 {
|
||||
panic!("traces.tx_storage_trace.len() != 1")
|
||||
}
|
||||
CHECKERS
|
||||
.get_mut()
|
||||
.expect("fail to get circuit capacity checkers map in apply_tx")
|
||||
.get_mut(&id)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"fail to get circuit capacity checker (id: {id:?}) in apply_tx"
|
||||
)
|
||||
})
|
||||
.estimate_circuit_capacity(&[traces.clone()])
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"id: {:?}, fail to estimate_circuit_capacity in apply_tx, block_hash: {:?}, tx_hash: {:?}",
|
||||
id, traces.header.hash, traces.transactions[0].tx_hash
|
||||
)
|
||||
})
|
||||
});
|
||||
let r = match result {
|
||||
Ok((acc_row_usage, tx_row_usage)) => {
|
||||
log::debug!(
|
||||
"id: {:?}, acc_row_usage: {:?}, tx_row_usage: {:?}",
|
||||
id,
|
||||
acc_row_usage.row_number,
|
||||
tx_row_usage.row_number
|
||||
);
|
||||
RowUsageResult {
|
||||
acc_row_usage: Some(acc_row_usage),
|
||||
tx_row_usage: Some(tx_row_usage),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
Err(e) => RowUsageResult {
|
||||
acc_row_usage: None,
|
||||
tx_row_usage: None,
|
||||
error: Some(format!("{e:?}")),
|
||||
},
|
||||
};
|
||||
serde_json::to_vec(&r).map_or(null(), vec_to_c_char)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn apply_block(id: u64, block_trace: *const c_char) -> *const c_char {
|
||||
let result = panic::catch_unwind(|| {
|
||||
let block_trace = c_char_to_vec(block_trace);
|
||||
let traces = serde_json::from_slice::<BlockTrace>(&block_trace)
|
||||
.unwrap_or_else(|_| panic!("id: {id:?}, fail to deserialize block_trace"));
|
||||
CHECKERS
|
||||
.get_mut()
|
||||
.expect("fail to get circuit capacity checkers map in apply_block")
|
||||
.get_mut(&id)
|
||||
.unwrap_or_else(|| {
|
||||
panic!(
|
||||
"fail to get circuit capacity checker (id: {id:?}) in apply_block"
|
||||
)
|
||||
})
|
||||
.estimate_circuit_capacity(&[traces.clone()])
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"id: {:?}, fail to estimate_circuit_capacity in apply_block, block_hash: {:?}",
|
||||
id, traces.header.hash
|
||||
)
|
||||
})
|
||||
});
|
||||
let r = match result {
|
||||
Ok((acc_row_usage, tx_row_usage)) => {
|
||||
log::debug!(
|
||||
"id: {:?}, acc_row_usage: {:?}, tx_row_usage: {:?}",
|
||||
id,
|
||||
acc_row_usage.row_number,
|
||||
tx_row_usage.row_number
|
||||
);
|
||||
RowUsageResult {
|
||||
acc_row_usage: Some(acc_row_usage),
|
||||
tx_row_usage: Some(tx_row_usage),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
Err(e) => RowUsageResult {
|
||||
acc_row_usage: None,
|
||||
tx_row_usage: None,
|
||||
error: Some(format!("{e:?}")),
|
||||
},
|
||||
};
|
||||
serde_json::to_vec(&r).map_or(null(), vec_to_c_char)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod utils {
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn c_char_to_str(c: *const c_char) -> &'static str {
|
||||
let cstr = unsafe { CStr::from_ptr(c) };
|
||||
cstr.to_str().expect("fail to cast cstr to str")
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn c_char_to_vec(c: *const c_char) -> Vec<u8> {
|
||||
let cstr = unsafe { CStr::from_ptr(c) };
|
||||
cstr.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn vec_to_c_char(bytes: Vec<u8>) -> *const c_char {
|
||||
CString::new(bytes)
|
||||
.expect("fail to create new CString from bytes")
|
||||
.into_raw()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn bool_to_int(b: bool) -> u8 {
|
||||
match b {
|
||||
true => 1,
|
||||
false => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
28
rollup/circuitcapacitychecker/mock.go
Normal file
28
rollup/circuitcapacitychecker/mock.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//go:build !circuit_capacity_checker
|
||||
|
||||
package circuitcapacitychecker
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
type CircuitCapacityChecker struct {
|
||||
ID uint64
|
||||
}
|
||||
|
||||
func NewCircuitCapacityChecker() *CircuitCapacityChecker {
|
||||
return &CircuitCapacityChecker{ID: rand.Uint64()}
|
||||
}
|
||||
|
||||
func (ccc *CircuitCapacityChecker) Reset() {
|
||||
}
|
||||
|
||||
func (ccc *CircuitCapacityChecker) ApplyTransaction(traces *types.BlockTrace) (*types.RowConsumption, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ccc *CircuitCapacityChecker) ApplyBlock(traces *types.BlockTrace) (*types.RowConsumption, error) {
|
||||
return nil, nil
|
||||
}
|
||||
19
rollup/circuitcapacitychecker/types.go
Normal file
19
rollup/circuitcapacitychecker/types.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package circuitcapacitychecker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/scroll-tech/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUnknown = errors.New("unknown circuit capacity checker error")
|
||||
ErrTxRowConsumptionOverflow = errors.New("tx row consumption oveflow")
|
||||
ErrBlockRowConsumptionOverflow = errors.New("block row consumption oveflow")
|
||||
)
|
||||
|
||||
type WrappedRowUsage struct {
|
||||
AccRowUsage *types.RowUsage `json:"acc_row_usage,omitempty"`
|
||||
TxRowUsage *types.RowUsage `json:"tx_row_usage,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ func (t *BlockTest) Run(snapshotter bool) error {
|
|||
cache.SnapshotLimit = 1
|
||||
cache.SnapshotWait = true
|
||||
}
|
||||
chain, err := core.NewBlockChain(db, cache, config, engine, vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(db, cache, config, engine, vm.Config{}, nil, nil, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func makechain() (bc *core.BlockChain, addrHashes, txHashes []common.Hash) {
|
|||
addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:]))
|
||||
txHashes = append(txHashes, tx.Hash())
|
||||
})
|
||||
bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, false)
|
||||
if _, err := bc.InsertChain(blocks); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue