Skip to content

Commit

Permalink
Datastream Scalable Roots (#3)
Browse files Browse the repository at this point in the history
* feat(stream): add stateroot to l2tx

* feat(rpcroots): remove roots files

* feat(rpcroots): consume stateroot from the l2 datastream

* fix(state_domain): temp fix effective gas %

* changed block stateroot to tx stateroot

* feat(rpcroots): remove lfs and update readme

---------

Co-authored-by: Valentin Staykov <v.staykov@razorlabs.com>
  • Loading branch information
revitteth and V-Staykov committed Dec 14, 2023
1 parent e52c30b commit b351aaa
Show file tree
Hide file tree
Showing 24 changed files with 78 additions and 513,469 deletions.
3 changes: 1 addition & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
zkevm-roots.json filter=lfs diff=lfs merge=lfs -text
zkevm-roots-testnet.json filter=lfs diff=lfs merge=lfs -text

14 changes: 1 addition & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@ zkEVM-Erigon is a fork of Erigon, currently in Alpha, optimized for syncing with

***

## Prerequisites

- [Git LFS](https://git-lfs.com/)

For syncing different networks, you will need the corresponding network-specific files e.g. `zkevm-roots-testnet.json`. To do this run the following commands:
- `git lfs install` - Initializes the Git LFS
- `git lfs pull` - Downloads the actual LFS files

These contain some pre-calculated roots from the RPC which are later validated by the local node in the intermediate hashes stage.

***

## zkevm-specific API Support

In order to enable the zkevm_ namespace, please add 'zkevm' to the http.api flag (see the example config below).
Expand All @@ -41,7 +29,7 @@ In order to enable the zkevm_ namespace, please add 'zkevm' to the http.api flag
## Limitations/Warnings

- The golden poseidon hashing will be much faster on x86, so developers on Mac may experience slowness on Apple silicone
- Falling behind by > 20 blocks (at some point this will be configurable) will cause a SMT rebuild - which will take some time for longer chains
- Falling behind by > 500 blocks (at some point this will be configurable) will cause a SMT rebuild - which will take some time for longer chains

***

Expand Down
8 changes: 7 additions & 1 deletion cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import (
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/tests"
"github.com/ledgerwatch/erigon/turbo/trie"
"github.com/ledgerwatch/erigon/zk/hermez_db"
)

const (
Expand Down Expand Up @@ -302,12 +303,17 @@ func Main(ctx *cli.Context) error {
}
defer tx.Rollback()

hermezDb, err := hermez_db.NewHermezDb(tx)
if err != nil {
return err
}

reader, writer := MakePreState(chainConfig.Rules(0, 0), tx, prestate.Pre)
// serenity engine can be used for pre-merge blocks as well, as it
// redirects to the ethash engine based on the block number
engine := serenity.New(&ethash.FakeEthash{})

result, err := core.ExecuteBlockEphemerally(chainConfig, &vmConfig, getHash, engine, block, reader, writer, nil, getTracer, tx)
result, err := core.ExecuteBlockEphemerally(chainConfig, &vmConfig, getHash, engine, block, reader, writer, nil, getTracer, tx, hermezDb)

if hashError != nil {
return NewError(ErrorMissingBlockhash, fmt.Errorf("blockhash error: %v", err))
Expand Down
2 changes: 1 addition & 1 deletion cmd/hack/hack.go
Original file line number Diff line number Diff line change
Expand Up @@ -1444,7 +1444,7 @@ func main() {
printCurrentBlockNumber(*chaindata)

case "bucket":
printBucket(*chaindata, state.RpcRootsBucketName)
printBucket(*chaindata, kv.PlainState)

case "buckets":
printBuckets(*chaindata, *bucket)
Expand Down
2 changes: 1 addition & 1 deletion cmd/integration/commands/state_domains.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ func (b *blockProcessor) applyBlock(
ibs.Prepare(tx.Hash(), block.Hash(), i)
ct := exec3.NewCallTracer()
b.vmConfig.Tracer = ct
receipt, _, err := core.ApplyTransaction(b.chainConfig, getHashFn, b.engine, nil, gp, ibs, b.writer, header, tx, usedGas, b.vmConfig, parentHeader.ExcessDataGas)
receipt, _, err := core.ApplyTransaction(b.chainConfig, getHashFn, b.engine, nil, gp, ibs, b.writer, header, tx, usedGas, b.vmConfig, parentHeader.ExcessDataGas, 100) // TODO [zkevm] - fix for build, but may need to pass correct value
if err != nil {
return nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err)
}
Expand Down
5 changes: 0 additions & 5 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,11 +401,6 @@ var (
Usage: "RPC rate limit in requests per second.",
Value: 0,
}
RpcRootsFileFlag = cli.StringFlag{
Name: "zkevm.rpc-roots-file",
Usage: "File to store the RPC roots in",
Value: "",
}
RpcBatchConcurrencyFlag = cli.UintFlag{
Name: "rpc.batch.concurrency",
Usage: "Does limit amount of goroutines to process 1 batch request. Means 1 bach request can't overload server. 1 batch still can have unlimited amount of request",
Expand Down
13 changes: 3 additions & 10 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ type EphemeralExecResult struct {
StateSyncReceipt *types.Receipt `json:"-"`
}

type EffectivePricePercentageGetter interface {
GetEffectiveGasPricePercentage(txHash libcommon.Hash) (uint8, error)
}

// ExecuteBlockEphemerally runs a block from provided stateReader and
// writes the result to the provided stateWriter
func ExecuteBlockEphemerally(
Expand All @@ -90,7 +86,7 @@ func ExecuteBlockEphemerally(
chainReader consensus.ChainHeaderReader,
getTracer func(txIndex int, txHash libcommon.Hash) (vm.EVMLogger, error),
dbTx kv.RwTx,
effectivePricePercentageGetter EffectivePricePercentageGetter,
roHermezDb state.ReadOnlyHermezDb,
) (*EphemeralExecResult, error) {

defer BlockExecutionTimer.UpdateDuration(time.Now())
Expand Down Expand Up @@ -140,7 +136,7 @@ func ExecuteBlockEphemerally(

gp.Reset(block.GasLimit())

effectiveGasPricePercentage, err := effectivePricePercentageGetter.GetEffectiveGasPricePercentage(tx.Hash())
effectiveGasPricePercentage, err := roHermezDb.GetEffectiveGasPricePercentage(tx.Hash())
if err != nil {
return nil, err
}
Expand All @@ -166,14 +162,11 @@ func ExecuteBlockEphemerally(
}
}

// [zkevm] - set smt root for each tx, unless last tx in block (see ibs block commit)
//if i != block.Transactions().Len()-1 {
// [zkevm] - set smt root hash in magic account
err = ibs.ScalableSetSmtRootHash(dbTx)
err = ibs.ScalableSetSmtRootHash(roHermezDb)
if err != nil {
return nil, err
}
//}
}

receiptSha := types.DeriveSha(receipts)
Expand Down
68 changes: 7 additions & 61 deletions core/state/intra_block_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,10 @@ import (
"fmt"
"sort"

"encoding/binary"
"encoding/hex"
"math/big"
"strings"

"github.com/holiman/uint256"
"github.com/iden3/go-iden3-crypto/keccak256"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/kv"
types2 "github.com/ledgerwatch/erigon-lib/types"
"github.com/ledgerwatch/erigon/chain"
"github.com/ledgerwatch/erigon/common"
Expand All @@ -41,6 +36,11 @@ import (
"github.com/ledgerwatch/erigon/turbo/trie"
)

type ReadOnlyHermezDb interface {
GetEffectiveGasPricePercentage(txHash libcommon.Hash) (uint8, error)
GetStateRoot(l2BlockNo uint64) (libcommon.Hash, error)
}

type revision struct {
id int
journalIndex int
Expand All @@ -49,8 +49,6 @@ type revision struct {
// SystemAddress - sender address for internal state updates.
var SystemAddress = libcommon.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")

const RpcRootsBucketName = "HermezRpcRoot"

// BalanceIncrease represents the increase of balance of an account that did not require
// reading the account first
type BalanceIncrease struct {
Expand Down Expand Up @@ -822,7 +820,7 @@ func (sdb *IntraBlockState) ScalableSetTxNum() {
sdb.SetState(saddr, &sl0, *txNum)
}

func (sdb *IntraBlockState) ScalableSetSmtRootHash(dbTx kv.RwTx) error {
func (sdb *IntraBlockState) ScalableSetSmtRootHash(roHermezDb ReadOnlyHermezDb) error {
saddr := libcommon.HexToAddress("0x000000000000000000000000000000005ca1ab1e")
sl0 := libcommon.HexToHash("0x0")

Expand All @@ -835,7 +833,7 @@ func (sdb *IntraBlockState) ScalableSetSmtRootHash(dbTx kv.RwTx) error {
mapKey := keccak256.Hash(d1, d2)
mkh := libcommon.BytesToHash(mapKey)

rpcHash, err := getDbRoot(dbTx, txNum.Uint64())
rpcHash, err := roHermezDb.GetStateRoot(txNum.Uint64())
if err != nil {
return err
}
Expand All @@ -848,55 +846,3 @@ func (sdb *IntraBlockState) ScalableSetSmtRootHash(dbTx kv.RwTx) error {

return nil
}

func trimHexString(s string) string {
if strings.HasPrefix(s, "0x") {
s = s[2:]
}

for i := 0; i < len(s); i++ {
if s[i] != '0' {
return "0x" + s[i:]
}
}

return "0x0"
}

func verifyRoot(dbTx kv.RwTx, hash string, txNum uint64) (*libcommon.Hash, error) {
rpcHash, err := getDbRoot(dbTx, txNum)
if err != nil {
return nil, err
}
h := libcommon.HexToHash(hash)

if rpcHash.Big().Cmp(big.NewInt(0)) == 0 {
return &h, fmt.Errorf("RPC root hash is zero")
}

trimmedRpcHash := trimHexString(rpcHash.String())

fmt.Printf("rpcHash: %s\n", trimmedRpcHash)
fmt.Printf("hash: %s\n", hash)

if trimmedRpcHash != hash {
return &h, fmt.Errorf("root hash mismatch")
}

return &h, nil
}

func getDbRoot(dbTx kv.RwTx, txNum uint64) (*libcommon.Hash, error) {
rootHash, err := dbTx.GetOne(RpcRootsBucketName, UintBytes(txNum))
if err != nil {
return nil, err
}
h := libcommon.BytesToHash(rootHash)
return &h, nil
}

func UintBytes(no uint64) []byte {
noBytes := make([]byte, 8)
binary.BigEndian.PutUint64(noBytes, no)
return noBytes
}
1 change: 0 additions & 1 deletion eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@ type Zk struct {
L1GERManagerContractAddress common.Address
L1FirstBlock uint64
RpcRateLimits int
RpcRootsFile string

RebuildTreeAfter uint64
}
Expand Down
17 changes: 0 additions & 17 deletions eth/stagedsync/default_stages.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@ func DefaultZkStages(
cumulativeIndex CumulativeIndexCfg,
blockHashCfg BlockHashesCfg,
senders SendersCfg,
rpcRootsCfg zkStages.RpcRootsCfg,
exec ExecuteBlockCfg,
hashState HashStateCfg,
zkInterHashesCfg zkStages.ZkInterHashesCfg,
Expand Down Expand Up @@ -400,22 +399,6 @@ func DefaultZkStages(
return PruneSendersStage(p, tx, senders, ctx)
},
},
{
ID: sync_stages.RpcRoots,
Description: "Download RPC roots",
Forward: func(firstCycle bool, badBlockUnwind bool, s *sync_stages.StageState, u sync_stages.Unwinder, tx kv.RwTx, quiet bool) error {
if badBlockUnwind {
return nil
}
return zkStages.SpawnStageRpcRoots(s, u, ctx, tx, rpcRootsCfg, test, firstCycle, quiet)
},
Unwind: func(firstCycle bool, u *sync_stages.UnwindState, s *sync_stages.StageState, tx kv.RwTx) error {
return zkStages.UnwindRpcRootsStage(u, tx, rpcRootsCfg, ctx)
},
Prune: func(firstCycle bool, p *sync_stages.PruneState, tx kv.RwTx) error {
return zkStages.PruneRpcRootsStage(p, tx, rpcRootsCfg, ctx)
},
},
{
ID: sync_stages.Execution,
Description: "Execute blocks w/o hash checks",
Expand Down
4 changes: 2 additions & 2 deletions eth/stagedsync/stage_execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func executeBlock(
writeCallTraces bool,
initialCycle bool,
stateStream bool,
effectivePricePercentageGetter core.EffectivePricePercentageGetter,
roHermezDb state.ReadOnlyHermezDb,
) error {
blockNum := block.NumberU64()

Expand Down Expand Up @@ -212,7 +212,7 @@ func executeBlock(
} else {
// for zkEVM no receipts
//vmConfig.NoReceipts = true
execRs, err = core.ExecuteBlockEphemerally(cfg.chainConfig, &vmConfig, getHashFn, cfg.engine, block, stateReader, stateWriter, ChainReaderImpl{config: cfg.chainConfig, tx: tx, blockReader: cfg.blockReader}, getTracer, tx, effectivePricePercentageGetter)
execRs, err = core.ExecuteBlockEphemerally(cfg.chainConfig, &vmConfig, getHashFn, cfg.engine, block, stateReader, stateWriter, ChainReaderImpl{config: cfg.chainConfig, tx: tx, blockReader: cfg.blockReader}, getTracer, tx, roHermezDb)
}
if err != nil {
return err
Expand Down
1 change: 0 additions & 1 deletion sync_stages/stages.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ var (
L1Sequences SyncStage = "L1Sequences"
L1VerificationsBatchNo SyncStage = "L1VerificationsBatchNo"
Batches SyncStage = "Batches"
RpcRoots SyncStage = "RpcRoots"
HighestHashableL2BlockNo SyncStage = "HighestHashableL2BlockNo"
VerificationsStateRootCheck SyncStage = "VerificationStateRootCheck"
ForkId SyncStage = "ForkId"
Expand Down
1 change: 0 additions & 1 deletion turbo/cli/default_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,5 +175,4 @@ var DefaultFlags = []cli.Flag{
&utils.L1FirstBlockFlag,
&utils.RpcRateLimitsFlag,
&utils.RebuildTreeAfterFlag,
&utils.RpcRootsFileFlag,
}
2 changes: 0 additions & 2 deletions turbo/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,6 @@ func ApplyFlagsForZkConfig(ctx *cli.Context, cfg *ethconfig.Config) {
L1FirstBlock: ctx.Uint64(utils.L1FirstBlockFlag.Name),
RpcRateLimits: ctx.Int(utils.RpcRateLimitsFlag.Name),
RebuildTreeAfter: ctx.Uint64(utils.RebuildTreeAfterFlag.Name),
RpcRootsFile: ctx.String(utils.RpcRootsFileFlag.Name),
}

checkFlag(utils.L2ChainIdFlag.Name, cfg.Zk.L2ChainId)
Expand All @@ -318,7 +317,6 @@ func ApplyFlagsForZkConfig(ctx *cli.Context, cfg *ethconfig.Config) {
checkFlag(utils.L1FirstBlockFlag.Name, cfg.Zk.L1FirstBlock)
checkFlag(utils.RpcRateLimitsFlag.Name, cfg.Zk.RpcRateLimits)
checkFlag(utils.RebuildTreeAfterFlag.Name, cfg.Zk.RebuildTreeAfter)
// don't check RpcRootsFile as it will get set by network as a default if not overriden by flag
}

func ApplyFlagsForEthConfigCobra(f *pflag.FlagSet, cfg *ethconfig.Config) {
Expand Down
16 changes: 0 additions & 16 deletions turbo/stages/stageloop.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
"github.com/ledgerwatch/erigon/cmd/sentry/sentry"
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/core/rawdb"
corestate "github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/eth/ethconfig"
Expand Down Expand Up @@ -84,20 +83,6 @@ func StageLoop(
) {
defer close(waitForDone)
initialCycle := true
trw, err := db.BeginRw(ctx)
if err != nil {
log.Error("Failed to start transaction to add new zkevm batch tables", "err", err)
return
}
err = trw.CreateBucket(corestate.RpcRootsBucketName)
if err != nil {
log.Error(fmt.Sprintf("Failed to create %s bucket", corestate.RpcRootsBucketName), "err", err)
return
}
if err := trw.Commit(); err != nil {
log.Error("Failed to commit transaction to add new zkevm batch tables", "err", err)
return
}

for {
start := time.Now()
Expand Down Expand Up @@ -500,7 +485,6 @@ func NewDefaultZkStages(ctx context.Context,
stagedsync.StageCumulativeIndexCfg(db),
stagedsync.StageBlockHashesCfg(db, dirs.Tmp, controlServer.ChainConfig),
stagedsync.StageSendersCfg(db, controlServer.ChainConfig, false, dirs.Tmp, cfg.Prune, blockRetire, controlServer.Hd),
zkStages.StageRpcRootsCfg(db, controlServer.ChainConfig, cfg.Zk),
stagedsync.StageExecuteBlocksCfg(
db,
cfg.Prune,
Expand Down
Loading

0 comments on commit b351aaa

Please sign in to comment.