From f4b9d058b6402b1a012658750b437d2cd989d906 Mon Sep 17 00:00:00 2001 From: constwz Date: Fri, 21 Jun 2024 18:49:37 +0800 Subject: [PATCH 01/10] feat: data analysis --- cmd/geth/snapshot.go | 179 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 040736a0d7..346f0bb8ac 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "os" "path/filepath" "strings" @@ -220,6 +221,26 @@ block is used. Description: ` The export-preimages command exports hash preimages to a flat file, in exactly the expected order for the overlay tree migration. +`, + }, + { + Name: "traverse-var", + Usage: "Dump a specific block from storage (same as 'geth dump' but using snapshots)", + ArgsUsage: "[? | ]", + Action: traversalVar, + Flags: flags.Merge([]cli.Flag{ + utils.ExcludeCodeFlag, + utils.ExcludeStorageFlag, + utils.StartKeyFlag, + utils.DumpLimitFlag, + utils.TriesInMemoryFlag, + }, utils.NetworkFlags, utils.DatabaseFlags), + Description: ` +This command is semantically equivalent to 'geth dump', but uses the snapshots +as the backend data source, making this command a lot faster. + +The argument is interpreted as block number or hash. If none is provided, the latest +block is used. `, }, }, @@ -1020,3 +1041,161 @@ func checkAccount(ctx *cli.Context) error { log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start))) return nil } + +func traversalVar(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + _, db, root, err := parseDumpConfig(ctx, stack) + if err != nil { + return err + } + defer db.Close() + triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false) + defer triedb.Close() + + snapConfig := snapshot.Config{ + CacheSize: 256, + Recovery: false, + NoBuild: true, + AsyncBuild: false, + } + triesInMemory := ctx.Uint64(utils.TriesInMemoryFlag.Name) + snaptree, err := snapshot.New(snapConfig, db, triedb, root, int(triesInMemory), false) + snapshot := snaptree.Snapshot(root) + if err != nil { + return err + } + contractAddress := common.HexToAddress("0xC806e70a62eaBC56E3Ee0c2669c2FF14452A9B3d") + emptyKeyCount := 0 + varCount := 0 + arrayCount := 0 + arrayVarCount := 0 + for i := 0; ; i++ { + key := common.BigToHash(big.NewInt(int64(i))) + enc, encErr := snapshot.Storage(crypto.Keccak256Hash(contractAddress[:]), crypto.Keccak256Hash(key.Bytes())) + if encErr != nil { + log.Info(fmt.Sprintf("key: %s, storage error: %v", key.String(), encErr)) + continue + } + if len(enc) > 0 { + log.Info(common.BytesToHash(enc).String()) + emptyKeyCount = 0 + if isArr, arrLen := isArray(snapshot, contractAddress, key); isArr { + arrayVarCount += arrLen + arrayCount++ + } else { + varCount++ + } + + } else { + emptyKeyCount++ + } + if emptyKeyCount == 20 { + break + } + } + log.Info("array count:", arrayCount, "array item count", arrayVarCount, "var count", varCount) + return nil +} + +func isArray(snap snapshot.Snapshot, contractAddress common.Address, slotIdx common.Hash) (result bool, slotLen int) { + start := crypto.Keccak256Hash(slotIdx.Bytes()) + log.Info("start", start.String()) + emptyValue := 0 + hasValue := false + for i := 0; ; i++ { + idx := common.BigToHash(big.NewInt(0).Add(start.Big(), big.NewInt(int64(i)))) + log.Info("idx:", idx.String(), "big", idx.Big().String()) + enc, err := snap.Storage(crypto.Keccak256Hash(contractAddress[:]), crypto.Keccak256Hash(idx.Bytes())) + if err != nil { + log.Info("storage error", err.Error(), "slot", idx.String()) + continue + } + if len(enc) == 0 { + emptyValue++ + } else { + log.Info("isArray", common.BytesToHash(enc).String()) + emptyValue = 0 + hasValue = true + slotLen++ + } + if emptyValue == 10 { + break + } + } + result = hasValue + + return +} + +/* +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.12 <0.9.0; + +contract HelloWorld { + uint a; + bytes32 b; + mapping(uint => uint) c; + uint[3] d; + uint[] e; + bytes f; + bool g; + uint256 h; + + + function print() public pure returns (string memory) { + return "Hello Const!"; + } + + constructor() { + // 状态变量通过其名称访问,而不是通过例如 this.owner 的方式访问。 + // 这也适用于函数,特别是在构造函数中,你只能像这样(“内部地”)调用它们, + // 因为合约本身还不存在。 + a = 1; + b = "constbh contract"; + for (uint i=0; i<20; i++) + { + c[i] = i; + } + d = [1,2,3]; + e = [4,5,6]; + f = "a"; + g = true; + h = 8; + } + + function getA() public view returns (uint) { + return a; + } + + function getB() public view returns (bytes32) { + return b; + } + + function getCByValue(uint key) public view returns (uint) { + return c[key]; + } + + function getD() public view returns (uint256[3] memory) { + return d; + } + + function getE() public view returns (uint256[] memory) { + return e; + } + + function getF() public view returns (bytes memory) { + return f; + } + + function getG() public view returns (bool) { + return g; + } + + function getH() public view returns (uint256) { + return h; + } +} + +*/ From 4593d62ec7ed96aa2e8f9b142c2b223bba60e8d3 Mon Sep 17 00:00:00 2001 From: constwz Date: Sat, 22 Jun 2024 11:21:57 +0800 Subject: [PATCH 02/10] feat: get address from snapshot --- cmd/geth/snapshot.go | 65 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 346f0bb8ac..00112d39f7 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -25,6 +25,7 @@ import ( "os" "path/filepath" "strings" + "sync" "time" "github.com/prometheus/tsdb/fileutil" @@ -1051,8 +1052,8 @@ func traversalVar(ctx *cli.Context) error { return err } defer db.Close() - triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false) - defer triedb.Close() + tdb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false) + defer tdb.Close() snapConfig := snapshot.Config{ CacheSize: 256, @@ -1061,12 +1062,64 @@ func traversalVar(ctx *cli.Context) error { AsyncBuild: false, } triesInMemory := ctx.Uint64(utils.TriesInMemoryFlag.Name) - snaptree, err := snapshot.New(snapConfig, db, triedb, root, int(triesInMemory), false) - snapshot := snaptree.Snapshot(root) + snaptree, err := snapshot.New(snapConfig, db, tdb, root, int(triesInMemory), false) + snap := snaptree.Snapshot(root) if err != nil { return err } - contractAddress := common.HexToAddress("0xC806e70a62eaBC56E3Ee0c2669c2FF14452A9B3d") + + // + pool := make(chan struct{}, 20) + defer close(pool) + waitGroup := &sync.WaitGroup{} + + it := db.NewIterator(nil, nil) + defer it.Release() + + hasher := crypto.NewKeccakState() + + // Inspect key-value database first. + for it.Next() { + key := it.Key() + switch { + case bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength): + accountAddress := common.BytesToAddress(key) + log.Info(fmt.Sprintf("address:%s", accountAddress.String())) + pool <- struct{}{} + waitGroup.Add(1) + go func(address common.Address) { + defer waitGroup.Done() + <-pool + traversalContract(snap, address, hasher) + }(accountAddress) + } + } + + waitGroup.Wait() + + return nil +} + +func traversalContract(snapshot snapshot.Snapshot, contractAddress common.Address, hasher crypto.KeccakState) { + var slimAccount *types.SlimAccount + var err error + for retry := 0; retry < 5; retry++ { + slimAccount, err = snapshot.Account(crypto.HashData(hasher, contractAddress.Bytes())) + if err != nil { + time.Sleep(5 * time.Millisecond) + continue + } + break + } + if slimAccount == nil { + log.Info("snapshot Account error") + // todo: record account by write file + return + } + if common.Bytes2Hex(slimAccount.CodeHash) == common.HexToHash("").String() { + return + } + log.Info("is contract address") emptyKeyCount := 0 varCount := 0 arrayCount := 0 @@ -1096,7 +1149,6 @@ func traversalVar(ctx *cli.Context) error { } } log.Info("array count:", arrayCount, "array item count", arrayVarCount, "var count", varCount) - return nil } func isArray(snap snapshot.Snapshot, contractAddress common.Address, slotIdx common.Hash) (result bool, slotLen int) { @@ -1125,7 +1177,6 @@ func isArray(snap snapshot.Snapshot, contractAddress common.Address, slotIdx com } } result = hasValue - return } From 96e51a187f569b58d320eb7237ad077eb45a5b30 Mon Sep 17 00:00:00 2001 From: constwz Date: Sun, 23 Jun 2024 10:09:51 +0800 Subject: [PATCH 03/10] feat: iterator db --- cmd/geth/snapshot.go | 60 +++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 00112d39f7..24a5980517 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum/common" "math/big" "os" "path/filepath" @@ -31,7 +32,6 @@ import ( "github.com/prometheus/tsdb/fileutil" "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -240,6 +240,26 @@ the expected order for the overlay tree migration. This command is semantically equivalent to 'geth dump', but uses the snapshots as the backend data source, making this command a lot faster. +The argument is interpreted as block number or hash. If none is provided, the latest +block is used. +`, + }, + { + Name: "keccak256", + Usage: "Dump a specific block from storage (same as 'geth dump' but using snapshots)", + ArgsUsage: "[? | ]", + Action: keccak256, + Flags: flags.Merge([]cli.Flag{ + utils.ExcludeCodeFlag, + utils.ExcludeStorageFlag, + utils.StartKeyFlag, + utils.DumpLimitFlag, + utils.TriesInMemoryFlag, + }, utils.NetworkFlags, utils.DatabaseFlags), + Description: ` +This command is semantically equivalent to 'geth dump', but uses the snapshots +as the backend data source, making this command a lot faster. + The argument is interpreted as block number or hash. If none is provided, the latest block is used. `, @@ -1073,25 +1093,26 @@ func traversalVar(ctx *cli.Context) error { defer close(pool) waitGroup := &sync.WaitGroup{} - it := db.NewIterator(nil, nil) + it := db.NewIterator(rawdb.SnapshotAccountPrefix, nil) defer it.Release() - hasher := crypto.NewKeccakState() - // Inspect key-value database first. for it.Next() { key := it.Key() switch { case bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength): - accountAddress := common.BytesToAddress(key) - log.Info(fmt.Sprintf("address:%s", accountAddress.String())) + log.Info("DebugInfo", common.BytesToHash(key[1:])) + cc := common.HexToAddress("0xC806e70a62eaBC56E3Ee0c2669c2FF14452A9B3d") + log.Info(crypto.Keccak256Hash(cc[:]).String()) + addrHash := common.BytesToHash(key[1:]) + log.Info(fmt.Sprintf("address:%s", addrHash.String())) pool <- struct{}{} waitGroup.Add(1) - go func(address common.Address) { + go func(address common.Hash) { defer waitGroup.Done() <-pool - traversalContract(snap, address, hasher) - }(accountAddress) + traversalContract(snap, address) + }(addrHash) } } @@ -1100,11 +1121,11 @@ func traversalVar(ctx *cli.Context) error { return nil } -func traversalContract(snapshot snapshot.Snapshot, contractAddress common.Address, hasher crypto.KeccakState) { +func traversalContract(snapshot snapshot.Snapshot, contractAddress common.Hash) { var slimAccount *types.SlimAccount var err error for retry := 0; retry < 5; retry++ { - slimAccount, err = snapshot.Account(crypto.HashData(hasher, contractAddress.Bytes())) + slimAccount, err = snapshot.Account(contractAddress) if err != nil { time.Sleep(5 * time.Millisecond) continue @@ -1119,14 +1140,13 @@ func traversalContract(snapshot snapshot.Snapshot, contractAddress common.Addres if common.Bytes2Hex(slimAccount.CodeHash) == common.HexToHash("").String() { return } - log.Info("is contract address") emptyKeyCount := 0 varCount := 0 arrayCount := 0 arrayVarCount := 0 for i := 0; ; i++ { key := common.BigToHash(big.NewInt(int64(i))) - enc, encErr := snapshot.Storage(crypto.Keccak256Hash(contractAddress[:]), crypto.Keccak256Hash(key.Bytes())) + enc, encErr := snapshot.Storage(contractAddress, crypto.Keccak256Hash(key.Bytes())) if encErr != nil { log.Info(fmt.Sprintf("key: %s, storage error: %v", key.String(), encErr)) continue @@ -1151,15 +1171,13 @@ func traversalContract(snapshot snapshot.Snapshot, contractAddress common.Addres log.Info("array count:", arrayCount, "array item count", arrayVarCount, "var count", varCount) } -func isArray(snap snapshot.Snapshot, contractAddress common.Address, slotIdx common.Hash) (result bool, slotLen int) { +func isArray(snap snapshot.Snapshot, contractAddress common.Hash, slotIdx common.Hash) (result bool, slotLen int) { start := crypto.Keccak256Hash(slotIdx.Bytes()) - log.Info("start", start.String()) emptyValue := 0 hasValue := false for i := 0; ; i++ { idx := common.BigToHash(big.NewInt(0).Add(start.Big(), big.NewInt(int64(i)))) - log.Info("idx:", idx.String(), "big", idx.Big().String()) - enc, err := snap.Storage(crypto.Keccak256Hash(contractAddress[:]), crypto.Keccak256Hash(idx.Bytes())) + enc, err := snap.Storage(contractAddress, crypto.Keccak256Hash(idx.Bytes())) if err != nil { log.Info("storage error", err.Error(), "slot", idx.String()) continue @@ -1167,7 +1185,7 @@ func isArray(snap snapshot.Snapshot, contractAddress common.Address, slotIdx com if len(enc) == 0 { emptyValue++ } else { - log.Info("isArray", common.BytesToHash(enc).String()) + log.Info("isArray " + common.BytesToHash(enc).String()) emptyValue = 0 hasValue = true slotLen++ @@ -1250,3 +1268,9 @@ contract HelloWorld { } */ + +func keccak256(ctx *cli.Context) error { + address := common.HexToAddress("C806e70a62eaBC56E3Ee0c2669c2FF14452A9B3d") + log.Info(crypto.Keccak256Hash(address[:]).String()) + return nil +} From f4e83df7db337d55f31229a69e84338445f68418 Mon Sep 17 00:00:00 2001 From: constwz Date: Sun, 23 Jun 2024 14:20:02 +0800 Subject: [PATCH 04/10] test: debug --- cmd/geth/snapshot.go | 15 ++++++++++++++- core/state/snapshot/difflayer.go | 1 + core/state/snapshot/disklayer.go | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 24a5980517..21c62d6976 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "github.com/ethereum/go-ethereum/common" + "golang.org/x/crypto/sha3" "math/big" "os" "path/filepath" @@ -1088,6 +1089,18 @@ func traversalVar(ctx *cli.Context) error { return err } + cc := common.HexToAddress("0x7acb886287e665f383bee198241442b781dc6385") + ccHash := crypto.HashData(sha3.NewLegacyKeccak256().(crypto.KeccakState), cc.Bytes()) + + cc1 := common.HexToAddress("0x0000000000000000000000000000000000001000") + ccHash1 := crypto.HashData(sha3.NewLegacyKeccak256().(crypto.KeccakState), cc1.Bytes()) + fmt.Printf("System contract hash %s\n", ccHash1.String()) + + fmt.Printf("contract hash %s\n", ccHash.String()) + a, err := snap.Account(ccHash) + fmt.Println(a) + fmt.Println(err) + // pool := make(chan struct{}, 20) defer close(pool) @@ -1100,7 +1113,7 @@ func traversalVar(ctx *cli.Context) error { for it.Next() { key := it.Key() switch { - case bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength): + case bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.Hgit ashLength): log.Info("DebugInfo", common.BytesToHash(key[1:])) cc := common.HexToAddress("0xC806e70a62eaBC56E3Ee0c2669c2FF14452A9B3d") log.Info(crypto.Keccak256Hash(cc[:]).String()) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index eb9fa2ed13..604a08b094 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -286,6 +286,7 @@ func (dl *diffLayer) Stale() bool { // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. func (dl *diffLayer) Account(hash common.Hash) (*types.SlimAccount, error) { + fmt.Printf("get account diff %s\n", hash.String()) data, err := dl.AccountRLP(hash) if err != nil { return nil, err diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index 58ce3e3657..456175fb3a 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -18,6 +18,7 @@ package snapshot import ( "bytes" + "fmt" "sync" "github.com/VictoriaMetrics/fastcache" @@ -96,6 +97,7 @@ func (dl *diskLayer) Accounts() (map[common.Hash]*types.SlimAccount, error) { // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. func (dl *diskLayer) Account(hash common.Hash) (*types.SlimAccount, error) { + fmt.Printf("get account disk %s\n", hash.String()) data, err := dl.AccountRLP(hash) if err != nil { return nil, err From f63a57450a6a67d795744fcdda4246309ea2575d Mon Sep 17 00:00:00 2001 From: constwz Date: Sun, 23 Jun 2024 16:46:12 +0800 Subject: [PATCH 05/10] fix: RoutinePool --- cmd/geth/pool.go | 51 ++++++++++ cmd/geth/snapshot.go | 221 ++++++++++--------------------------------- cmd/utils/flags.go | 7 ++ 3 files changed, 107 insertions(+), 172 deletions(-) create mode 100644 cmd/geth/pool.go diff --git a/cmd/geth/pool.go b/cmd/geth/pool.go new file mode 100644 index 0000000000..25e9040b58 --- /dev/null +++ b/cmd/geth/pool.go @@ -0,0 +1,51 @@ +package main + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state/snapshot" + "sync" + "sync/atomic" +) + +type routinePool struct { + wg sync.WaitGroup + taskQueue chan common.Hash + + arrCount atomic.Uint64 + varCount atomic.Uint64 + arrItemCount atomic.Uint64 +} + +func NewRoutinePool(size int64, snap snapshot.Snapshot, keySet []common.Hash) *routinePool { + pool := &routinePool{ + taskQueue: make(chan common.Hash), + arrCount: atomic.Uint64{}, + varCount: atomic.Uint64{}, + arrItemCount: atomic.Uint64{}, + } + + for i := int64(0); i < size; i++ { + go pool.worker(snap, keySet) + } + return pool +} + +func (pool *routinePool) AddTask(addrHash common.Hash) { + pool.wg.Add(1) + pool.taskQueue <- addrHash +} + +func (pool *routinePool) worker(snap snapshot.Snapshot, keySet []common.Hash) { + for addrHash := range pool.taskQueue { + arrCount, arrItemCount, varCount := traversalContract(snap, addrHash, keySet) + pool.arrCount.Add(uint64(arrCount)) + pool.arrItemCount.Add(uint64(arrItemCount)) + pool.varCount.Add(uint64(varCount)) + pool.wg.Done() + } +} + +// Wait +func (pool *routinePool) Wait() { + pool.wg.Wait() +} diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 21c62d6976..f090ef1aba 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -22,12 +22,10 @@ import ( "errors" "fmt" "github.com/ethereum/go-ethereum/common" - "golang.org/x/crypto/sha3" "math/big" "os" "path/filepath" "strings" - "sync" "time" "github.com/prometheus/tsdb/fileutil" @@ -226,43 +224,17 @@ the expected order for the overlay tree migration. `, }, { - Name: "traverse-var", - Usage: "Dump a specific block from storage (same as 'geth dump' but using snapshots)", + Name: "inspect-storage", + Usage: "Statistical variable data for all contracts", ArgsUsage: "[? | ]", - Action: traversalVar, + Action: inspectStorage, Flags: flags.Merge([]cli.Flag{ - utils.ExcludeCodeFlag, - utils.ExcludeStorageFlag, - utils.StartKeyFlag, - utils.DumpLimitFlag, - utils.TriesInMemoryFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), + utils.CPUCountFlag, + }, utils.DatabaseFlags), Description: ` -This command is semantically equivalent to 'geth dump', but uses the snapshots -as the backend data source, making this command a lot faster. +This command collects the variable information of all contracts from snapshot. -The argument is interpreted as block number or hash. If none is provided, the latest -block is used. -`, - }, - { - Name: "keccak256", - Usage: "Dump a specific block from storage (same as 'geth dump' but using snapshots)", - ArgsUsage: "[? | ]", - Action: keccak256, - Flags: flags.Merge([]cli.Flag{ - utils.ExcludeCodeFlag, - utils.ExcludeStorageFlag, - utils.StartKeyFlag, - utils.DumpLimitFlag, - utils.TriesInMemoryFlag, - }, utils.NetworkFlags, utils.DatabaseFlags), - Description: ` -This command is semantically equivalent to 'geth dump', but uses the snapshots -as the backend data source, making this command a lot faster. - -The argument is interpreted as block number or hash. If none is provided, the latest -block is used. +The argument is interpreted as the number of cpus. `, }, }, @@ -1064,7 +1036,7 @@ func checkAccount(ctx *cli.Context) error { return nil } -func traversalVar(ctx *cli.Context) error { +func inspectStorage(ctx *cli.Context) error { stack, _ := makeConfigNode(ctx) defer stack.Close() @@ -1089,22 +1061,15 @@ func traversalVar(ctx *cli.Context) error { return err } - cc := common.HexToAddress("0x7acb886287e665f383bee198241442b781dc6385") - ccHash := crypto.HashData(sha3.NewLegacyKeccak256().(crypto.KeccakState), cc.Bytes()) - - cc1 := common.HexToAddress("0x0000000000000000000000000000000000001000") - ccHash1 := crypto.HashData(sha3.NewLegacyKeccak256().(crypto.KeccakState), cc1.Bytes()) - fmt.Printf("System contract hash %s\n", ccHash1.String()) - - fmt.Printf("contract hash %s\n", ccHash.String()) - a, err := snap.Account(ccHash) - fmt.Println(a) - fmt.Println(err) + // init key + keySet := make([]common.Hash, 0, 1000) + for i := 0; i < 1000; i++ { + key := common.BigToHash(big.NewInt(int64(i))) + keySet = append(keySet, crypto.Keccak256Hash(key.Bytes())) + } - // - pool := make(chan struct{}, 20) - defer close(pool) - waitGroup := &sync.WaitGroup{} + cpuCount := ctx.Int64(utils.CPUCountFlag.Name) + p := NewRoutinePool(cpuCount*100, snap, keySet) it := db.NewIterator(rawdb.SnapshotAccountPrefix, nil) defer it.Release() @@ -1113,61 +1078,45 @@ func traversalVar(ctx *cli.Context) error { for it.Next() { key := it.Key() switch { - case bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.Hgit ashLength): - log.Info("DebugInfo", common.BytesToHash(key[1:])) - cc := common.HexToAddress("0xC806e70a62eaBC56E3Ee0c2669c2FF14452A9B3d") - log.Info(crypto.Keccak256Hash(cc[:]).String()) - addrHash := common.BytesToHash(key[1:]) - log.Info(fmt.Sprintf("address:%s", addrHash.String())) - pool <- struct{}{} - waitGroup.Add(1) - go func(address common.Hash) { - defer waitGroup.Done() - <-pool - traversalContract(snap, address) - }(addrHash) + case bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength): + var account types.SlimAccount + err := rlp.DecodeBytes(it.Value(), &account) + if err != nil { + log.Error("failed to DecodeBytes", "addrHash", key[1:]) + continue + } + if common.Bytes2Hex(account.CodeHash) == types.EmptyCodeHash.String() { + continue + } + p.AddTask(common.BytesToHash(key[1:])) } } - waitGroup.Wait() + p.Wait() + fmt.Println("all done") + fmt.Println("var count", p.varCount.Load()) + fmt.Println("arr count", p.arrCount.Load()) + fmt.Println("arr item count", p.arrItemCount.Load()) return nil } -func traversalContract(snapshot snapshot.Snapshot, contractAddress common.Hash) { - var slimAccount *types.SlimAccount - var err error - for retry := 0; retry < 5; retry++ { - slimAccount, err = snapshot.Account(contractAddress) - if err != nil { - time.Sleep(5 * time.Millisecond) - continue - } - break - } - if slimAccount == nil { - log.Info("snapshot Account error") - // todo: record account by write file - return - } - if common.Bytes2Hex(slimAccount.CodeHash) == common.HexToHash("").String() { - return - } +func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keySet []common.Hash) (int, int, int) { emptyKeyCount := 0 varCount := 0 arrayCount := 0 arrayVarCount := 0 - for i := 0; ; i++ { - key := common.BigToHash(big.NewInt(int64(i))) - enc, encErr := snapshot.Storage(contractAddress, crypto.Keccak256Hash(key.Bytes())) + + for _, storageHash := range keySet { + enc, encErr := snap.Storage(contractAddress, storageHash) if encErr != nil { - log.Info(fmt.Sprintf("key: %s, storage error: %v", key.String(), encErr)) + log.Info(fmt.Sprintf("key: %s, storage error: %v", storageHash.String(), encErr)) + continue } if len(enc) > 0 { - log.Info(common.BytesToHash(enc).String()) emptyKeyCount = 0 - if isArr, arrLen := isArray(snapshot, contractAddress, key); isArr { + if isArr, arrLen := isArray(snap, contractAddress, storageHash); isArr { arrayVarCount += arrLen arrayCount++ } else { @@ -1181,24 +1130,29 @@ func traversalContract(snapshot snapshot.Snapshot, contractAddress common.Hash) break } } - log.Info("array count:", arrayCount, "array item count", arrayVarCount, "var count", varCount) + //log.Info("array count:", arrayCount, "array item count", arrayVarCount, "var count", varCount) + + return arrayCount, arrayVarCount, varCount } func isArray(snap snapshot.Snapshot, contractAddress common.Hash, slotIdx common.Hash) (result bool, slotLen int) { - start := crypto.Keccak256Hash(slotIdx.Bytes()) emptyValue := 0 hasValue := false + errTime := 0 for i := 0; ; i++ { - idx := common.BigToHash(big.NewInt(0).Add(start.Big(), big.NewInt(int64(i)))) + if errTime > 100 { + break + } + idx := common.BigToHash(big.NewInt(0).Add(slotIdx.Big(), big.NewInt(int64(i)))) enc, err := snap.Storage(contractAddress, crypto.Keccak256Hash(idx.Bytes())) if err != nil { - log.Info("storage error", err.Error(), "slot", idx.String()) + fmt.Printf("storage error:%s", err.Error()) + errTime++ continue } if len(enc) == 0 { emptyValue++ } else { - log.Info("isArray " + common.BytesToHash(enc).String()) emptyValue = 0 hasValue = true slotLen++ @@ -1210,80 +1164,3 @@ func isArray(snap snapshot.Snapshot, contractAddress common.Hash, slotIdx common result = hasValue return } - -/* -// SPDX-License-Identifier: MIT -pragma solidity >=0.6.12 <0.9.0; - -contract HelloWorld { - uint a; - bytes32 b; - mapping(uint => uint) c; - uint[3] d; - uint[] e; - bytes f; - bool g; - uint256 h; - - - function print() public pure returns (string memory) { - return "Hello Const!"; - } - - constructor() { - // 状态变量通过其名称访问,而不是通过例如 this.owner 的方式访问。 - // 这也适用于函数,特别是在构造函数中,你只能像这样(“内部地”)调用它们, - // 因为合约本身还不存在。 - a = 1; - b = "constbh contract"; - for (uint i=0; i<20; i++) - { - c[i] = i; - } - d = [1,2,3]; - e = [4,5,6]; - f = "a"; - g = true; - h = 8; - } - - function getA() public view returns (uint) { - return a; - } - - function getB() public view returns (bytes32) { - return b; - } - - function getCByValue(uint key) public view returns (uint) { - return c[key]; - } - - function getD() public view returns (uint256[3] memory) { - return d; - } - - function getE() public view returns (uint256[] memory) { - return e; - } - - function getF() public view returns (bytes memory) { - return f; - } - - function getG() public view returns (bool) { - return g; - } - - function getH() public view returns (uint256) { - return h; - } -} - -*/ - -func keccak256(ctx *cli.Context) error { - address := common.HexToAddress("C806e70a62eaBC56E3Ee0c2669c2FF14452A9B3d") - log.Info(crypto.Keccak256Hash(address[:]).String()) - return nil -} diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 8707840692..ff30202ac3 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1145,6 +1145,13 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Value: params.DefaultExtraReserveForBlobRequests, Category: flags.MiscCategory, } + + // cpu count + CPUCountFlag = &cli.Int64Flag{ + Name: "cpu", + Usage: "The count of cpu", + Value: 4, + } ) var ( From 469a0c5a3abdfc638d5fb1944e843a3a595aca53 Mon Sep 17 00:00:00 2001 From: constwz Date: Mon, 24 Jun 2024 10:35:55 +0800 Subject: [PATCH 06/10] perf: add log --- cmd/geth/snapshot.go | 23 +++++++++++++++++++++++ core/state/snapshot/difflayer.go | 1 - core/state/snapshot/disklayer.go | 2 -- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index f090ef1aba..b848d97c6c 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -1075,6 +1075,7 @@ func inspectStorage(ctx *cli.Context) error { defer it.Release() // Inspect key-value database first. + idx := uint64(0) for it.Next() { key := it.Key() switch { @@ -1088,6 +1089,8 @@ func inspectStorage(ctx *cli.Context) error { if common.Bytes2Hex(account.CodeHash) == types.EmptyCodeHash.String() { continue } + fmt.Printf("add task: %d\n", idx) + idx++ p.AddTask(common.BytesToHash(key[1:])) } } @@ -1098,6 +1101,26 @@ func inspectStorage(ctx *cli.Context) error { fmt.Println("arr count", p.arrCount.Load()) fmt.Println("arr item count", p.arrItemCount.Load()) + f, err := os.Create("result.txt") + if err != nil { + fmt.Println(err) + } + + _, err = f.WriteString(fmt.Sprintf("var count: %d\n", p.varCount.Load())) + if err != nil { + fmt.Println(err) + } + + _, err = f.WriteString(fmt.Sprintf("arr count: %d\n", p.arrCount.Load())) + if err != nil { + fmt.Println(err) + } + + _, err = f.WriteString(fmt.Sprintf("arr item count: %d\n", p.arrItemCount.Load())) + if err != nil { + fmt.Println(err) + } + return nil } diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index 604a08b094..eb9fa2ed13 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -286,7 +286,6 @@ func (dl *diffLayer) Stale() bool { // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. func (dl *diffLayer) Account(hash common.Hash) (*types.SlimAccount, error) { - fmt.Printf("get account diff %s\n", hash.String()) data, err := dl.AccountRLP(hash) if err != nil { return nil, err diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index 456175fb3a..58ce3e3657 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -18,7 +18,6 @@ package snapshot import ( "bytes" - "fmt" "sync" "github.com/VictoriaMetrics/fastcache" @@ -97,7 +96,6 @@ func (dl *diskLayer) Accounts() (map[common.Hash]*types.SlimAccount, error) { // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. func (dl *diskLayer) Account(hash common.Hash) (*types.SlimAccount, error) { - fmt.Printf("get account disk %s\n", hash.String()) data, err := dl.AccountRLP(hash) if err != nil { return nil, err From 385c359e54b58ae1419ffd044b4353ca17d4af07 Mon Sep 17 00:00:00 2001 From: constwz Date: Mon, 24 Jun 2024 14:04:33 +0800 Subject: [PATCH 07/10] fix: args usage --- cmd/geth/snapshot.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index b848d97c6c..96a2649401 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -224,10 +224,9 @@ the expected order for the overlay tree migration. `, }, { - Name: "inspect-storage", - Usage: "Statistical variable data for all contracts", - ArgsUsage: "[? | ]", - Action: inspectStorage, + Name: "inspect-storage", + Usage: "Statistical variable data for all contracts", + Action: inspectStorage, Flags: flags.Merge([]cli.Flag{ utils.CPUCountFlag, }, utils.DatabaseFlags), From c024d0ed3218dbd9c1a237df76375d4f792ba55e Mon Sep 17 00:00:00 2001 From: constwz Date: Mon, 24 Jun 2024 16:16:07 +0800 Subject: [PATCH 08/10] fix: desc --- cmd/geth/snapshot.go | 2 +- cmd/utils/flags.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 96a2649401..1b2e45b96a 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -1085,7 +1085,7 @@ func inspectStorage(ctx *cli.Context) error { log.Error("failed to DecodeBytes", "addrHash", key[1:]) continue } - if common.Bytes2Hex(account.CodeHash) == types.EmptyCodeHash.String() { + if bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { continue } fmt.Printf("add task: %d\n", idx) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index ff30202ac3..84135036ca 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1149,7 +1149,7 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. // cpu count CPUCountFlag = &cli.Int64Flag{ Name: "cpu", - Usage: "The count of cpu", + Usage: "core number", Value: 4, } ) From 58b4529f5c2c9db0bad2b9605e096f311a25c73f Mon Sep 17 00:00:00 2001 From: constwz Date: Tue, 25 Jun 2024 10:16:27 +0800 Subject: [PATCH 09/10] feat: slot number --- cmd/geth/pool.go | 25 +++++++++++------ cmd/geth/snapshot.go | 66 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/cmd/geth/pool.go b/cmd/geth/pool.go index 25e9040b58..4ba77abafc 100644 --- a/cmd/geth/pool.go +++ b/cmd/geth/pool.go @@ -11,17 +11,22 @@ type routinePool struct { wg sync.WaitGroup taskQueue chan common.Hash - arrCount atomic.Uint64 - varCount atomic.Uint64 - arrItemCount atomic.Uint64 + emptyContract atomic.Uint64 + arrCount atomic.Uint64 + varCount atomic.Uint64 + arrItemCount atomic.Uint64 + arrItemCountMap map[int]uint64 + mapLock *sync.Mutex } func NewRoutinePool(size int64, snap snapshot.Snapshot, keySet []common.Hash) *routinePool { pool := &routinePool{ - taskQueue: make(chan common.Hash), - arrCount: atomic.Uint64{}, - varCount: atomic.Uint64{}, - arrItemCount: atomic.Uint64{}, + taskQueue: make(chan common.Hash), + arrCount: atomic.Uint64{}, + varCount: atomic.Uint64{}, + arrItemCount: atomic.Uint64{}, + arrItemCountMap: make(map[int]uint64), + mapLock: &sync.Mutex{}, } for i := int64(0); i < size; i++ { @@ -37,10 +42,14 @@ func (pool *routinePool) AddTask(addrHash common.Hash) { func (pool *routinePool) worker(snap snapshot.Snapshot, keySet []common.Hash) { for addrHash := range pool.taskQueue { - arrCount, arrItemCount, varCount := traversalContract(snap, addrHash, keySet) + arrCount, arrItemCount, varCount := traversalContract(snap, addrHash, keySet, pool.mapLock, pool.arrItemCountMap) pool.arrCount.Add(uint64(arrCount)) pool.arrItemCount.Add(uint64(arrItemCount)) pool.varCount.Add(uint64(varCount)) + + if arrCount == 0 && varCount == 0 { + pool.emptyContract.Add(1) + } pool.wg.Done() } } diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 1b2e45b96a..834afd4f12 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -25,7 +25,9 @@ import ( "math/big" "os" "path/filepath" + "sort" "strings" + "sync" "time" "github.com/prometheus/tsdb/fileutil" @@ -1088,7 +1090,9 @@ func inspectStorage(ctx *cli.Context) error { if bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { continue } - fmt.Printf("add task: %d\n", idx) + if idx%10000 == 0 { + fmt.Printf("add task: %d\n", idx) + } idx++ p.AddTask(common.BytesToHash(key[1:])) } @@ -1120,10 +1124,63 @@ func inspectStorage(ctx *cli.Context) error { fmt.Println(err) } + _, err = f.WriteString(fmt.Sprintf("empty contract count: %d\n", p.emptyContract.Load())) + if err != nil { + fmt.Println(err) + } + + arrCount := make([]int, 0, len(p.arrItemCountMap)) + for k := range p.arrItemCountMap { + arrCount = append(arrCount, k) + } + sort.Ints(arrCount) + sum := uint64(0) + maxCount := uint64(0) + mode := 0 + for _, k := range arrCount { + count := p.arrItemCountMap[k] + sum += count + if sum >= p.arrCount.Load()/2 { + fmt.Printf("median: %d\n", k) + _, err = f.WriteString(fmt.Sprintf("median: %d\n", k)) + if err != nil { + fmt.Println(err) + } + break + } + } + + for _, k := range arrCount { + count := p.arrItemCountMap[k] + if maxCount < count { + maxCount = count + mode = k + } + } + + fmt.Printf("mode: %d\n", mode) + _, err = f.WriteString(fmt.Sprintf("mode: %d\n", mode)) + if err != nil { + fmt.Println(err) + } + + storageIt := db.NewIterator(rawdb.SnapshotStoragePrefix, nil) + defer storageIt.Release() + + storageKeyCount := uint64(0) + for storageIt.Next() { + storageKeyCount++ + } + fmt.Printf("storage key count: %d", storageKeyCount) + _, err = f.WriteString(fmt.Sprintf("storage key count: %d\n", storageKeyCount)) + if err != nil { + fmt.Println(err) + } + return nil } -func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keySet []common.Hash) (int, int, int) { +func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keySet []common.Hash, mapLock *sync.Mutex, arrItemCountMap map[int]uint64) (int, int, int) { emptyKeyCount := 0 varCount := 0 arrayCount := 0 @@ -1141,6 +1198,9 @@ func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keyS if isArr, arrLen := isArray(snap, contractAddress, storageHash); isArr { arrayVarCount += arrLen arrayCount++ + mapLock.Lock() + arrItemCountMap[arrLen]++ + mapLock.Unlock() } else { varCount++ } @@ -1148,7 +1208,7 @@ func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keyS } else { emptyKeyCount++ } - if emptyKeyCount == 20 { + if emptyKeyCount == 100 { break } } From 1b50b565f9a0f17f7a9c4ad37cb31437d1f7fd06 Mon Sep 17 00:00:00 2001 From: constwz Date: Tue, 25 Jun 2024 10:16:27 +0800 Subject: [PATCH 10/10] feat: slot number --- cmd/geth/pool.go | 25 +++++++++++----- cmd/geth/snapshot.go | 69 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/cmd/geth/pool.go b/cmd/geth/pool.go index 25e9040b58..4ba77abafc 100644 --- a/cmd/geth/pool.go +++ b/cmd/geth/pool.go @@ -11,17 +11,22 @@ type routinePool struct { wg sync.WaitGroup taskQueue chan common.Hash - arrCount atomic.Uint64 - varCount atomic.Uint64 - arrItemCount atomic.Uint64 + emptyContract atomic.Uint64 + arrCount atomic.Uint64 + varCount atomic.Uint64 + arrItemCount atomic.Uint64 + arrItemCountMap map[int]uint64 + mapLock *sync.Mutex } func NewRoutinePool(size int64, snap snapshot.Snapshot, keySet []common.Hash) *routinePool { pool := &routinePool{ - taskQueue: make(chan common.Hash), - arrCount: atomic.Uint64{}, - varCount: atomic.Uint64{}, - arrItemCount: atomic.Uint64{}, + taskQueue: make(chan common.Hash), + arrCount: atomic.Uint64{}, + varCount: atomic.Uint64{}, + arrItemCount: atomic.Uint64{}, + arrItemCountMap: make(map[int]uint64), + mapLock: &sync.Mutex{}, } for i := int64(0); i < size; i++ { @@ -37,10 +42,14 @@ func (pool *routinePool) AddTask(addrHash common.Hash) { func (pool *routinePool) worker(snap snapshot.Snapshot, keySet []common.Hash) { for addrHash := range pool.taskQueue { - arrCount, arrItemCount, varCount := traversalContract(snap, addrHash, keySet) + arrCount, arrItemCount, varCount := traversalContract(snap, addrHash, keySet, pool.mapLock, pool.arrItemCountMap) pool.arrCount.Add(uint64(arrCount)) pool.arrItemCount.Add(uint64(arrItemCount)) pool.varCount.Add(uint64(varCount)) + + if arrCount == 0 && varCount == 0 { + pool.emptyContract.Add(1) + } pool.wg.Done() } } diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 1b2e45b96a..be7ad40bb9 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -25,7 +25,9 @@ import ( "math/big" "os" "path/filepath" + "sort" "strings" + "sync" "time" "github.com/prometheus/tsdb/fileutil" @@ -1088,7 +1090,9 @@ func inspectStorage(ctx *cli.Context) error { if bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { continue } - fmt.Printf("add task: %d\n", idx) + if idx%10000 == 0 { + fmt.Printf("add task: %d\n", idx) + } idx++ p.AddTask(common.BytesToHash(key[1:])) } @@ -1120,10 +1124,66 @@ func inspectStorage(ctx *cli.Context) error { fmt.Println(err) } + _, err = f.WriteString(fmt.Sprintf("empty contract count: %d\n", p.emptyContract.Load())) + if err != nil { + fmt.Println(err) + } + + arrCount := make([]int, 0, len(p.arrItemCountMap)) + for k := range p.arrItemCountMap { + arrCount = append(arrCount, k) + } + sort.Ints(arrCount) + sum := uint64(0) + maxCount := uint64(0) + mode := 0 + for _, k := range arrCount { + count := p.arrItemCountMap[k] + sum += count + if sum >= p.arrCount.Load()/2 { + fmt.Printf("median: %d\n", k) + _, err = f.WriteString(fmt.Sprintf("median: %d\n", k)) + if err != nil { + fmt.Println(err) + } + break + } + } + + for _, k := range arrCount { + count := p.arrItemCountMap[k] + if maxCount < count { + maxCount = count + mode = k + } + } + + fmt.Printf("mode: %d\n", mode) + _, err = f.WriteString(fmt.Sprintf("mode: %d\n", mode)) + if err != nil { + fmt.Println(err) + } + + storageIt := db.NewIterator(rawdb.SnapshotStoragePrefix, nil) + defer storageIt.Release() + + storageKeyCount := uint64(0) + for storageIt.Next() { + key := storageIt.Key() + if len(key) == (len(rawdb.SnapshotStoragePrefix) + 2*common.HashLength) { + storageKeyCount++ + } + } + fmt.Printf("storage key count: %d", storageKeyCount) + _, err = f.WriteString(fmt.Sprintf("storage key count: %d\n", storageKeyCount)) + if err != nil { + fmt.Println(err) + } + return nil } -func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keySet []common.Hash) (int, int, int) { +func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keySet []common.Hash, mapLock *sync.Mutex, arrItemCountMap map[int]uint64) (int, int, int) { emptyKeyCount := 0 varCount := 0 arrayCount := 0 @@ -1141,6 +1201,9 @@ func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keyS if isArr, arrLen := isArray(snap, contractAddress, storageHash); isArr { arrayVarCount += arrLen arrayCount++ + mapLock.Lock() + arrItemCountMap[arrLen]++ + mapLock.Unlock() } else { varCount++ } @@ -1148,7 +1211,7 @@ func traversalContract(snap snapshot.Snapshot, contractAddress common.Hash, keyS } else { emptyKeyCount++ } - if emptyKeyCount == 20 { + if emptyKeyCount == 100 { break } }