diff --git a/.github/workflows/nancy.yml b/.github/workflows/nancy.yml index 6a9a8593d6..e7a901bc89 100644 --- a/.github/workflows/nancy.yml +++ b/.github/workflows/nancy.yml @@ -1,8 +1,9 @@ name: Go Nancy on: - # Scan changed files in PRs (diff-aware scanning): - pull_request: {} + # Scan changed files in PRs from same repository only: + pull_request: + types: [opened, synchronize, reopened] # Scan on-demand through GitHub Actions interface: workflow_dispatch: {} # Scan mainline branches and report all findings: @@ -11,6 +12,7 @@ on: jobs: build: + if: github.event.pull_request.head.repo.full_name == github.repository strategy: matrix: go-version: [1.24.x] @@ -18,20 +20,28 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out code into the Go module directory - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Go 1.x in order to write go.list file - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} - - name: Go mod tidy - run: go mod tidy + - name: Install Nancy + run: | + curl -sSL -o nancy https://github.com/sonatype-nexus-community/nancy/releases/download/v1.0.52/nancy-v1.0.52-linux-amd64 + chmod +x nancy + sudo mv nancy /usr/local/bin/ + file /usr/local/bin/nancy - - name: WriteGoList - run: go list -json -deps ./... > go.list - - - name: Nancy - uses: sonatype-nexus-community/nancy-github-action@main - with: - nancyCommand: sleuth --loud + - name: Nancy Security Scan + shell: bash + env: + OSSINDEX_USERNAME: ${{ secrets.OSSINDEX_USERNAME }} + OSSINDEX_TOKEN: ${{ secrets.OSSINDEX_TOKEN }} + run: | + if [[ -z "${OSSINDEX_USERNAME:-}" || -z "${OSSINDEX_TOKEN:-}" ]]; then + echo "::error::Missing OSS Index credentials" + fi + + go list -json -deps ./... | nancy sleuth --username "${OSSINDEX_USERNAME}" --token "${OSSINDEX_TOKEN}" diff --git a/CHANGELOG.md b/CHANGELOG.md index cbeec06c0f..55d10340e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,32 @@ # Changelog + +## v1.6.2 +### FEATURE +[\#3363](https://github.com/bnb-chain/bsc/pull/3363) websocket: add transactionReceipts for receipts notification +[\#3367](https://github.com/bnb-chain/bsc/pull/3367) BEP-619: Short Block Interval Phase Three: 0.45 Seconds +[\#3368](https://github.com/bnb-chain/bsc/pull/3368) BEP-590: Extended Voting Rules for Fast Finality Stability +[\#3374](https://github.com/bnb-chain/bsc/pull/3374) Implement BEP-592: Non-Consensus Based Block-Level Access List +[\#3372](https://github.com/bnb-chain/bsc/pull/3372) core/systemcontracts: define fermiUpgrade +[\#3390](https://github.com/bnb-chain/bsc/pull/3390) feat: implement incremental snapshot +[\#3395](https://github.com/bnb-chain/bsc/pull/3395) feat: EVM execution opcode level optimization +[\#3400](https://github.com/bnb-chain/bsc/pull/3400) consensus/parlia: set kAncestorGenerationDepth to 3 in BEP-590 +[\#3397](https://github.com/bnb-chain/bsc/pull/3397) consensus/parlia: fix updateAttestation&improve assembleVoteAttestation +[\#0000](https://github.com/bnb-chain/bsc/pull/0000) p2p: define ProxyedNodeIds in Config #3417 + +### BUGFIX +[\#3373](https://github.com/bnb-chain/bsc/pull/3373) ethapi: reject oversize storage keys before hex decode + +### IMPROVEMENT +[\#3388](https://github.com/bnb-chain/bsc/pull/3388) miner/minerconfig: update config to adapt 100M gaslimit +[\#3404](https://github.com/bnb-chain/bsc/pull/3404) miner: validator not inturn backoff before mining +[\#3407](https://github.com/bnb-chain/bsc/pull/3407) fix: change lock to read lock in legacy pool +[\#3415](https://github.com/bnb-chain/bsc/pull/3415) eth: broadcast votes to evn peers regardless of deltaTdThreshold +[\#3416](https://github.com/bnb-chain/bsc/pull/3416) cmd/geth: improve config for sentry nodes when init network +[\#3419](https://github.com/bnb-chain/bsc/pull/3419) miner: use latest block as pending block for simplicity +[\#3426](https://github.com/bnb-chain/bsc/pull/3426) eth: increase the delta td threshold to broadcast votes + ## v1.6.1 -v1.6.1-alpha is a preview release, which fixes several issues of the v1.6.0-alpha, it is more reliable than v1.6.0-alpha, so mark it as beta stage. +v1.6.1-beta is a preview release, which fixes several issues of the v1.6.0-alpha, it is more reliable than v1.6.0-alpha, so mark it as beta stage. ### FEATURE NA diff --git a/cmd/evm/main.go b/cmd/evm/main.go index bf5be9a359..8722995a55 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -114,6 +114,12 @@ var ( Category: traceCategory, } + // Vm flags. + VMOpcodeOptimizeFlag = &cli.BoolFlag{ + Name: "vm.opcode.optimize", + Usage: "enable opcode optimization", + } + // Deprecated flags. DebugFlag = &cli.BoolFlag{ Name: "debug", diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index b2cf28353b..c6bc5ea333 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -284,7 +284,8 @@ func runCmd(ctx *cli.Context) error { BlobHashes: blobHashes, BlobBaseFee: blobBaseFee, EVMConfig: vm.Config{ - Tracer: tracer, + Tracer: tracer, + EnableOpcodeOptimizations: ctx.Bool(VMOpcodeOptimizeFlag.Name), }, } diff --git a/cmd/evm/testdata/eof/results.initcode.txt b/cmd/evm/testdata/eof/results.initcode.txt index 311b93d954..a1fbd6ec04 100644 --- a/cmd/evm/testdata/eof/results.initcode.txt +++ b/cmd/evm/testdata/eof/results.initcode.txt @@ -202,38 +202,38 @@ ERR: undefined instruction: op opcode 0xac not defined, pos 0 ERR: undefined instruction: op opcode 0xad not defined, pos 0 ERR: undefined instruction: op opcode 0xae not defined, pos 0 ERR: undefined instruction: op opcode 0xaf not defined, pos 0 -ERR: undefined instruction: op opcode 0xb0 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb1 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb2 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb3 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb4 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb5 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb6 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb7 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb8 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb9 not defined, pos 0 -ERR: undefined instruction: op opcode 0xba not defined, pos 0 -ERR: undefined instruction: op opcode 0xbb not defined, pos 0 -ERR: undefined instruction: op opcode 0xbc not defined, pos 0 -ERR: undefined instruction: op opcode 0xbd not defined, pos 0 -ERR: undefined instruction: op opcode 0xbe not defined, pos 0 -ERR: undefined instruction: op opcode 0xbf not defined, pos 0 -ERR: undefined instruction: op opcode 0xc0 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc1 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc2 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc3 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc4 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc5 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc6 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc7 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc8 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc9 not defined, pos 0 -ERR: undefined instruction: op opcode 0xca not defined, pos 0 -ERR: undefined instruction: op opcode 0xcb not defined, pos 0 -ERR: undefined instruction: op opcode 0xcc not defined, pos 0 -ERR: undefined instruction: op opcode 0xcd not defined, pos 0 -ERR: undefined instruction: op opcode 0xce not defined, pos 0 -ERR: undefined instruction: op opcode 0xcf not defined, pos 0 +ERR: undefined instruction: op NOP, pos 0 +ERR: undefined instruction: op ANDSWAP1POPSWAP2SWAP1, pos 0 +ERR: undefined instruction: op SWAP2SWAP1POPJUMP, pos 0 +ERR: undefined instruction: op SWAP1POPSWAP2SWAP1, pos 0 +ERR: undefined instruction: op POPSWAP2SWAP1POP, pos 0 +ERR: undefined instruction: op PUSH2JUMP, pos 0 +ERR: undefined instruction: op PUSH2JUMPI, pos 0 +ERR: undefined instruction: op PUSH1PUSH1, pos 0 +ERR: undefined instruction: op PUSH1ADD, pos 0 +ERR: undefined instruction: op PUSH1SHL, pos 0 +ERR: undefined instruction: op PUSH1DUP1, pos 0 +ERR: undefined instruction: op SWAP1POP, pos 0 +ERR: undefined instruction: op POPJUMP, pos 0 +ERR: undefined instruction: op POP2, pos 0 +ERR: undefined instruction: op SWAP2SWAP1, pos 0 +ERR: undefined instruction: op SWAP2POP, pos 0 +ERR: undefined instruction: op DUP2LT, pos 0 +ERR: undefined instruction: op JUMPIFZERO, pos 0 +ERR: undefined instruction: op ISZEROPUSH2, pos 0 +ERR: undefined instruction: op DUP2MSTOREPUSH1ADD, pos 0 +ERR: undefined instruction: op DUP1PUSH4EQPUSH2, pos 0 +ERR: undefined instruction: op PUSH1CALLDATALOADPUSH1SHRDUP1PUSH4GTPUSH2, pos 0 +ERR: undefined instruction: op PUSH1PUSH1PUSH1SHLSUB, pos 0 +ERR: undefined instruction: op ANDDUP2ADDSWAP1DUP2LT, pos 0 +ERR: undefined instruction: op SWAP1PUSH1DUP1NOTSWAP2ADDANDDUP2ADDSWAP1DUP2LT, pos 0 +ERR: undefined instruction: op DUP3AND, pos 0 +ERR: undefined instruction: op SWAP2SWAP1DUP3SUBSWAP2DUP3GTPUSH2, pos 0 +ERR: undefined instruction: op SWAP1DUP2, pos 0 +ERR: undefined instruction: op SHRSHRDUP1MULDUP1, pos 0 +ERR: undefined instruction: op SWAP3POPPOPPOP, pos 0 +ERR: undefined instruction: op SUBSLTISZEROPUSH2, pos 0 +ERR: undefined instruction: op DUP11MULDUP3SUBMULDUP1, pos 0 ERR: undefined instruction: op opcode 0xd4 not defined, pos 0 ERR: undefined instruction: op opcode 0xd5 not defined, pos 0 ERR: undefined instruction: op opcode 0xd6 not defined, pos 0 @@ -2250,7 +2250,7 @@ ERR: invalid jump destination: out-of-bounds offset: offset -21151, dest -20628, ERR: initcode contains a RETURN or STOP opcode ERR: initcode contains a RETURN or STOP opcode ERR: initcode contains a RETURN or STOP opcode -ERR: undefined instruction: op opcode 0xc8 not defined, pos 1179 +ERR: undefined instruction: op SWAP1PUSH1DUP1NOTSWAP2ADDANDDUP2ADDSWAP1DUP2LT, pos 1179 ERR: invalid dataloadN argument: arg 5, last 32, pos 1728 ERR: invalid dataloadN argument: arg 62719, last 32, pos 945 ERR: invalid dataloadN argument: arg 16, last 32, pos 771 diff --git a/cmd/evm/testdata/eof/results.regular.txt b/cmd/evm/testdata/eof/results.regular.txt index 43594ec1b3..ee5bc7ceec 100644 --- a/cmd/evm/testdata/eof/results.regular.txt +++ b/cmd/evm/testdata/eof/results.regular.txt @@ -202,38 +202,38 @@ ERR: undefined instruction: op opcode 0xac not defined, pos 0 ERR: undefined instruction: op opcode 0xad not defined, pos 0 ERR: undefined instruction: op opcode 0xae not defined, pos 0 ERR: undefined instruction: op opcode 0xaf not defined, pos 0 -ERR: undefined instruction: op opcode 0xb0 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb1 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb2 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb3 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb4 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb5 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb6 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb7 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb8 not defined, pos 0 -ERR: undefined instruction: op opcode 0xb9 not defined, pos 0 -ERR: undefined instruction: op opcode 0xba not defined, pos 0 -ERR: undefined instruction: op opcode 0xbb not defined, pos 0 -ERR: undefined instruction: op opcode 0xbc not defined, pos 0 -ERR: undefined instruction: op opcode 0xbd not defined, pos 0 -ERR: undefined instruction: op opcode 0xbe not defined, pos 0 -ERR: undefined instruction: op opcode 0xbf not defined, pos 0 -ERR: undefined instruction: op opcode 0xc0 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc1 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc2 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc3 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc4 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc5 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc6 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc7 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc8 not defined, pos 0 -ERR: undefined instruction: op opcode 0xc9 not defined, pos 0 -ERR: undefined instruction: op opcode 0xca not defined, pos 0 -ERR: undefined instruction: op opcode 0xcb not defined, pos 0 -ERR: undefined instruction: op opcode 0xcc not defined, pos 0 -ERR: undefined instruction: op opcode 0xcd not defined, pos 0 -ERR: undefined instruction: op opcode 0xce not defined, pos 0 -ERR: undefined instruction: op opcode 0xcf not defined, pos 0 +ERR: undefined instruction: op NOP, pos 0 +ERR: undefined instruction: op ANDSWAP1POPSWAP2SWAP1, pos 0 +ERR: undefined instruction: op SWAP2SWAP1POPJUMP, pos 0 +ERR: undefined instruction: op SWAP1POPSWAP2SWAP1, pos 0 +ERR: undefined instruction: op POPSWAP2SWAP1POP, pos 0 +ERR: undefined instruction: op PUSH2JUMP, pos 0 +ERR: undefined instruction: op PUSH2JUMPI, pos 0 +ERR: undefined instruction: op PUSH1PUSH1, pos 0 +ERR: undefined instruction: op PUSH1ADD, pos 0 +ERR: undefined instruction: op PUSH1SHL, pos 0 +ERR: undefined instruction: op PUSH1DUP1, pos 0 +ERR: undefined instruction: op SWAP1POP, pos 0 +ERR: undefined instruction: op POPJUMP, pos 0 +ERR: undefined instruction: op POP2, pos 0 +ERR: undefined instruction: op SWAP2SWAP1, pos 0 +ERR: undefined instruction: op SWAP2POP, pos 0 +ERR: undefined instruction: op DUP2LT, pos 0 +ERR: undefined instruction: op JUMPIFZERO, pos 0 +ERR: undefined instruction: op ISZEROPUSH2, pos 0 +ERR: undefined instruction: op DUP2MSTOREPUSH1ADD, pos 0 +ERR: undefined instruction: op DUP1PUSH4EQPUSH2, pos 0 +ERR: undefined instruction: op PUSH1CALLDATALOADPUSH1SHRDUP1PUSH4GTPUSH2, pos 0 +ERR: undefined instruction: op PUSH1PUSH1PUSH1SHLSUB, pos 0 +ERR: undefined instruction: op ANDDUP2ADDSWAP1DUP2LT, pos 0 +ERR: undefined instruction: op SWAP1PUSH1DUP1NOTSWAP2ADDANDDUP2ADDSWAP1DUP2LT, pos 0 +ERR: undefined instruction: op DUP3AND, pos 0 +ERR: undefined instruction: op SWAP2SWAP1DUP3SUBSWAP2DUP3GTPUSH2, pos 0 +ERR: undefined instruction: op SWAP1DUP2, pos 0 +ERR: undefined instruction: op SHRSHRDUP1MULDUP1, pos 0 +ERR: undefined instruction: op SWAP3POPPOPPOP, pos 0 +ERR: undefined instruction: op SUBSLTISZEROPUSH2, pos 0 +ERR: undefined instruction: op DUP11MULDUP3SUBMULDUP1, pos 0 ERR: undefined instruction: op opcode 0xd4 not defined, pos 0 ERR: undefined instruction: op opcode 0xd5 not defined, pos 0 ERR: undefined instruction: op opcode 0xd6 not defined, pos 0 @@ -2250,7 +2250,7 @@ ERR: invalid jump destination: out-of-bounds offset: offset -21151, dest -20628, ERR: invalid dataloadN argument: arg 24833, last 0, pos 1397 ERR: invalid jump destination: out-of-bounds offset: offset 24833, dest 26278, pos 1443 ERR: stack limit reached 1048 (1024): at pos 75 -ERR: undefined instruction: op opcode 0xc8 not defined, pos 1179 +ERR: undefined instruction: op SWAP1PUSH1DUP1NOTSWAP2ADDANDDUP2ADDSWAP1DUP2LT, pos 1179 ERR: invalid dataloadN argument: arg 5, last 32, pos 1728 ERR: invalid dataloadN argument: arg 62719, last 32, pos 945 ERR: invalid dataloadN argument: arg 16, last 32, pos 771 diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index d9a31d98a3..fe53b074b0 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -363,7 +363,7 @@ func initGenesis(ctx *cli.Context) error { log.Warn("Multi-database is an experimental feature") } - triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle()) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle(), false) defer triedb.Close() _, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides) @@ -521,7 +521,6 @@ func initNetwork(ctx *cli.Context) error { sentryEnodes []*enode.Node sentryNodeIDs []enode.ID connectOneExtraEnodes bool - staticConnect bool ) if enableSentryNode { sentryConfigs, sentryEnodes, err = createSentryNodeConfigs(ctx, config, initDir) @@ -533,10 +532,9 @@ func initNetwork(ctx *cli.Context) error { sentryNodeIDs[i] = sentryEnodes[i].ID() } connectOneExtraEnodes = true - staticConnect = true } - configs, enodes, accounts, err := createConfigs(config, initDir, "node", ips, ports, sentryEnodes, connectOneExtraEnodes, staticConnect) + configs, enodes, accounts, err := createConfigs(config, initDir, "node", ips, ports, sentryEnodes, connectOneExtraEnodes, true) if err != nil { utils.Fatalf("Failed to create node configs: %v", err) } @@ -549,6 +547,7 @@ func initNetwork(ctx *cli.Context) error { if enableSentryNode { for i := 0; i < len(sentryConfigs); i++ { sentryConfigs[i].Node.P2P.ProxyedValidatorAddresses = accounts[i] + sentryConfigs[i].Node.P2P.ProxyedNodeIds = []enode.ID{nodeIDs[i]} } } if ctx.Bool(utils.InitEVNValidatorWhitelist.Name) { @@ -563,7 +562,8 @@ func initNetwork(ctx *cli.Context) error { } if enableSentryNode && ctx.Bool(utils.InitEVNSentryWhitelist.Name) { for i := 0; i < len(sentryConfigs); i++ { - sentryConfigs[i].Node.P2P.EVNNodeIdsWhitelist = sentryNodeIDs + sentryConfigs[i].Node.P2P.EVNNodeIdsWhitelist = append([]enode.ID{}, sentryNodeIDs...) + sentryConfigs[i].Node.P2P.EVNNodeIdsWhitelist[i] = nodeIDs[i] } } if enableSentryNode && ctx.Bool(utils.InitEVNSentryRegister.Name) { @@ -587,11 +587,9 @@ func initNetwork(ctx *cli.Context) error { } if ctx.Int(utils.InitFullNodeSize.Name) > 0 { - var extraEnodes []*enode.Node + extraEnodes := enodes if enableSentryNode { extraEnodes = sentryEnodes - } else { - extraEnodes = enodes } _, _, err := createAndSaveFullNodeConfigs(ctx, inGenesisFile, config, initDir, extraEnodes) if err != nil { @@ -1091,7 +1089,7 @@ func dump(ctx *cli.Context) error { return err } defer db.Close() - triedb := utils.MakeTrieDatabase(ctx, stack, db, true, true, false) // always enable preimage lookup + triedb := utils.MakeTrieDatabase(ctx, stack, db, true, true, false, false) // always enable preimage lookup defer triedb.Close() state, err := state.New(root, state.NewDatabase(triedb, nil)) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index fb97240e94..55c7d5d008 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -280,7 +280,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { } if ctx.IsSet(utils.OverrideMinBlocksForBlobRequests.Name) { params.MinBlocksForBlobRequests = ctx.Uint64(utils.OverrideMinBlocksForBlobRequests.Name) - params.MinTimeDurationForBlobRequests = uint64(float64(params.MinBlocksForBlobRequests) * 0.75 /*maxwellBlockInterval*/) + params.MinTimeDurationForBlobRequests = uint64(float64(params.MinBlocksForBlobRequests) * 0.45 /*fermiBlockInterval*/) } if ctx.IsSet(utils.OverrideDefaultExtraReserveForBlobRequests.Name) { params.DefaultExtraReserveForBlobRequests = ctx.Uint64(utils.OverrideDefaultExtraReserveForBlobRequests.Name) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 2ebc1c21b1..0b53233263 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -93,6 +93,7 @@ Remove blockchain and state databases`, dbTrieDeleteCmd, dbDeleteTrieStateCmd, ancientInspectCmd, + incrInspectCmd, }, } dbInspectCmd = &cli.Command{ @@ -275,6 +276,13 @@ of ancientStore, will also displays the reserved number of blocks in ancientStor }, utils.NetworkFlags, utils.DatabaseFlags), Description: "This command queries the history of the account or storage slot within the specified block range", } + incrInspectCmd = &cli.Command{ + Action: inspectIncrSnapshot, + Name: "inspect-incr-snapshot", + Flags: []cli.Flag{utils.IncrSnapshotPathFlag}, + Usage: "Inspect the incremental snapshot information", + Description: `This command reads and displays incremental store information`, + } ) func removeDB(ctx *cli.Context) error { @@ -945,7 +953,7 @@ func dbDumpTrie(ctx *cli.Context) error { db := utils.MakeChainDatabase(ctx, stack, true) defer db.Close() - triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false, false) defer triedb.Close() var ( @@ -1279,7 +1287,7 @@ func inspectHistory(ctx *cli.Context) error { db := utils.MakeChainDatabase(ctx, stack, true) defer db.Close() - triedb := utils.MakeTrieDatabase(ctx, stack, db, false, false, false) + triedb := utils.MakeTrieDatabase(ctx, stack, db, false, false, false, false) defer triedb.Close() var ( @@ -1327,3 +1335,14 @@ func inspectHistory(ctx *cli.Context) error { } return inspectStorage(triedb, start, end, address, slot, ctx.Bool("raw")) } + +func inspectIncrSnapshot(ctx *cli.Context) error { + if !ctx.IsSet(utils.IncrSnapshotPathFlag.Name) { + return errors.New("increment snapshot path is not set") + } + baseDir := ctx.String(utils.IncrSnapshotPathFlag.Name) + if err := rawdb.InspectIncrStore(baseDir); err != nil { + return err + } + return nil +} diff --git a/cmd/geth/initnetwork_test.go b/cmd/geth/initnetwork_test.go index 1473f056e4..a9352b7c98 100644 --- a/cmd/geth/initnetwork_test.go +++ b/cmd/geth/initnetwork_test.go @@ -91,18 +91,18 @@ func verifyConfigFileRemoteHosts(t *testing.T, config *gethConfig, ipStr string, t.Fatalf("expected ListenAddr to be %s but it is %s instead", expectedListenAddr, config.Node.P2P.ListenAddr) } - bootnodes := config.Node.P2P.BootstrapNodes + staticnodes := config.Node.P2P.StaticNodes // 3. check correctness of peers' hosts for j := 0; j < i; j++ { - ip := bootnodes[j].IP().String() + ip := staticnodes[j].IP().String() if ip != ips[j] { t.Fatalf("expected IP of bootnode to be %s but found %s instead", ips[j], ip) } } for j := i + 1; j < size; j++ { - ip := bootnodes[j-1].IP().String() + ip := staticnodes[j-1].IP().String() if ip != ips[j] { t.Fatalf("expected IP of bootnode to be %s but found %s instead", ips[j-1], ip) } @@ -110,8 +110,8 @@ func verifyConfigFileRemoteHosts(t *testing.T, config *gethConfig, ipStr string, // 4. check correctness of peer port numbers for j := 0; j < size-1; j++ { - if bootnodes[j].UDP() != basePort { - t.Fatalf("expected bootnode port at position %d to be %d but got %d instead", j, basePort, bootnodes[j].UDP()) + if staticnodes[j].UDP() != basePort { + t.Fatalf("expected bootnode port at position %d to be %d but got %d instead", j, basePort, staticnodes[j].UDP()) } } } @@ -123,11 +123,11 @@ func verifyConfigFileLocalhost(t *testing.T, config *gethConfig, i int, basePort t.Fatalf("expected ListenAddr to be %s but it is %s instead", expectedListenAddr, config.Node.P2P.ListenAddr) } - bootnodes := config.Node.P2P.BootstrapNodes + staticnodes := config.Node.P2P.StaticNodes // 2. check correctness of peers' hosts localhost := "127.0.0.1" for j := 0; j < size-1; j++ { - ip := bootnodes[j].IP().String() + ip := staticnodes[j].IP().String() if ip != localhost { t.Fatalf("expected IP of bootnode to be %s but found %s instead", localhost, ip) } @@ -135,13 +135,13 @@ func verifyConfigFileLocalhost(t *testing.T, config *gethConfig, i int, basePort // 3. check correctness of peer port numbers for j := 0; j < i; j++ { - if bootnodes[j].UDP() != basePort+j { - t.Fatalf("expected bootnode port at position %d to be %d but got %d instead", j, basePort+j, bootnodes[j].UDP()) + if staticnodes[j].UDP() != basePort+j { + t.Fatalf("expected bootnode port at position %d to be %d but got %d instead", j, basePort+j, staticnodes[j].UDP()) } } for j := i + 1; j < size; j++ { - if bootnodes[j-1].UDP() != basePort+j { - t.Fatalf("expected bootnode port at position %d to be %d but got %d instead", j-1, basePort+j, bootnodes[j-1].UDP()) + if staticnodes[j-1].UDP() != basePort+j { + t.Fatalf("expected bootnode port at position %d to be %d but got %d instead", j-1, basePort+j, staticnodes[j-1].UDP()) } } } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 239ef3a1d3..a2f470945e 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -146,6 +146,7 @@ var ( utils.MinerRecommitIntervalFlag, utils.MinerNewPayloadTimeoutFlag, // deprecated utils.MinerDelayLeftoverFlag, + utils.EnableBALFlag, // utils.MinerNewPayloadTimeout, utils.NATFlag, utils.NoDiscoverFlag, @@ -182,6 +183,14 @@ var ( utils.LogDebugFlag, utils.LogBacktraceAtFlag, utils.BlobExtraReserveFlag, + utils.VMOpcodeOptimizeFlag, + utils.EnableIncrSnapshotFlag, + utils.IncrSnapshotPathFlag, + utils.IncrSnapshotBlockIntervalFlag, + utils.IncrSnapshotStateBufferFlag, + utils.IncrSnapshotKeptBlocksFlag, + utils.UseRemoteIncrSnapshotFlag, + utils.RemoteIncrSnapshotURLFlag, // utils.BeaconApiFlag, // utils.BeaconApiHeaderFlag, // utils.BeaconThresholdFlag, diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index e8d1440731..d41b529e44 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -27,6 +27,7 @@ import ( "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" "github.com/ethereum/go-ethereum/core/state/pruner" @@ -163,6 +164,15 @@ The export-preimages command exports hash preimages to a flat file, in exactly the expected order for the overlay tree migration. `, }, + { + Action: mergeIncrSnapshot, + Name: "merge-incr-snapshot", + Usage: "Merge the incremental snapshot into local data", + ArgsUsage: "", + Flags: slices.Concat([]cli.Flag{utils.IncrSnapshotPathFlag}, + utils.DatabaseFlags), + Description: `This command merges multiple incremental snapshots into local data`, + }, }, } ) @@ -220,7 +230,7 @@ func verifyState(ctx *cli.Context) error { log.Error("Failed to load head block") return errors.New("no head block") } - triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false, false) defer triedb.Close() var ( @@ -285,7 +295,7 @@ func traverseState(ctx *cli.Context) error { chaindb := utils.MakeChainDatabase(ctx, stack, true) defer chaindb.Close() - triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false, false) defer triedb.Close() headBlock := rawdb.ReadHeadBlock(chaindb) @@ -394,7 +404,7 @@ func traverseRawState(ctx *cli.Context) error { chaindb := utils.MakeChainDatabase(ctx, stack, true) defer chaindb.Close() - triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false, false) defer triedb.Close() headBlock := rawdb.ReadHeadBlock(chaindb) @@ -562,7 +572,7 @@ func dumpState(ctx *cli.Context) error { return err } defer db.Close() - triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false, false) defer triedb.Close() snapConfig := snapshot.Config{ @@ -645,7 +655,7 @@ func snapshotExportPreimages(ctx *cli.Context) error { chaindb := utils.MakeChainDatabase(ctx, stack, true) defer chaindb.Close() - triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false) + triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false, false) defer triedb.Close() var root common.Hash @@ -707,3 +717,70 @@ func checkAccount(ctx *cli.Context) error { log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start))) return nil } + +// mergeIncrSnapshot merges the incremental snapshot into local data. +func mergeIncrSnapshot(ctx *cli.Context) error { + stack, _ := makeConfigNode(ctx) + defer stack.Close() + + chainDB := utils.MakeChainDatabase(ctx, stack, false) + defer chainDB.Close() + + trieDB := utils.MakeTrieDatabase(ctx, stack, chainDB, false, false, false, true) + defer trieDB.Close() + + if !ctx.IsSet(utils.IncrSnapshotPathFlag.Name) { + return errors.New("incremental snapshot path is not set") + } + path := ctx.String(utils.IncrSnapshotPathFlag.Name) + + startBlock, err := trieDB.GetStartBlock() + if err != nil { + log.Error("Failed to get start block", "error", err) + return err + } + dirs, err := rawdb.GetAllIncrDirs(path) + if err != nil { + log.Error("Failed to get all incremental directories", "err", err) + return err + } + if startBlock < dirs[0].StartBlockNum { + return fmt.Errorf("local start block %d is lower than incr first start block %d", startBlock, dirs[0].StartBlockNum) + } + + for i := 1; i < len(dirs); i++ { + prevFile := dirs[i-1] + currFile := dirs[i] + + expectedStartBlock := prevFile.EndBlockNum + 1 + if currFile.StartBlockNum != expectedStartBlock { + return fmt.Errorf("file continuity broken: file %s ends at %d, but file %s starts at %d (expected %d)", + prevFile.Name, prevFile.EndBlockNum, currFile.Name, currFile.StartBlockNum, expectedStartBlock) + } + } + + log.Info("Start merging incremental snapshot", "path", path, "incremental snapshot number", len(dirs)) + for i, dir := range dirs { + if i == len(dirs)-1 { + complete, err := rawdb.CheckIncrSnapshotComplete(dir.Path) + if err != nil { + log.Error("Failed to check last incr snapshot complete", "err", err) + return err + } + if !complete { + log.Warn("Skip last incr snapshot due to data is incomplete") + continue + } + } + + if dir.StartBlockNum >= startBlock && dir.EndBlockNum > startBlock { + if err = core.MergeIncrSnapshot(chainDB, trieDB, dir.Path); err != nil { + log.Error("Failed to merge incremental snapshot", "err", err) + return err + } + } else { + log.Info("Skip merge incremental snapshot", "dir", dir.Name) + } + } + return nil +} diff --git a/cmd/jsutils/getchainstatus.js b/cmd/jsutils/getchainstatus.js index 1eab7fddf4..c890f89d89 100644 --- a/cmd/jsutils/getchainstatus.js +++ b/cmd/jsutils/getchainstatus.js @@ -6,7 +6,7 @@ program.option("--startNum ", "start num"); program.option("--endNum ", "end num"); program.option("--miner ", "miner", ""); program.option("--num ", "validator num", 21); -program.option("--turnLength ", "the consecutive block length", 8); +program.option("--turnLength ", "the consecutive block length", 16); program.option("--topNum ", "top num of address to be displayed", 20); program.option("--blockNum ", "block num", 0); program.option("--stepLength ", "step length", 115200); @@ -43,7 +43,7 @@ function printUsage() { console.log(" --stepNum the step num, default: 1"); console.log("\nExample:"); console.log(" node getchainstatus.js GetMaxTxCountInBlockRange --rpc https://bsc-testnet-dataseed.bnbchain.org --startNum 40000001 --endNum 40000005"); - console.log(" node getchainstatus.js GetBinaryVersion --rpc https://bsc-testnet-dataseed.bnbchain.org --num 21 --turnLength 8"); + console.log(" node getchainstatus.js GetBinaryVersion --rpc https://bsc-testnet-dataseed.bnbchain.org --num 21 --turnLength 16"); console.log(" node getchainstatus.js GetTopAddr --rpc https://bsc-testnet-dataseed.bnbchain.org --startNum 40000001 --endNum 40000010 --topNum 10"); console.log(" node getchainstatus.js GetSlashCount --rpc https://bsc-testnet-dataseed.bnbchain.org --blockNum 40000001 --stepNum 1 --stepLength 115200"); // default: latest block console.log(" node getchainstatus.js GetPerformanceData --rpc https://bsc-testnet-dataseed.bnbchain.org --startNum 40000001 --endNum 40000010"); @@ -410,7 +410,7 @@ async function getSlashCountAtHeight(num) { } let slashScale = await validatorSet.maintainSlashScale({ blockTag: blockNum }); let maxElected = await stakeHub.maxElectedValidators({ blockTag: blockNum }); - const maintainThreshold = BigInt(150); // governable, hardcode to avoid one RPC call + const maintainThreshold = BigInt(200); // governable, hardcode to avoid one RPC call const felonyThreshold = BigInt(600); // governable, hardcode to avoid one RPC call let block = await provider.getBlock(blockNum); @@ -468,7 +468,7 @@ async function getPerformanceData() { let gasUsedTotal = 0; let inturnBlocks = 0; let justifiedBlocks = 0; - let turnLength = 8; + let turnLength = 16; let lastTimestamp = null; let parliaEnabled = true; diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 50092dc4f4..aa7bef1a97 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -43,6 +43,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/opcodeCompiler/compiler" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" @@ -182,6 +183,11 @@ var ( Usage: "Chapel network: pre-configured Proof-of-Stake-Authority BSC test network", Category: flags.EthCategory, } + EnableBALFlag = &cli.BoolFlag{ + Name: "enablebal", + Usage: "Enable block access list feature, validator will generate BAL for each block", + Category: flags.EthCategory, + } // Dev mode DeveloperFlag = &cli.BoolFlag{ Name: "dev", @@ -1195,6 +1201,12 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Category: flags.MetricsCategory, } + VMOpcodeOptimizeFlag = &cli.BoolFlag{ + Name: "vm.opcode.optimize", + Usage: "enable opcode optimization", + Category: flags.VMCategory, + } + CheckSnapshotWithMPT = &cli.BoolFlag{ Name: "check-snapshot-with-mpt", Usage: "Enable checking between snapshot and MPT ", @@ -1269,6 +1281,50 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Value: fakebeacon.DefaultPort, Category: flags.APICategory, } + + // incremental snapshot related flags + EnableIncrSnapshotFlag = &cli.BoolFlag{ + Name: "incr.enable", + Usage: "Enable incremental snapshot generation", + Value: false, + Category: flags.StateCategory, + } + IncrSnapshotPathFlag = &flags.DirectoryFlag{ + Name: "incr.datadir", + Usage: "Data directory for storing incremental snapshot data: can be used to store generated or downloaded incremental snapshot", + Value: "", + Category: flags.StateCategory, + } + IncrSnapshotBlockIntervalFlag = &cli.Uint64Flag{ + Name: "incr.block-interval", + Usage: "Set how many blocks interval are stored into one incremental snapshot", + Value: pathdb.DefaultBlockInterval, + Category: flags.StateCategory, + } + IncrSnapshotStateBufferFlag = &cli.Uint64Flag{ + Name: "incr.state-buffer", + Usage: "Set the incr state memory buffer to aggregate MPT trie nodes. The larger the setting, the smaller the incr snapshot size", + Value: pathdb.DefaultIncrStateBufferSize, + Category: flags.StateCategory, + } + IncrSnapshotKeptBlocksFlag = &cli.Uint64Flag{ + Name: "incr.kept-blocks", + Usage: "Set how many blocks are kept in incr snapshot. At least is 1024 blocks", + Value: pathdb.DefaultKeptBlocks, + Category: flags.StateCategory, + } + UseRemoteIncrSnapshotFlag = &cli.BoolFlag{ + Name: "incr.use-remote", + Usage: "Enable download and merge incremental snapshots into local data", + Value: false, + Category: flags.StateCategory, + } + RemoteIncrSnapshotURLFlag = &cli.StringFlag{ + Name: "incr.remote-url", + Usage: "Set from which remote url is used to download incremental snapshots", + Value: "", + Category: flags.StateCategory, + } ) var ( @@ -1743,6 +1799,9 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { if ctx.IsSet(DisableSnapProtocolFlag.Name) { cfg.DisableSnapProtocol = ctx.Bool(DisableSnapProtocolFlag.Name) } + if ctx.IsSet(EnableBALFlag.Name) { + cfg.EnableBAL = ctx.Bool(EnableBALFlag.Name) + } if ctx.IsSet(RangeLimitFlag.Name) { cfg.RangeLimit = ctx.Bool(RangeLimitFlag.Name) } @@ -2042,6 +2101,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(CacheNoPrefetchFlag.Name) { cfg.NoPrefetch = ctx.Bool(CacheNoPrefetchFlag.Name) } + if ctx.IsSet(EnableBALFlag.Name) { + cfg.EnableBAL = ctx.Bool(EnableBALFlag.Name) + } // Read the value from the flag no matter if it's set or not. cfg.Preimages = ctx.Bool(CachePreimagesFlag.Name) if cfg.NoPruning && !cfg.Preimages { @@ -2149,6 +2211,13 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) } + if ctx.IsSet(VMOpcodeOptimizeFlag.Name) { + cfg.EnableOpcodeOptimizing = ctx.Bool(VMOpcodeOptimizeFlag.Name) + if cfg.EnableOpcodeOptimizing { + compiler.EnableOptimization() + } + } + if ctx.IsSet(RPCGlobalGasCapFlag.Name) { cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name) } @@ -2307,6 +2376,38 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name) } } + + // Download and merge incremental snapshot config + if ctx.IsSet(UseRemoteIncrSnapshotFlag.Name) { + cfg.UseRemoteIncrSnapshot = true + if !ctx.IsSet(RemoteIncrSnapshotURLFlag.Name) { + Fatalf("Must provide a remote increment snapshot URL") + } else { + cfg.RemoteIncrSnapshotURL = ctx.String(RemoteIncrSnapshotURLFlag.Name) + } + if ctx.IsSet(IncrSnapshotPathFlag.Name) { + cfg.IncrSnapshotPath = ctx.String(IncrSnapshotPathFlag.Name) + } else { + Fatalf("Must provide a path to store downloaded incr snapshot") + } + } + + // enable incremental snapshot generation config + if ctx.IsSet(EnableIncrSnapshotFlag.Name) { + cfg.EnableIncrSnapshots = true + if ctx.IsSet(IncrSnapshotPathFlag.Name) { + cfg.IncrSnapshotPath = ctx.String(IncrSnapshotPathFlag.Name) + } + if ctx.IsSet(IncrSnapshotBlockIntervalFlag.Name) { + cfg.IncrSnapshotBlockInterval = ctx.Uint64(IncrSnapshotBlockIntervalFlag.Name) + } + if ctx.IsSet(IncrSnapshotStateBufferFlag.Name) { + cfg.IncrSnapshotStateBuffer = ctx.Uint64(IncrSnapshotStateBufferFlag.Name) + } + if ctx.IsSet(IncrSnapshotKeptBlocksFlag.Name) { + cfg.IncrSnapshotKeptBlocks = ctx.Uint64(IncrSnapshotKeptBlocksFlag.Name) + } + } } // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if @@ -2697,6 +2798,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh options := &core.BlockChainConfig{ TrieCleanLimit: ethconfig.Defaults.TrieCleanCache, NoPrefetch: ctx.Bool(CacheNoPrefetchFlag.Name), + EnableBAL: ctx.Bool(EnableBALFlag.Name), TrieDirtyLimit: ethconfig.Defaults.TrieDirtyCache, ArchiveMode: ctx.String(GCModeFlag.Name) == "archive", TrieTimeLimit: ethconfig.Defaults.TrieTimeout, @@ -2732,7 +2834,12 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh options.TriesInMemory = ctx.Uint64(TriesInMemoryFlag.Name) } vmcfg := vm.Config{ - EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name), + EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name), + EnableOpcodeOptimizations: ctx.Bool(VMOpcodeOptimizeFlag.Name), + } + + if vmcfg.EnableOpcodeOptimizations { + compiler.EnableOptimization() } if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { @@ -2771,7 +2878,7 @@ func MakeConsolePreloads(ctx *cli.Context) []string { } // MakeTrieDatabase constructs a trie database based on the configured scheme. -func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database { +func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool, mergeIncr bool) *triedb.Database { config := &triedb.Config{ Preimages: preimage, IsVerkle: isVerkle, @@ -2791,6 +2898,9 @@ func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, p config.PathDB = pathdb.ReadOnly } else { config.PathDB = pathdb.Defaults + if mergeIncr { + config.PathDB.MergeIncr = true + } } config.PathDB.JournalFilePath = fmt.Sprintf("%s/%s", stack.ResolvePath("chaindata"), eth.JournalFileName) return triedb.NewDatabase(disk, config) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 0f11aada2c..aaa1111456 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -491,6 +491,15 @@ func (beacon *Beacon) SealHash(header *types.Header) common.Hash { return beacon.ethone.SealHash(header) } +func (beacon *Beacon) SignBAL(blockAccessList *types.BlockAccessListEncode) error { + return nil +} + +// VerifyBAL verifies the BAL of the block +func (beacon *Beacon) VerifyBAL(block *types.Block, bal *types.BlockAccessListEncode) error { + return nil +} + // CalcDifficulty is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 7e5e85c578..411ddc5fd4 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -787,3 +787,11 @@ func encodeSigHeader(w io.Writer, header *types.Header) { panic("can't encode: " + err.Error()) } } + +func (c *Clique) SignBAL(bal *types.BlockAccessListEncode) error { + return nil +} + +func (c *Clique) VerifyBAL(block *types.Block, bal *types.BlockAccessListEncode) error { + return nil +} diff --git a/consensus/consensus.go b/consensus/consensus.go index 115a28a76e..714382f37d 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -68,7 +68,7 @@ type ChainHeaderReader interface { } type VotePool interface { - FetchVoteByBlockHash(blockHash common.Hash) []*types.VoteEnvelope + FetchVotesByBlockHash(blockHash common.Hash) []*types.VoteEnvelope } // ChainReader defines a small collection of methods needed to access the local @@ -136,6 +136,12 @@ type Engine interface { // SealHash returns the hash of a block prior to it being sealed. SealHash(header *types.Header) common.Hash + // SignBAL signs the BAL of the block + SignBAL(blockAccessList *types.BlockAccessListEncode) error + + // VerifyBAL verifies the BAL of the block + VerifyBAL(block *types.Block, bal *types.BlockAccessListEncode) error + // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty // that a new block should have. CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index f624f73875..3027898981 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -76,3 +76,11 @@ func (ethash *Ethash) Close() error { func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { panic("ethash (pow) sealing not supported any more") } + +func (ethash *Ethash) SignBAL(bal *types.BlockAccessListEncode) error { + return nil +} + +func (ethash *Ethash) VerifyBAL(block *types.Block, bal *types.BlockAccessListEncode) error { + return nil +} diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 8379bf7738..e6d77c6c15 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -61,6 +61,7 @@ const ( defaultBlockInterval uint64 = 3000 // Default block interval in milliseconds lorentzBlockInterval uint64 = 1500 // Block interval starting from the Lorentz hard fork maxwellBlockInterval uint64 = 750 // Block interval starting from the Maxwell hard fork + fermiBlockInterval uint64 = 450 // Block interval starting from the Fermi hard fork defaultTurnLength uint8 = 1 // Default consecutive number of blocks a validator receives priority for block production extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity @@ -84,6 +85,8 @@ const ( // `finalityRewardInterval` should be smaller than `inMemorySnapshots`, otherwise, it will result in excessive computation. finalityRewardInterval = 200 + + kAncestorGenerationDepth = 3 ) var ( @@ -451,8 +454,17 @@ func (p *Parlia) getParent(chain consensus.ChainHeaderReader, header *types.Head return parent, nil } +// trimParents safely removes last element if exists. +func trimParents(parents []*types.Header) []*types.Header { + if len(parents) > 1 { + return parents[:len(parents)-1] + } + return nil +} + // verifyVoteAttestation checks whether the vote attestation in the header is valid. func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + // === Step 1: Extract attestation === epochLength, err := p.epochLength(chain, header, parents) if err != nil { return err @@ -470,21 +482,15 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header if len(attestation.Extra) > types.MaxAttestationExtraLength { return fmt.Errorf("invalid attestation, too large extra length: %d", len(attestation.Extra)) } + if attestation.Data.SourceNumber >= attestation.Data.TargetNumber { + return errors.New("invalid attestation, SourceNumber not lower than TargetNumber") + } - // Get parent block + // === Step 2: Verify source block === parent, err := p.getParent(chain, header, parents) if err != nil { return err } - - // The target block should be direct parent. - targetNumber := attestation.Data.TargetNumber - targetHash := attestation.Data.TargetHash - if targetNumber != parent.Number.Uint64() || targetHash != parent.Hash() { - return fmt.Errorf("invalid attestation, target mismatch, expected block: %d, hash: %s; real block: %d, hash: %s", - parent.Number.Uint64(), parent.Hash(), targetNumber, targetHash) - } - // The source block should be the highest justified block. sourceNumber := attestation.Data.SourceNumber sourceHash := attestation.Data.SourceHash @@ -501,17 +507,34 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header justifiedBlockNumber, justifiedBlockHash, sourceNumber, sourceHash) } - // The snapshot should be the targetNumber-1 block's snapshot. - if len(parents) > 1 { - parents = parents[:len(parents)-1] - } else { - parents = nil + // === Step 3: Verify target block === + targetNumber := attestation.Data.TargetNumber + targetHash := attestation.Data.TargetHash + match := false + ancestor := parent + ancestorParents := trimParents(parents) + for range p.GetAncestorGenerationDepth(header) { + if targetNumber == ancestor.Number.Uint64() && targetHash == ancestor.Hash() { + match = true + break + } + + ancestor, err = p.getParent(chain, ancestor, ancestorParents) + if err != nil { + return err + } + ancestorParents = trimParents(ancestorParents) } - snap, err := p.snapshot(chain, parent.Number.Uint64()-1, parent.ParentHash, parents) + if !match { + return fmt.Errorf("invalid attestation, target mismatch, real block: %d, hash: %s", targetNumber, targetHash) + } + + // === Step 4: Check quorum === + // The snapshot should be the targetNumber-1 block's snapshot. + snap, err := p.snapshot(chain, ancestor.Number.Uint64()-1, ancestor.ParentHash, ancestorParents) if err != nil { return err } - // Filter out valid validator from attestation. validators := snap.validators() validatorsBitSet := bitset.From([]uint64{uint64(attestation.VoteAddressSet)}) @@ -530,13 +553,12 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header } votedAddrs = append(votedAddrs, voteAddr) } - // The valid voted validators should be no less than 2/3 validators. if len(votedAddrs) < cmath.CeilDiv(len(snap.Validators)*2, 3) { return errors.New("invalid attestation, not enough validators voted") } - // Verify the aggregated signature. + // === Step 5: Signature verification === aggSig, err := bls.SignatureFromBytes(attestation.AggSignature[:]) if err != nil { return fmt.Errorf("BLS signature converts failed: %v", err) @@ -788,7 +810,9 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash blockHeader := chain.GetHeaderByNumber(number) if blockHeader != nil { blockHash = blockHeader.Hash() - if p.chainConfig.IsMaxwell(blockHeader.Number, blockHeader.Time) { + if p.chainConfig.IsFermi(blockHeader.Number, blockHeader.Time) { + blockInterval = fermiBlockInterval + } else if p.chainConfig.IsMaxwell(blockHeader.Number, blockHeader.Time) { blockInterval = maxwellBlockInterval } else if p.chainConfig.IsLorentz(blockHeader.Number, blockHeader.Time) { blockInterval = lorentzBlockInterval @@ -1022,55 +1046,72 @@ func (p *Parlia) prepareTurnLength(chain consensus.ChainHeaderReader, header *ty return nil } +// assembleVoteAttestation collects votes and assembles the vote attestation into the block header. func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, header *types.Header) error { - if !p.chainConfig.IsLuban(header.Number) || header.Number.Uint64() < 2 { + // === Step 1: Preconditions === + if !p.chainConfig.IsLuban(header.Number) || header.Number.Uint64() < 3 || p.VotePool == nil { return nil } - if p.VotePool == nil { - return nil - } - - // Fetch direct parent's votes + // === Step 2: Find target header with quorum votes === parent := chain.GetHeaderByHash(header.ParentHash) if parent == nil { return errors.New("parent not found") } - snap, err := p.snapshot(chain, parent.Number.Uint64()-1, parent.ParentHash, nil) + justifiedBlockNumber, justifiedBlockHash, err := p.GetJustifiedNumberAndHash(chain, []*types.Header{parent}) if err != nil { - return err + return errors.New("unexpected error when getting the highest justified number and hash") + } + var ( + votes []*types.VoteEnvelope + targetHeader = parent + targetHeaderParentSnap *Snapshot + ) + for range p.GetAncestorGenerationDepth(header) { + snap, err := p.snapshot(chain, targetHeader.Number.Uint64()-1, targetHeader.ParentHash, nil) + if err != nil { + return err + } + votes = p.VotePool.FetchVotesByBlockHash(targetHeader.Hash()) + quorum := cmath.CeilDiv(len(snap.Validators)*2, 3) + if len(votes) >= quorum { + targetHeaderParentSnap = snap + break + } + + targetHeader = chain.GetHeaderByHash(targetHeader.ParentHash) + if targetHeader == nil { + return errors.New("parent not found") + } + if targetHeader.Number.Uint64() <= justifiedBlockNumber { + break + } } - votes := p.VotePool.FetchVoteByBlockHash(parent.Hash()) - if len(votes) < cmath.CeilDiv(len(snap.Validators)*2, 3) { + if targetHeaderParentSnap == nil { return nil } - // Prepare vote attestation - // Prepare vote data - justifiedBlockNumber, justifiedBlockHash, err := p.GetJustifiedNumberAndHash(chain, []*types.Header{parent}) - if err != nil { - return errors.New("unexpected error when getting the highest justified number and hash") - } + // === Step 3: Build vote attestation === attestation := &types.VoteAttestation{ Data: &types.VoteData{ SourceNumber: justifiedBlockNumber, SourceHash: justifiedBlockHash, - TargetNumber: parent.Number.Uint64(), - TargetHash: parent.Hash(), + TargetNumber: targetHeader.Number.Uint64(), + TargetHash: targetHeader.Hash(), }, } - // Check vote data from votes + // Validate vote data consistency for _, vote := range votes { if vote.Data.Hash() != attestation.Data.Hash() { - return fmt.Errorf("vote check error, expected: %v, real: %v", attestation.Data, vote) + return fmt.Errorf("vote check error, expected: %v, real: %v", attestation.Data, vote.Data) } } // Prepare aggregated vote signature voteAddrSet := make(map[types.BLSPublicKey]struct{}, len(votes)) - signatures := make([][]byte, 0, len(votes)) - for _, vote := range votes { + signatures := make([][]byte, len(votes)) + for i, vote := range votes { voteAddrSet[vote.VoteAddress] = struct{}{} - signatures = append(signatures, vote.Signature[:]) + signatures[i] = vote.Signature[:] } sigs, err := bls.MultipleSignaturesFromBytes(signatures) if err != nil { @@ -1078,28 +1119,25 @@ func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, head } copy(attestation.AggSignature[:], bls.AggregateSignatures(sigs).Marshal()) // Prepare vote address bitset. - for _, valInfo := range snap.Validators { + for _, valInfo := range targetHeaderParentSnap.Validators { if _, ok := voteAddrSet[valInfo.VoteAddress]; ok { attestation.VoteAddressSet |= 1 << (valInfo.Index - 1) // Index is offset by 1 } } - validatorsBitSet := bitset.From([]uint64{uint64(attestation.VoteAddressSet)}) - if validatorsBitSet.Count() < uint(len(signatures)) { - log.Warn(fmt.Sprintf("assembleVoteAttestation, check VoteAddress Set failed, expected:%d, real:%d", len(signatures), validatorsBitSet.Count())) + bitsetCount := bitset.From([]uint64{uint64(attestation.VoteAddressSet)}).Count() + if bitsetCount < uint(len(signatures)) { + log.Warn(fmt.Sprintf("assembleVoteAttestation, check VoteAddress Set failed, expected:%d, real:%d", len(signatures), bitsetCount)) return errors.New("invalid attestation, check VoteAddress Set failed") } - // Append attestation to header extra field. + // === Step 4: Encode & insert into header extra === buf := new(bytes.Buffer) - err = rlp.Encode(buf, attestation) - if err != nil { - return err + if err = rlp.Encode(buf, attestation); err != nil { + return fmt.Errorf("attestation: failed to encode: %w", err) } - - // Insert vote attestation into header extra ahead extra seal. extraSealStart := len(header.Extra) - extraSeal extraSealBytes := header.Extra[extraSealStart:] - header.Extra = append(header.Extra[0:extraSealStart], buf.Bytes()...) + header.Extra = append(header.Extra[:extraSealStart], buf.Bytes()...) header.Extra = append(header.Extra, extraSealBytes...) return nil @@ -1639,12 +1677,20 @@ func (p *Parlia) Authorize(val common.Address, signFn SignerFn, signTxFn SignerT // Argument leftOver is the time reserved for block finalize(calculate root, distribute income...) func (p *Parlia) Delay(chain consensus.ChainReader, header *types.Header, leftOver *time.Duration) *time.Duration { - number := header.Number.Uint64() - snap, err := p.snapshot(chain, number-1, header.ParentHash, nil) + snap, err := p.snapshot(chain, header.Number.Uint64()-1, header.ParentHash, nil) if err != nil { return nil } + delay := p.delayForRamanujanFork(snap, header) + // The blocking time should be no more than half of period when snap.TurnLength == 1 + timeForMining := time.Duration(snap.BlockInterval) * time.Millisecond / 2 + if !snap.lastBlockInOneTurn(header.Number.Uint64()) { + timeForMining = time.Duration(snap.BlockInterval) * time.Millisecond + } + if delay > timeForMining { + delay = timeForMining + } if *leftOver >= time.Duration(snap.BlockInterval)*time.Millisecond { // ignore invalid leftOver @@ -1656,14 +1702,6 @@ func (p *Parlia) Delay(chain consensus.ChainReader, header *types.Header, leftOv delay = delay - *leftOver } - // The blocking time should be no more than half of period when snap.TurnLength == 1 - timeForMining := time.Duration(snap.BlockInterval) * time.Millisecond / 2 - if !snap.lastBlockInOneTurn(header.Number.Uint64()) { - timeForMining = time.Duration(snap.BlockInterval) * time.Millisecond * 4 / 5 - } - if delay > timeForMining { - delay = timeForMining - } return &delay } @@ -1716,7 +1754,7 @@ func (p *Parlia) Seal(chain consensus.ChainHeaderReader, block *types.Block, res if err != nil { /* If the vote attestation can't be assembled successfully, the blockchain won't get fast finalized, but it can be tolerated, so just report this error here. */ - log.Error("Assemble vote attestation failed when sealing", "err", err) + log.Debug("Assemble vote attestation failed when sealing", "err", err) } // Sign all the things! @@ -1755,6 +1793,71 @@ func (p *Parlia) Seal(chain consensus.ChainHeaderReader, block *types.Block, res return nil } +func (p *Parlia) SignBAL(blockAccessList *types.BlockAccessListEncode) error { + p.lock.RLock() + val, signFn := p.val, p.signFn + p.lock.RUnlock() + + data, err := rlp.EncodeToBytes([]interface{}{blockAccessList.Version, blockAccessList.Number, blockAccessList.Hash, blockAccessList.Accounts}) + if err != nil { + log.Error("Encode to bytes failed when sealing", "err", err) + return errors.New("encode to bytes failed") + } + + if len(data) > int(params.MaxBALSize) { + log.Error("data is too large", "dataSize", len(data), "maxSize", params.MaxBALSize) + return errors.New("data is too large") + } + + sig, err := signFn(accounts.Account{Address: val}, accounts.MimetypeParlia, data) + if err != nil { + log.Error("Sign for the block header failed when sealing", "err", err) + return errors.New("sign for the block header failed") + } + + copy(blockAccessList.SignData, sig) + return nil +} + +func (p *Parlia) VerifyBAL(block *types.Block, bal *types.BlockAccessListEncode) error { + if bal.Version != 0 { + log.Error("invalid BAL version", "version", bal.Version) + return errors.New("invalid BAL version") + } + + if len(bal.SignData) != 65 { + log.Error("invalid BAL signature", "signatureSize", len(bal.SignData)) + return errors.New("invalid BAL signature") + } + + // Recover the public key and the Ethereum address + data, err := rlp.EncodeToBytes([]interface{}{bal.Version, block.Number(), block.Hash(), bal.Accounts}) + if err != nil { + log.Error("encode to bytes failed", "err", err) + return errors.New("encode to bytes failed") + } + + if len(data) > int(params.MaxBALSize) { + log.Error("data is too large", "dataSize", len(data), "maxSize", params.MaxBALSize) + return errors.New("data is too large") + } + + pubkey, err := crypto.Ecrecover(crypto.Keccak256(data), bal.SignData) + if err != nil { + return err + } + var pubkeyAddr common.Address + copy(pubkeyAddr[:], crypto.Keccak256(pubkey[1:])[12:]) + + signer := block.Header().Coinbase + if signer != pubkeyAddr { + log.Error("BAL signer mismatch", "signer", signer, "pubkeyAddr", pubkeyAddr, "bal.Number", bal.Number, "bal.Hash", bal.Hash) + return errors.New("signer mismatch") + } + + return nil +} + func (p *Parlia) shouldWaitForCurrentBlockProcess(chain consensus.ChainHeaderReader, header *types.Header, snap *Snapshot) bool { if header.Difficulty.Cmp(diffInTurn) == 0 { return false @@ -2368,6 +2471,15 @@ func (p *Parlia) detectNewVersionWithFork(chain consensus.ChainHeaderReader, hea } } +// TODO(Nathan): use kAncestorGenerationDepth directly instead of this func once Fermi hardfork passed +func (p *Parlia) GetAncestorGenerationDepth(header *types.Header) uint64 { + if p.chainConfig.IsFermi(header.Number, header.Time) { + return kAncestorGenerationDepth + } + + return 1 +} + // chain context type chainContext struct { Chain consensus.ChainHeaderReader diff --git a/consensus/parlia/parlia_test.go b/consensus/parlia/parlia_test.go index 005f90a983..d726ae6a27 100644 --- a/consensus/parlia/parlia_test.go +++ b/consensus/parlia/parlia_test.go @@ -10,6 +10,7 @@ import ( "strings" "testing" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" @@ -618,15 +619,16 @@ func TestSimulateP2P(t *testing.T) { if err != nil { t.Fatalf("[Testcase %d] simulate P2P error: %v", index, err) } - for _, val := range c.validators { - t.Logf("[Testcase %d] validator(%d) head block: %d", - index, val.index, val.head.blockNumber) - t.Logf("[Testcase %d] validator(%d) highest justified block: %d", - index, val.index, val.head.GetJustifiedNumber()) - t.Logf("[Testcase %d] validator(%d) highest finalized block: %d", - index, val.index, val.head.GetFinalizedBlock().blockNumber) - } - + /* + for _, val := range c.validators { + t.Logf("[Testcase %d] validator(%d) head block: %d", + index, val.index, val.head.blockNumber) + t.Logf("[Testcase %d] validator(%d) highest justified block: %d", + index, val.index, val.head.GetJustifiedNumber()) + t.Logf("[Testcase %d] validator(%d) highest finalized block: %d", + index, val.index, val.head.GetFinalizedBlock().blockNumber) + } + */ if c.CheckChain() == false { t.Fatalf("[Testcase %d] chain not works as expected", index) } @@ -858,3 +860,490 @@ func (c *mockParlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, head func (c *mockParlia) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { return big.NewInt(1) } + +func TestSignBAL(t *testing.T) { + // Setup test environment + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + // Create mock signing function that succeeds + mockSignFn := func(account accounts.Account, mimeType string, data []byte) ([]byte, error) { + if account.Address != addr { + return nil, fmt.Errorf("wrong address") + } + if mimeType != accounts.MimetypeParlia { + return nil, fmt.Errorf("wrong mime type") + } + // Return a dummy 65-byte signature + sig := make([]byte, 65) + copy(sig, []byte("test_signature_data_for_testing_purposes_123456789012345678901234")) + return sig, nil + } + + // Create Parlia instance + parlia := &Parlia{ + val: addr, + signFn: mockSignFn, + } + + tests := []struct { + name string + bal *types.BlockAccessListEncode + expectedError bool + signFn SignerFn + description string + }{ + { + name: "successful signing", + bal: &types.BlockAccessListEncode{ + Version: 0, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1234567890123456789012345678901234567890"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + }, + }, + }, + }, + expectedError: false, + signFn: mockSignFn, + description: "Should successfully sign a valid BlockAccessListEncode", + }, + { + name: "signing function error", + bal: &types.BlockAccessListEncode{ + Version: 0, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{}, + }, + expectedError: true, + signFn: func(account accounts.Account, mimeType string, data []byte) ([]byte, error) { + return nil, fmt.Errorf("signing failed") + }, + description: "Should return error when signing function fails", + }, + { + name: "empty accounts list", + bal: &types.BlockAccessListEncode{ + Version: 0, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{}, + }, + expectedError: false, + signFn: mockSignFn, + description: "Should successfully sign even with empty accounts list", + }, + { + name: "multiple accounts", + bal: &types.BlockAccessListEncode{ + Version: 2, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1111111111111111111111111111111111111111"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + {Key: common.HexToHash("0x02"), TxIndex: 1, Dirty: true}, + }, + }, + { + Address: common.HexToAddress("0x2222222222222222222222222222222222222222"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x03"), TxIndex: 2, Dirty: false}, + }, + }, + }, + }, + expectedError: false, + signFn: mockSignFn, + description: "Should successfully sign with multiple accounts", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set up Parlia with the test signing function + parlia.signFn = tt.signFn + + // Call SignBAL + err := parlia.SignBAL(tt.bal) + + // Check results + if tt.expectedError { + if err == nil { + t.Errorf("Expected error but got none. %s", tt.description) + } + } else { + if err != nil { + t.Errorf("Expected no error but got: %v. %s", err, tt.description) + } + // Verify signature was copied to SignData + if tt.bal != nil && len(tt.bal.SignData) != 65 { + t.Errorf("Expected SignData to be 65 bytes, got %d", len(tt.bal.SignData)) + } + // Verify signature content (for successful cases) + if tt.bal != nil && !tt.expectedError { + expectedSig := "test_signature_data_for_testing_purposes_123456789012345678901234" + if string(tt.bal.SignData[:len(expectedSig)]) != expectedSig { + t.Errorf("SignData was not properly set") + } + } + } + }) + } +} + +func TestVerifyBAL(t *testing.T) { + // Setup test environment + signerKey, _ := crypto.GenerateKey() + signerAddr := crypto.PubkeyToAddress(signerKey.PublicKey) + + // Helper function to create a properly signed BAL + createBlockWithBAL := func(addr common.Address, version uint32, signLength int, accounts []types.AccountAccessListEncode) *types.Block { + header := &types.Header{ + ParentHash: types.EmptyRootHash, + Number: big.NewInt(10), + Coinbase: addr, + } + block := types.NewBlock(header, nil, nil, nil) + bal := &types.BlockAccessListEncode{ + Version: version, + Number: block.Number().Uint64(), + Hash: block.Hash(), + SignData: make([]byte, signLength), + Accounts: accounts, + } + + // RLP encode the data + data, _ := rlp.EncodeToBytes([]interface{}{bal.Version, bal.Number, bal.Hash, bal.Accounts}) + + // Create signature using the test key + hash := crypto.Keccak256(data) + sig, _ := crypto.Sign(hash, signerKey) + copy(bal.SignData, sig) + block = block.WithBAL(bal) + return block + } + + // Create a Parlia instance + parlia := &Parlia{} + + tests := []struct { + name string + block *types.Block + expectedError bool + description string + }{ + { + name: "valid signature verification", + block: createBlockWithBAL(signerAddr, 0, 65, []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1234567890123456789012345678901234567890"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + }, + }, + }), + expectedError: false, + description: "Should successfully verify a properly signed BAL", + }, + { + name: "invalid version", + block: createBlockWithBAL(signerAddr, 1, 65, []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1234567890123456789012345678901234567890"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + }, + }, + }), + expectedError: true, + description: "Should fail when version is invalid", + }, + { + name: "invalid signature length - too short", + block: createBlockWithBAL(signerAddr, 0, 64, []types.AccountAccessListEncode{}), + expectedError: true, + description: "Should fail when signature is too short", + }, + { + name: "invalid signature length - too long", + block: createBlockWithBAL(signerAddr, 0, 66, []types.AccountAccessListEncode{}), + expectedError: true, + description: "Should fail when signature is too long", + }, + { + name: "empty signature", + block: createBlockWithBAL(signerAddr, 0, 0, []types.AccountAccessListEncode{}), + expectedError: true, + description: "Should fail with empty signature", + }, + { + name: "signer mismatch", + block: createBlockWithBAL(common.HexToAddress("0x1234567890123456789012345678901234567890"), 0, 65, []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1234567890123456789012345678901234567890"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + }, + }, + }), + expectedError: true, + description: "Should fail when signer address doesn't match recovered address", + }, + { + name: "empty accounts list", + block: createBlockWithBAL(signerAddr, 0, 65, []types.AccountAccessListEncode{}), + expectedError: false, + description: "Should successfully verify BAL with empty accounts", + }, + { + name: "multiple accounts", + block: createBlockWithBAL(signerAddr, 0, 65, []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1111111111111111111111111111111111111111"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + {Key: common.HexToHash("0x02"), TxIndex: 1, Dirty: true}, + }, + }, + { + Address: common.HexToAddress("0x2222222222222222222222222222222222222222"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x03"), TxIndex: 2, Dirty: false}, + }, + }, + }), + expectedError: false, + description: "Should successfully verify BAL with multiple accounts", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := parlia.VerifyBAL(tt.block, tt.block.BAL()) + if tt.expectedError { + if err == nil { + t.Errorf("Expected error but got none. %s", tt.description) + } + } else { + if err != nil { + t.Errorf("Expected no error but got: %v. %s", err, tt.description) + } + } + }) + } +} + +func TestVerifyBAL_EdgeCases(t *testing.T) { + // Test with different key to ensure proper signature verification + key1, _ := crypto.GenerateKey() + key2, _ := crypto.GenerateKey() + addr1 := crypto.PubkeyToAddress(key1.PublicKey) + addr2 := crypto.PubkeyToAddress(key2.PublicKey) + + parlia := &Parlia{} + + header1 := &types.Header{ + ParentHash: types.EmptyRootHash, + Number: big.NewInt(10), + Coinbase: addr1, + } + block1 := types.NewBlock(header1, nil, nil, nil) + // Create BAL signed with key1 + bal := &types.BlockAccessListEncode{ + Version: 0, + Number: block1.Number().Uint64(), + Hash: block1.Hash(), + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1234567890123456789012345678901234567890"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + }, + }, + }, + } + + // Sign with key1 + data, _ := rlp.EncodeToBytes([]interface{}{bal.Version, bal.Number, bal.Hash, bal.Accounts}) + hash := crypto.Keccak256(data) + sig, _ := crypto.Sign(hash, key1) + copy(bal.SignData, sig) + + // Should succeed with addr1 + err := parlia.VerifyBAL(block1, bal) + if err != nil { + t.Errorf("Verification with correct signer failed: %v", err) + } + + // Should fail with addr2 (different key) + header2 := &types.Header{ + ParentHash: types.EmptyRootHash, + Number: big.NewInt(10), + Coinbase: addr2, + } + block2 := types.NewBlock(header2, nil, nil, nil) + err = parlia.VerifyBAL(block2, bal) + if err == nil { + t.Error("Expected verification to fail with different signer address") + } +} + +func TestVerifyBAL_TooLargeData(t *testing.T) { + // Test with large amount of data to ensure RLP encoding works correctly + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + parlia := &Parlia{} + + // Create BAL with many accounts + accounts := make([]types.AccountAccessListEncode, 20000) + for i := 0; i < 20000; i++ { + accounts[i] = types.AccountAccessListEncode{ + Address: common.BigToAddress(big.NewInt(int64(i))), + StorageItems: []types.StorageAccessItem{ + {Key: common.BigToHash(big.NewInt(int64(i))), TxIndex: uint32(i), Dirty: i%2 == 0}, + {Key: common.BigToHash(big.NewInt(int64(i + 1000))), TxIndex: uint32(i + 1), Dirty: i%3 == 0}, + }, + } + } + + header := &types.Header{ + ParentHash: types.EmptyRootHash, + Number: big.NewInt(10), + Coinbase: addr, + } + block := types.NewBlock(header, nil, nil, nil) + bal := &types.BlockAccessListEncode{ + Version: 0, + Number: block.Number().Uint64(), + Hash: block.Hash(), + SignData: make([]byte, 65), + Accounts: accounts, + } + + // Sign the large data + data, err := rlp.EncodeToBytes([]interface{}{bal.Version, bal.Number, bal.Hash, bal.Accounts}) + if err != nil { + t.Fatalf("Failed to RLP encode large data: %v", err) + } + + hash := crypto.Keccak256(data) + sig, err := crypto.Sign(hash, key) + if err != nil { + t.Fatalf("Failed to sign large data: %v", err) + } + copy(bal.SignData, sig) + + // Verify the signature + err = parlia.VerifyBAL(block, bal) + if err.Error() != "data is too large" { + t.Errorf("Failed to verify BAL with large data: %v", err) + } +} + +func TestSignBAL_VerifyBAL_Integration(t *testing.T) { + // Test complete sign-verify cycle + key, _ := crypto.GenerateKey() + addr := crypto.PubkeyToAddress(key.PublicKey) + + // Create mock signing function + mockSignFn := func(account accounts.Account, mimeType string, data []byte) ([]byte, error) { + if account.Address != addr { + return nil, fmt.Errorf("wrong address") + } + if mimeType != accounts.MimetypeParlia { + return nil, fmt.Errorf("wrong mime type") + } + // Use the actual private key to sign + hash := crypto.Keccak256(data) + return crypto.Sign(hash, key) + } + + parlia := &Parlia{ + val: addr, + signFn: mockSignFn, + } + + testCases := []struct { + name string + version uint32 + accounts []types.AccountAccessListEncode + }{ + { + name: "empty accounts", + version: 0, + accounts: []types.AccountAccessListEncode{}, + }, + { + name: "single account", + version: 0, + accounts: []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1234567890123456789012345678901234567890"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + {Key: common.HexToHash("0x02"), TxIndex: 1, Dirty: true}, + }, + }, + }, + }, + { + name: "multiple accounts", + version: 0, + accounts: []types.AccountAccessListEncode{ + { + Address: common.HexToAddress("0x1111111111111111111111111111111111111111"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + }, + }, + { + Address: common.HexToAddress("0x2222222222222222222222222222222222222222"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x02"), TxIndex: 1, Dirty: true}, + {Key: common.HexToHash("0x03"), TxIndex: 2, Dirty: false}, + }, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + header := &types.Header{ + ParentHash: types.EmptyRootHash, + Number: big.NewInt(10), + Coinbase: addr, + } + block := types.NewBlock(header, nil, nil, nil) + // Create BAL + bal := &types.BlockAccessListEncode{ + Version: tc.version, + Number: block.Number().Uint64(), + Hash: block.Hash(), + SignData: make([]byte, 65), + Accounts: tc.accounts, + } + + // Sign the BAL + err := parlia.SignBAL(bal) + if err != nil { + t.Fatalf("SignBAL failed: %v", err) + } + + // Verify signature length + if len(bal.SignData) != 65 { + t.Errorf("Expected SignData to be 65 bytes, got %d", len(bal.SignData)) + } + + // Verify the BAL with correct signer + err = parlia.VerifyBAL(block, bal) + if err != nil { + t.Errorf("VerifyBAL failed with correct signer: %v", err) + } + }) + } +} diff --git a/consensus/parlia/ramanujanfork.go b/consensus/parlia/ramanujanfork.go index e26cebc9d8..53473e0350 100644 --- a/consensus/parlia/ramanujanfork.go +++ b/consensus/parlia/ramanujanfork.go @@ -12,7 +12,7 @@ import ( const ( wiggleTimeBeforeFork = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers fixedBackOffTimeBeforeFork = 200 * time.Millisecond - millisecondsUnit = 250 // not enforced at the consensus level + millisecondsUnit = 50 // not enforced at the consensus level ) func (p *Parlia) delayForRamanujanFork(snap *Snapshot, header *types.Header) time.Duration { diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go index 477098fa85..ecabc62a56 100644 --- a/consensus/parlia/snapshot.go +++ b/consensus/parlia/snapshot.go @@ -207,14 +207,16 @@ func (s *Snapshot) updateAttestation(header *types.Header, chainConfig *params.C } // Headers with bad attestation are accepted before Plato upgrade, - // but Attestation of snapshot is only updated when the target block is direct parent of the header - targetNumber := attestation.Data.TargetNumber - targetHash := attestation.Data.TargetHash - if targetHash != header.ParentHash || targetNumber+1 != header.Number.Uint64() { - log.Warn("updateAttestation failed", "error", fmt.Errorf("invalid attestation, target mismatch, expected block: %d, hash: %s; real block: %d, hash: %s", - header.Number.Uint64()-1, header.ParentHash, targetNumber, targetHash)) - updateAttestationErrorCounter.Inc(1) - return + // but Attestation of snapshot is only updated when the target block is direct parent of the header before Fermi upgrade + if !chainConfig.IsFermi(header.Number, header.Time) { + targetNumber := attestation.Data.TargetNumber + targetHash := attestation.Data.TargetHash + if targetHash != header.ParentHash || targetNumber+1 != header.Number.Uint64() { + log.Warn("updateAttestation failed", "error", fmt.Errorf("invalid attestation, target mismatch, expected block: %d, hash: %s; real block: %d, hash: %s", + header.Number.Uint64()-1, header.ParentHash, targetNumber, targetHash)) + updateAttestationErrorCounter.Inc(1) + return + } } // Update attestation @@ -342,7 +344,9 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity]) - if chainConfig.IsMaxwell(header.Number, header.Time) { + if chainConfig.IsFermi(header.Number, header.Time) { + snap.BlockInterval = fermiBlockInterval + } else if chainConfig.IsMaxwell(header.Number, header.Time) { snap.BlockInterval = maxwellBlockInterval } else if chainConfig.IsLorentz(header.Number, header.Time) { snap.BlockInterval = lorentzBlockInterval diff --git a/core/blockchain.go b/core/blockchain.go index b26bba4aa9..5bc0ab3b4e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -178,11 +178,18 @@ const ( // BlockChainConfig contains the configuration of the BlockChain object. type BlockChainConfig struct { - TriesInMemory uint64 // How many tries keeps in memory - NoTries bool // Insecure settings. Do not have any tries in databases if enabled. - PathSyncFlush bool // Whether sync flush the trienodebuffer of pathdb to disk. - JournalFilePath string - JournalFile bool + TriesInMemory uint64 // How many tries keeps in memory + NoTries bool // Insecure settings. Do not have any tries in databases if enabled. + PathSyncFlush bool // Whether sync flush the trienodebuffer of pathdb to disk. + JournalFilePath string // The path to store journal file which is used in pathdb + JournalFile bool // Whether to use single file to store journal data in pathdb + EnableIncr bool // Flag whether the freezer db stores incremental block and state history + IncrHistoryPath string // The path to store incremental block and chain files + IncrHistory uint64 // Amount of block and state history stored in incremental freezer db + IncrStateBuffer uint64 // Maximum memory allowance (in bytes) for incr state buffer + IncrKeptBlocks uint64 // Amount of block kept in incr snapshot + UseRemoteIncrSnapshot bool // Whether to download and merge incremental snapshots + RemoteIncrURL string // The url to download incremental snapshots // Trie database related options TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory @@ -215,6 +222,9 @@ type BlockChainConfig struct { // If the value is zero, all transactions of the entire chain will be indexed. // If the value is -1, indexing is disabled. TxLookupLimit int64 + + // EnableBAL enables the block access list feature + EnableBAL bool } // DefaultConfig returns the default config. @@ -269,6 +279,11 @@ func (cfg *BlockChainConfig) triedbConfig(isVerkle bool) *triedb.Config { config.PathDB = &pathdb.Config{ JournalFilePath: cfg.JournalFilePath, JournalFile: cfg.JournalFile, + EnableIncr: cfg.EnableIncr, + IncrHistoryPath: cfg.IncrHistoryPath, + IncrHistory: cfg.IncrHistory, + IncrStateBuffer: cfg.IncrStateBuffer, + IncrKeptBlocks: cfg.IncrKeptBlocks, StateHistory: cfg.StateHistory, EnableStateIndexing: cfg.ArchiveMode, @@ -412,7 +427,27 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine, if err != nil { return nil, err } - triedb := triedb.NewDatabase(db, cfg.triedbConfig(enableVerkle)) + trieConfig := cfg.triedbConfig(enableVerkle) + if cfg.UseRemoteIncrSnapshot && cfg.StateScheme == rawdb.PathScheme { + trieConfig.PathDB.MergeIncr = true + } + triedb := triedb.NewDatabase(db, trieConfig) + + if cfg.UseRemoteIncrSnapshot { + log.Info("Download the incremental snapshot", "remote incr url", cfg.RemoteIncrURL) + startBlock, err := triedb.GetStartBlock() + if err != nil { + log.Error("Failed to get start block", "error", err) + return nil, err + } + downloader := NewIncrDownloader(db, triedb, cfg.RemoteIncrURL, cfg.IncrHistoryPath, startBlock) + if err = downloader.RunConcurrent(); err != nil { + log.Error("Failed to download and merge incremental snapshot", "error", err) + return nil, err + } + log.Info("Download and merge incr snapshots successfully") + triedb.SetStateGenerator() + } // Write the supplied genesis to the database if it has not been initialized // yet. The corresponding chain config will be returned, either from the @@ -1222,6 +1257,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha rawdb.DeleteReceipts(db, hash, num) rawdb.DeleteTd(db, hash, num) rawdb.DeleteBlobSidecars(db, hash, num) + rawdb.DeleteBAL(db, hash, num) } // Todo(rjl493456442) txlookup, log index, etc } @@ -1736,6 +1772,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ if bc.chainConfig.IsCancun(block.Number(), block.Time()) { rawdb.WriteBlobSidecars(batch, block.Hash(), block.NumberU64(), block.Sidecars()) } + rawdb.WriteBAL(batch, block.Hash(), block.NumberU64(), block.BAL()) // Write everything belongs to the blocks into the database. So that // we can ensure all components of body is completed(body, receipts) @@ -1814,6 +1851,7 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e if bc.chainConfig.IsCancun(block.Number(), block.Time()) { rawdb.WriteBlobSidecars(blockBatch, block.Hash(), block.NumberU64(), block.Sidecars()) } + rawdb.WriteBAL(blockBatch, block.Hash(), block.NumberU64(), block.BAL()) if err := blockBatch.Write(); err != nil { log.Crit("Failed to write block into disk", "err", err) } @@ -1860,6 +1898,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. if bc.chainConfig.IsCancun(block.Number(), block.Time()) { rawdb.WriteBlobSidecars(blockBatch, block.Hash(), block.NumberU64(), block.Sidecars()) } + rawdb.WriteBAL(blockBatch, block.Hash(), block.NumberU64(), block.BAL()) if bc.db.HasSeparateStateStore() { rawdb.WritePreimages(bc.db.GetStateStore(), statedb.Preimages()) } else { @@ -2009,7 +2048,12 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types bc.futureBlocks.Remove(block.Hash()) if status == CanonStatTy { - bc.chainFeed.Send(ChainEvent{Header: block.Header()}) + bc.chainFeed.Send(ChainEvent{ + Header: block.Header(), + Receipts: receipts, + Transactions: block.Transactions(), + }) + if len(logs) > 0 { bc.logsFeed.Send(logs) } @@ -2415,7 +2459,7 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s defer interrupt.Store(true) // terminate the prefetch at the end needBadSharedStorage := bc.chainConfig.NeedBadSharedStorage(block.Number()) - needPrefetch := needBadSharedStorage || (!bc.cfg.NoPrefetch && len(block.Transactions()) >= prefetchTxNumber) + needPrefetch := needBadSharedStorage || (!bc.cfg.NoPrefetch && len(block.Transactions()) >= prefetchTxNumber) || block.BAL() != nil if !needPrefetch { statedb, err = state.New(parentRoot, bc.statedb) if err != nil { @@ -2453,11 +2497,17 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s storageCacheMissMeter.Mark(stats.StorageMiss) }() + interruptChan := make(chan struct{}) + defer close(interruptChan) go func(start time.Time, throwaway *state.StateDB, block *types.Block) { // Disable tracing for prefetcher executions. vmCfg := bc.cfg.VmConfig vmCfg.Tracer = nil - bc.prefetcher.Prefetch(block.Transactions(), block.Header(), block.GasLimit(), throwaway, vmCfg, &interrupt) + if block.BAL() != nil { + bc.prefetcher.PrefetchBAL(block, throwaway, interruptChan) + } else { + bc.prefetcher.Prefetch(block.Transactions(), block.Header(), block.GasLimit(), throwaway, vmCfg, &interrupt) + } blockPrefetchExecuteTimer.Update(time.Since(start)) if interrupt.Load() { @@ -2800,6 +2850,13 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co // collectLogs collects the logs that were generated or removed during the // processing of a block. These logs are later announced as deleted or reborn. func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { + _, logs := bc.collectReceiptsAndLogs(b, removed) + return logs +} + +// collectReceiptsAndLogs retrieves receipts from the database and returns both receipts and logs. +// This avoids duplicate database reads when both are needed. +func (bc *BlockChain) collectReceiptsAndLogs(b *types.Block, removed bool) ([]*types.Receipt, []*types.Log) { var blobGasPrice *big.Int if b.ExcessBlobGas() != nil { blobGasPrice = eip4844.CalcBlobFee(bc.chainConfig, b.Header()) @@ -2817,7 +2874,7 @@ func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log { logs = append(logs, log) } } - return logs + return receipts, logs } // reorg takes two blocks, an old chain and a new chain and will reconstruct the @@ -3052,8 +3109,12 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) { bc.writeHeadBlock(head) // Emit events - logs := bc.collectLogs(head, false) - bc.chainFeed.Send(ChainEvent{Header: head.Header()}) + receipts, logs := bc.collectReceiptsAndLogs(head, false) + bc.chainFeed.Send(ChainEvent{ + Header: head.Header(), + Receipts: receipts, + Transactions: head.Transactions(), + }) if len(logs) > 0 { bc.logsFeed.Send(logs) } diff --git a/core/blockchain_insert.go b/core/blockchain_insert.go index cf5a4977ce..1c9ba3e991 100644 --- a/core/blockchain_insert.go +++ b/core/blockchain_insert.go @@ -62,7 +62,7 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn context := []interface{}{ "number", end.Number(), "hash", end.Hash(), "miner", end.Coinbase(), "blocks", st.processed, "txs", txs, "blobs", blobs, "mgas", float64(st.usedGas) / 1000000, - "elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps, + "elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps, "BAL", end.BAL() != nil, } blockInsertMgaspsGauge.Update(int64(mgasps)) if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute { diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index f739100af4..e6e488c468 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -203,6 +203,11 @@ func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { } sidecars := rawdb.ReadBlobSidecars(bc.db, hash, number) block = block.WithSidecars(sidecars) + + bal := rawdb.ReadBAL(bc.db, hash, number) + if bal != nil { + block = block.WithBAL(bal) + } // Cache the found block for next time and return bc.blockCache.Add(block.Hash(), block) return block @@ -476,6 +481,34 @@ func (bc *BlockChain) State() (*state.StateDB, error) { // StateAt returns a new mutable state based on a particular point in time. func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { stateDb, err := state.New(root, bc.statedb) + if bc.cfg.EnableBAL { + stateDb.InitBlockAccessList() + } + if err != nil { + return nil, err + } + + // If there's no trie and the specified snapshot is not available, getting + // any state will by default return nil. + // Instead of that, it will be more useful to return an error to indicate + // the state is not available. + if stateDb.NoTries() && stateDb.GetSnap() == nil { + return nil, errors.New("state is not available") + } + + return stateDb, nil +} + +// StateWithCacheAt returns a new mutable state with cache based on a particular point in time. +func (bc *BlockChain) StateWithCacheAt(root common.Hash) (*state.StateDB, error) { + _, process, err := bc.statedb.ReadersWithCacheStats(root) + if err != nil { + return nil, err + } + stateDb, err := state.NewWithReader(root, bc.statedb, process) + if bc.cfg.EnableBAL { + stateDb.InitBlockAccessList() + } if err != nil { return nil, err } @@ -488,7 +521,7 @@ func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { return nil, errors.New("state is not available") } - return stateDb, err + return stateDb, nil } // HistoricState returns a historic state specified by the given root. diff --git a/core/data_availability.go b/core/data_availability.go index 6ec552d7a7..1f60efffa3 100644 --- a/core/data_availability.go +++ b/core/data_availability.go @@ -60,7 +60,7 @@ func IsDataAvailable(chain consensus.ChainHeaderReader, block *types.Block) (err // refer logic in ValidateBody if !chain.Config().IsCancun(block.Number(), block.Time()) { - if block.Sidecars() != nil { + if len(block.Sidecars()) != 0 { return errors.New("sidecars present in block body before cancun") } return nil diff --git a/core/events.go b/core/events.go index 3fc714822c..5b1b65750b 100644 --- a/core/events.go +++ b/core/events.go @@ -42,7 +42,9 @@ type NewVoteEvent struct{ Vote *types.VoteEnvelope } type FinalizedHeaderEvent struct{ Header *types.Header } type ChainEvent struct { - Header *types.Header + Header *types.Header + Receipts []*types.Receipt + Transactions []*types.Transaction } type ChainHeadEvent struct { diff --git a/core/incr_downloader.go b/core/incr_downloader.go new file mode 100644 index 0000000000..a55021c074 --- /dev/null +++ b/core/incr_downloader.go @@ -0,0 +1,1517 @@ +package core + +import ( + "archive/tar" + "context" + "crypto/md5" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/triedb" + "github.com/pierrec/lz4/v4" +) + +const ( + incrSnapshotNamePattern = `(.*)-incr-(\d+)-(\d+)\.tar\.lz4` + maxRetries = 5 + baseDelay = time.Second +) + +// Database keys for download status +var ( + incrDownloadedFilesKey = []byte("incr_downloaded_files") + incrToDownloadFilesKey = []byte("incr_to_download_files") + incrMergedFilesKey = []byte("incr_merged_files") + incrToMergeFilesKey = []byte("incr_to_merge_files") +) + +// metadata file contains many IncrMetadata, array +type IncrMetadata struct { + FileName string `json:"file_name"` + MD5Sum string `json:"md5_sum"` + Size uint64 `json:"size"` +} + +// IncrFileInfo represents parsed incremental file information +type IncrFileInfo struct { + Metadata IncrMetadata + StartBlock uint64 + EndBlock uint64 + LocalPath string + Downloaded bool + Verified bool + Extracted bool + Merged bool +} + +// DownloadProgress represents download progress for a file +type DownloadProgress struct { + FileName string + TotalSize uint64 + DownloadedSize uint64 + Progress float64 + Speed string + Status string + Error error +} + +// IncrDownloader handles incremental snapshot downloading and merging +type IncrDownloader struct { + db ethdb.Database + triedb *triedb.Database + remoteURL string + incrPath string + localBlockNum uint64 + + // Download management + downloadChan chan *IncrFileInfo + progressChan chan *DownloadProgress + errorChan chan error + + // State management + files []*IncrFileInfo + downloadWG sync.WaitGroup + mergeWG sync.WaitGroup + ctx context.Context + cancel context.CancelFunc + + // Statistics + totalFiles int + downloadedFiles int + mergedFiles int + mu sync.RWMutex + + // Merge order control + expectedNextBlockStart uint64 // Next expected start block for merge + downloadedFilesMap map[uint64]*IncrFileInfo // Downloaded files ready for merge, key is StartBlock + mergeMutex sync.Mutex // Protects merge-related state +} + +// NewIncrDownloader creates a new incremental downloader +func NewIncrDownloader(db ethdb.Database, triedb *triedb.Database, remoteURL, incrPath string, localBlockNum uint64) *IncrDownloader { + ctx, cancel := context.WithCancel(context.Background()) + // we don't validate the url and assume it's valid + newURL := strings.TrimSuffix(remoteURL, "/") + + downloader := &IncrDownloader{ + db: db, + triedb: triedb, + remoteURL: newURL, + incrPath: incrPath, + localBlockNum: localBlockNum, + downloadChan: make(chan *IncrFileInfo, 100), + progressChan: make(chan *DownloadProgress, 100), + errorChan: make(chan error, 10), + ctx: ctx, + cancel: cancel, + downloadedFilesMap: make(map[uint64]*IncrFileInfo), + } + + return downloader +} + +// saveDownloadedFiles saves list of downloaded files to db +func (d *IncrDownloader) saveDownloadedFiles(files []string) error { + data, err := json.Marshal(files) + if err != nil { + return fmt.Errorf("failed to marshal downloaded files: %v", err) + } + return d.db.Put(incrDownloadedFilesKey, data) +} + +// loadDownloadedFiles loads list of downloaded files from db +func (d *IncrDownloader) loadDownloadedFiles() ([]string, error) { + data, err := d.db.Get(incrDownloadedFilesKey) + if err != nil { + return nil, err + } + + var files []string + if err = json.Unmarshal(data, &files); err != nil { + return nil, fmt.Errorf("failed to unmarshal downloaded files: %v", err) + } + return files, nil +} + +// saveToDownloadFiles saves list of currently to download files to db +func (d *IncrDownloader) saveToDownloadFiles(files []string) error { + data, err := json.Marshal(files) + if err != nil { + return fmt.Errorf("failed to marshal to download files: %v", err) + } + return d.db.Put(incrToDownloadFilesKey, data) +} + +// loadToDownloadFiles loads list of currently to download files from db +func (d *IncrDownloader) loadToDownloadFiles() ([]string, error) { + data, err := d.db.Get(incrToDownloadFilesKey) + if err != nil { + return nil, err + } + + var files []string + if err = json.Unmarshal(data, &files); err != nil { + return nil, fmt.Errorf("failed to unmarshal to download files: %v", err) + } + return files, nil +} + +// saveDownloadedFiles saves list of downloaded files to db +func (d *IncrDownloader) saveMergedFiles(files []string) error { + data, err := json.Marshal(files) + if err != nil { + return fmt.Errorf("failed to marshal merged files: %v", err) + } + return d.db.Put(incrMergedFilesKey, data) +} + +// loadMergedFiles loads list of merged files from db +func (d *IncrDownloader) loadMergedFiles() ([]string, error) { + data, err := d.db.Get(incrMergedFilesKey) + if err != nil { + return nil, err + } + + var files []string + if err = json.Unmarshal(data, &files); err != nil { + return nil, fmt.Errorf("failed to unmarshal merged files: %v", err) + } + return files, nil +} + +// saveToMergeFiles saves list of currently to merge files to db +func (d *IncrDownloader) saveToMergeFiles(files []string) error { + data, err := json.Marshal(files) + if err != nil { + return fmt.Errorf("failed to marshal to merge files: %v", err) + } + return d.db.Put(incrToMergeFilesKey, data) +} + +// loadToMergeFiles loads list of currently to merge files from db +func (d *IncrDownloader) loadToMergeFiles() ([]string, error) { + data, err := d.db.Get(incrToMergeFilesKey) + if err != nil { + return nil, err + } + + var files []string + if err = json.Unmarshal(data, &files); err != nil { + return nil, fmt.Errorf("failed to unmarshal to merge files: %v", err) + } + return files, nil +} + +// Stage 1: Prepare - fetch metadata and validate +func (d *IncrDownloader) Prepare() error { + log.Info("Starting preparation phase", "remoteURL", d.remoteURL, "localBlockNum", d.localBlockNum) + + // Download metadata file + metadata, err := d.fetchMetadata() + if err != nil { + log.Error("Failed to fetch metadata", "error", err) + return err + } + + // Parse and filter file info + files, err := d.parseFileInfo(metadata) + if err != nil { + log.Error("Failed to parse file info", "error", err) + return err + } + + // Process file status and categorize files + if err = d.processFileStatus(files); err != nil { + return err + } + return nil +} + +// processFileStatus processes file status and categorizes files for download and merge +// toDownload -> downloaded -> toMerge -> merged +func (d *IncrDownloader) processFileStatus(files []*IncrFileInfo) error { + // Load existing file status from database + downloadedFiles, err := d.loadDownloadedFiles() + if err != nil { + log.Warn("Failed to load downloaded files list, starting fresh") + downloadedFiles = []string{} + } + + mergedFiles, err := d.loadMergedFiles() + if err != nil { + log.Warn("Failed to load merged files list, starting fresh") + mergedFiles = []string{} + } + + toDownloadFiles, err := d.loadToDownloadFiles() + if err != nil { + log.Warn("Failed to load to download files list, starting fresh") + toDownloadFiles = []string{} + } + + toMergeFiles, err := d.loadToMergeFiles() + if err != nil { + log.Warn("Failed to load to merge files list, starting fresh") + toMergeFiles = []string{} + } + + // Create sets for quick lookup + downloadedSet := make(map[string]bool) + for _, file := range downloadedFiles { + downloadedSet[file] = true + } + + mergedSet := make(map[string]bool) + for _, file := range mergedFiles { + mergedSet[file] = true + } + + toDownloadSet := make(map[string]bool) + for _, file := range toDownloadFiles { + toDownloadSet[file] = true + } + + toMergeSet := make(map[string]bool) + for _, file := range toMergeFiles { + toMergeSet[file] = true + } + + // Filter and categorize files based on their current status + var newToDownloadFiles []*IncrFileInfo + var newToDownloadFileNames []string + var newToMergeFiles []*IncrFileInfo + var newToMergeFileNames []string + + for _, file := range files { + fileName := file.Metadata.FileName + + // Skip already merged files + if mergedSet[fileName] { + log.Debug("Skipping already merged file", "fileName", fileName) + continue + } + + // Check if file is already downloaded + if downloadedSet[fileName] { + log.Debug("File already downloaded, checking merge status", "fileName", fileName) + d.downloadedFiles++ + + // If downloaded but not merged, add to merge queue + if !mergedSet[fileName] { + if !toMergeSet[fileName] { + newToMergeFiles = append(newToMergeFiles, file) + newToMergeFileNames = append(newToMergeFileNames, fileName) + log.Debug("Adding downloaded file to merge queue", "fileName", fileName) + } + } + continue + } + + // Check if file is currently being downloaded + if toDownloadSet[fileName] { + log.Debug("File is currently being downloaded, keeping in download queue", "fileName", fileName) + newToDownloadFiles = append(newToDownloadFiles, file) + newToDownloadFileNames = append(newToDownloadFileNames, fileName) + continue + } + + // Check if file is currently being merged + if toMergeSet[fileName] { + log.Debug("File is currently being merged, keeping in merge queue", "fileName", fileName) + newToMergeFiles = append(newToMergeFiles, file) + newToMergeFileNames = append(newToMergeFileNames, fileName) + continue + } + + // New file to download + log.Debug("Adding new file to download queue", "fileName", fileName) + newToDownloadFiles = append(newToDownloadFiles, file) + newToDownloadFileNames = append(newToDownloadFileNames, fileName) + } + + // Update file lists + d.files = newToDownloadFiles + d.totalFiles = len(d.files) + + if err = d.saveToDownloadFiles(newToDownloadFileNames); err != nil { + log.Error("Failed to save to download files", "error", err) + return err + } + + if err = d.saveToMergeFiles(newToMergeFileNames); err != nil { + log.Error("Failed to save to merge files", "error", err) + return err + } + + // Initialize downloaded files map with files that are ready for merge + for _, file := range newToMergeFiles { + d.downloadedFilesMap[file.StartBlock] = file + log.Debug("Added file to downloaded files map for merge", "fileName", file.Metadata.FileName, "startBlock", file.StartBlock) + } + + // Initialize expected next block start for merge ordering + if len(newToMergeFiles) > 0 { + // Find the earliest start block among files ready for merge + earliestBlock := newToMergeFiles[0].StartBlock + for _, file := range newToMergeFiles { + if file.StartBlock < earliestBlock { + earliestBlock = file.StartBlock + } + } + d.expectedNextBlockStart = earliestBlock + log.Info("Initialized expected next block start from existing merge queue", "expectedNextBlockStart", d.expectedNextBlockStart) + } else if len(d.files) > 0 { + d.expectedNextBlockStart = d.files[0].StartBlock + log.Info("Initialized expected next block start from new files", "expectedNextBlockStart", d.expectedNextBlockStart) + } + + log.Info("Preparation completed", "totalFiles", d.totalFiles, "downloadedFiles", len(downloadedFiles), + "mergedFiles", len(mergedFiles), "toDownloadFiles", len(newToDownloadFileNames), + "toMergeFiles", len(newToMergeFileNames), "firstBlock", d.getFirstBlockNum(), "lastBlock", d.getLastBlockNum()) + return nil +} + +// Download incremental files +func (d *IncrDownloader) Download() error { + log.Info("Starting download phase", "totalFiles", d.totalFiles) + + // Create download directory + if err := os.MkdirAll(d.incrPath, 0755); err != nil { + return fmt.Errorf("failed to create download directory: %v", err) + } + + // Start download workers + numWorkers := 4 + for i := 0; i < numWorkers; i++ { + d.downloadWG.Add(1) + go d.downloadWorker() + } + + // Start progress monitor + go d.progressMonitor() + + // Queue files for download + for _, file := range d.files { + select { + case d.downloadChan <- file: + case <-d.ctx.Done(): + return d.ctx.Err() + } + } + close(d.downloadChan) + + // Wait for all downloads to complete + d.downloadWG.Wait() + + log.Info("Download phase completed", "downloadedFiles", d.downloadedFiles) + return nil +} + +// RunConcurrent executes download and merge concurrently +func (d *IncrDownloader) RunConcurrent() error { + if err := d.Prepare(); err != nil { + return err + } + + // Start merge worker + d.mergeWG.Add(1) + go d.mergeWorker() + + // Start download and merge concurrently + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + if err := d.Download(); err != nil { + log.Error("Download failed", "error", err) + d.errorChan <- err + } + log.Debug("Download goroutine completed") + }() + + wg.Wait() + log.Info("All downloads completed, waiting for merge to complete") + + // Wait for merge worker to complete + d.mergeWG.Wait() + log.Info("All merges completed") + + return nil +} + +// fetchMetadata downloads and parses metadata file +func (d *IncrDownloader) fetchMetadata() ([]IncrMetadata, error) { + resp, err := http.Get(fmt.Sprintf("%s/incr_metadata.json", d.remoteURL)) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP error: %d", resp.StatusCode) + } + + var metadata []IncrMetadata + if err = json.NewDecoder(resp.Body).Decode(&metadata); err != nil { + return nil, err + } + log.Info("Metadata fetched", "metadata", metadata) + + return metadata, nil +} + +// parseFileInfo parses file names to extract block information +func (d *IncrDownloader) parseFileInfo(metadata []IncrMetadata) ([]*IncrFileInfo, error) { + if len(metadata) == 0 { + return nil, fmt.Errorf("no metadata found") + } + + pattern := regexp.MustCompile(incrSnapshotNamePattern) + + var ( + files []*IncrFileInfo + filteredFiles []*IncrFileInfo + ) + + // Parse all files from metadata and separate by format + for _, meta := range metadata { + if matches := pattern.FindStringSubmatch(meta.FileName); len(matches) == 4 { + startBlock, err := strconv.ParseUint(matches[2], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid start block in %s: %v", meta.FileName, err) + } + endBlock, err := strconv.ParseUint(matches[3], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid end block in %s: %v", meta.FileName, err) + } + + files = append(files, &IncrFileInfo{ + Metadata: meta, + StartBlock: startBlock, + EndBlock: endBlock, + LocalPath: filepath.Join(d.incrPath, meta.FileName), + }) + } else { + log.Warn("Invalid file name format", "fileName", meta.FileName) + continue + } + } + + // Sort files by end block in ascending order + sort.Slice(files, func(i, j int) bool { + return files[i].EndBlock < files[j].EndBlock + }) + // Check continuity of all files before filtering + if err := d.checkFileContinuity(files); err != nil { + return nil, err + } + // filter the block number that matches local data + for index, file := range files { + // Check if local block number falls within this file's range + if file.StartBlock <= d.localBlockNum && d.localBlockNum <= file.EndBlock { + // Found the file containing local block number, add all remaining files from this point + filteredFiles = append(filteredFiles, files[index:]...) + break + } + } + + if len(filteredFiles) == 0 { + return nil, fmt.Errorf("remote incr snapshots don't match local data: %d", d.localBlockNum) + } + + log.Info("Filtered incremental files", "totalFiles", len(files), "keptFiles", len(filteredFiles), + "localBlockNum", d.localBlockNum) + return filteredFiles, nil +} + +// checkFileContinuity checks if all files have continuous block ranges +// startBlock = previousEndBlock + 1 (except for the first file) +func (d *IncrDownloader) checkFileContinuity(files []*IncrFileInfo) error { + if len(files) == 0 { + return nil + } + + // For the first file, we don't check continuity since we don't know the previous end block + // We'll check from the second file onwards + for i := 1; i < len(files); i++ { + prevFile := files[i-1] + currFile := files[i] + + expectedStartBlock := prevFile.EndBlock + 1 + if currFile.StartBlock != expectedStartBlock { + return fmt.Errorf("file continuity broken: file %s ends at %d, but file %s starts at %d (expected %d)", + prevFile.Metadata.FileName, prevFile.EndBlock, + currFile.Metadata.FileName, currFile.StartBlock, expectedStartBlock) + } + } + + log.Info("File continuity check completed successfully", "totalFiles", len(files)) + return nil +} + +// downloadWorker handles file downloads +func (d *IncrDownloader) downloadWorker() { + defer d.downloadWG.Done() + + for file := range d.downloadChan { + // Mark file as to download + d.markFileAsToDownload(file.Metadata.FileName) + + if err := d.downloadFile(file); err != nil { + log.Error("Failed to download file", "file", file.Metadata.FileName, "error", err) + d.errorChan <- err + continue + } + + if err := d.verifyAndExtract(file); err != nil { + log.Error("Failed to verify or extract failed", "file", file.Metadata.FileName, "error", err) + d.errorChan <- err + continue + } + + log.Info("Finished downloading and verifying file", "file", file.Metadata.FileName) + // Mark file as downloaded + d.markFileAsDownloaded(file.Metadata.FileName) + + d.mu.Lock() + d.downloadedFiles++ + d.mu.Unlock() + + log.Debug("File completed, queuing for merge", "file", file.Metadata.FileName, "downloadedFiles", d.downloadedFiles) + d.queueForMerge(file) + } +} + +// markFileAsToDownload marks a file as currently to download +func (d *IncrDownloader) markFileAsToDownload(fileName string) { + d.mu.Lock() + defer d.mu.Unlock() + + downloadingFiles, _ := d.loadToDownloadFiles() + if downloadingFiles == nil { + downloadingFiles = []string{} + } + + // Check if already in list + found := false + for _, file := range downloadingFiles { + if file == fileName { + found = true + break + } + } + + if !found { + downloadingFiles = append(downloadingFiles, fileName) + d.saveToDownloadFiles(downloadingFiles) + log.Debug("Marked file as downloading", "fileName", fileName) + } +} + +// removeFromToDownload removes a file from to download list +func (d *IncrDownloader) removeFromToDownload(fileName string) { + toDownloadFiles, err := d.loadToDownloadFiles() + if err != nil { + log.Error("Failed to load to download files", "error", err) + } + + if toDownloadFiles != nil { + var newDownloadingFiles []string + for _, file := range toDownloadFiles { + if file != fileName { + newDownloadingFiles = append(newDownloadingFiles, file) + } + } + d.saveToDownloadFiles(newDownloadingFiles) + log.Debug("Removed file from to download list", "fileName", fileName) + } +} + +// markFileAsDownloaded marks a file as downloaded and removes from downloading list +func (d *IncrDownloader) markFileAsDownloaded(fileName string) { + d.mu.Lock() + defer d.mu.Unlock() + + // Add to downloaded files + downloadedFiles, _ := d.loadDownloadedFiles() + if downloadedFiles == nil { + downloadedFiles = []string{} + } + // Check if already in list + found := false + for _, file := range downloadedFiles { + if file == fileName { + found = true + break + } + } + + if !found { + downloadedFiles = append(downloadedFiles, fileName) + d.saveDownloadedFiles(downloadedFiles) + log.Debug("Marked file as downloaded", "fileName", fileName) + } + + // Remove from to download files + d.removeFromToDownload(fileName) +} + +// markFileAsToMerge marks a file as currently to merge +func (d *IncrDownloader) markFileAsToMerge(fileName string) { + d.mu.Lock() + defer d.mu.Unlock() + + toMergeFiles, _ := d.loadToMergeFiles() + if toMergeFiles == nil { + toMergeFiles = []string{} + } + + // Check if already in list + found := false + for _, file := range toMergeFiles { + if file == fileName { + found = true + break + } + } + + if !found { + toMergeFiles = append(toMergeFiles, fileName) + d.saveToMergeFiles(toMergeFiles) + log.Debug("Marked file as to merge", "fileName", fileName) + } +} + +// removeFromToMerge removes a file from to merge list +func (d *IncrDownloader) removeFromToMerge(fileName string) { + toMergeFiles, err := d.loadToMergeFiles() + if err != nil { + log.Error("Failed to load to merge files", "error", err) + return + } + + if toMergeFiles != nil { + var newToMergeFiles []string + for _, file := range toMergeFiles { + if file != fileName { + newToMergeFiles = append(newToMergeFiles, file) + } + } + d.saveToMergeFiles(newToMergeFiles) + log.Debug("Removed file from to merge list", "fileName", fileName) + } +} + +// markFileAsMerged marks a file as merged and removes from to merge list +func (d *IncrDownloader) markFileAsMerged(fileName string) { + d.mu.Lock() + defer d.mu.Unlock() + + // Add to merged files + mergedFiles, _ := d.loadMergedFiles() + if mergedFiles == nil { + mergedFiles = []string{} + } + // Check if already in list + found := false + for _, file := range mergedFiles { + if file == fileName { + found = true + break + } + } + + if !found { + mergedFiles = append(mergedFiles, fileName) + d.saveMergedFiles(mergedFiles) + log.Debug("Marked file as merged", "fileName", fileName) + } + + // Remove from to merge files + d.removeFromToMerge(fileName) +} + +// queueForMerge queues a file for merge (non-blocking) +func (d *IncrDownloader) queueForMerge(file *IncrFileInfo) { + d.mergeMutex.Lock() + defer d.mergeMutex.Unlock() + + // Check if file is already in downloaded files map + if existingFile, exists := d.downloadedFilesMap[file.StartBlock]; exists { + if existingFile.Metadata.FileName == file.Metadata.FileName { + log.Debug("File already in downloaded files map, skipping", "file", file.Metadata.FileName, "startBlock", file.StartBlock) + return + } + } + + // Check if file has already been merged + if file.Merged { + log.Debug("File already merged, skipping queue", "file", file.Metadata.FileName, "startBlock", file.StartBlock) + return + } + + // Add to downloaded files map + d.downloadedFilesMap[file.StartBlock] = file + + // Mark file as to merge in database + d.markFileAsToMerge(file.Metadata.FileName) + + log.Debug("File queued for merge", "file", file.Metadata.FileName, "startBlock", file.StartBlock) +} + +// downloadFile downloads a single file with retry mechanism +func (d *IncrDownloader) downloadFile(file *IncrFileInfo) error { + for attempt := 1; attempt <= maxRetries; attempt++ { + err := d.downloadWithHTTP(file) + if err == nil { + return nil + } + + // Log the error for this attempt + log.Warn("Download attempt failed", "file", file.Metadata.FileName, "attempt", attempt, + "maxRetries", maxRetries, "error", err) + + // If this is the last attempt, return the error + if attempt == maxRetries { + return fmt.Errorf("download failed after %d attempts, last error: %w", maxRetries, err) + } + + // Calculate delay with exponential backoff + delay := baseDelay * time.Duration(attempt) + log.Info("Retrying download", "file", file.Metadata.FileName, "attempt", attempt+1, "delay", delay) + + // Wait before retrying + select { + case <-d.ctx.Done(): + return d.ctx.Err() + case <-time.After(delay): + continue + } + } + + return fmt.Errorf("failed to download file after %d attempts", maxRetries) +} + +// ChunkInfo represents a download chunk +type ChunkInfo struct { + Index int + Start int64 + End int64 + Downloaded int64 + TempFile string + Completed bool +} + +// ChunkProgress represents progress of a chunk download +type ChunkProgress struct { + ChunkIndex int + Downloaded int64 + Total int64 + FileName string +} + +// downloadWithHTTP downloads file using concurrent HTTP requests +func (d *IncrDownloader) downloadWithHTTP(file *IncrFileInfo) error { + url := fmt.Sprintf("%s/%s", d.remoteURL, file.Metadata.FileName) + log.Info("Start downloading incremental snapshot", "url", url) + + // Check if file already exists and has correct size + if info, err := os.Stat(file.LocalPath); err == nil { + if uint64(info.Size()) == file.Metadata.Size { + log.Info("File already exists with correct size, skipping download", "file", file.Metadata.FileName) + return nil + } + } + + // Check if server supports range requests + supportsRange, contentLength, err := d.checkRangeSupport(url) + if err != nil { + return fmt.Errorf("failed to check range support: %v", err) + } + + if !supportsRange { + log.Info("Server doesn't support range requests, using single-threaded download", "file", file.Metadata.FileName) + return d.downloadSingleThreaded(url, file) + } + + // Use expected size from metadata, fallback to content-length + totalSize := file.Metadata.Size + if totalSize == 0 { + totalSize = uint64(contentLength) + } + + // Calculate chunk size and number of chunks + numChunks := 8 + chunkSize := int64(totalSize) / int64(numChunks) + if chunkSize < 1024*1024 { // Minimum 1MB per chunk + chunkSize = 1024 * 1024 + numChunks = int(int64(totalSize) / chunkSize) + if numChunks < 1 { + numChunks = 1 + } + } + + // Create chunk info + chunks := make([]*ChunkInfo, numChunks) + for i := 0; i < numChunks; i++ { + start := int64(i) * chunkSize + end := start + chunkSize - 1 + if i == numChunks-1 { + end = int64(totalSize) - 1 + } + + chunks[i] = &ChunkInfo{ + Index: i, + Start: start, + End: end, + TempFile: fmt.Sprintf("%s.part%d", file.LocalPath, i), + } + } + + // Check for existing partial downloads + d.checkExistingChunks(chunks) + + // Download chunks concurrently + var wg sync.WaitGroup + progressChan := make(chan *ChunkProgress, numChunks) + errorChan := make(chan error, numChunks) + + // Download each chunk + for _, chunk := range chunks { + if chunk.Completed { + continue // Skip already completed chunks + } + + wg.Add(1) + go func(chunk *ChunkInfo) { + defer wg.Done() + if err = d.downloadChunk(url, chunk, progressChan); err != nil { + errorChan <- fmt.Errorf("failed to download chunk %d: %v", chunk.Index, err) + } + }(chunk) + } + + wg.Wait() + close(progressChan) + + // Check for errors + select { + case err = <-errorChan: + return err + default: + } + + // Merge chunks + log.Debug("Merging chunks", "file", file.Metadata.FileName, "chunks", numChunks) + if err = d.mergeChunks(chunks, file.LocalPath); err != nil { + return fmt.Errorf("failed to merge chunks: %v", err) + } + + // Clean up temporary files + d.cleanupTempFiles(chunks) + + // Verify final file size + if info, err := os.Stat(file.LocalPath); err != nil { + return fmt.Errorf("downloaded file not found: %v", err) + } else if uint64(info.Size()) != totalSize { + return fmt.Errorf("downloaded file size mismatch: expected %d, got %d", totalSize, info.Size()) + } + + log.Debug("Download completed successfully", "file", file.Metadata.FileName, "size", totalSize) + return nil +} + +// verifyAndExtract verifies MD5 hash and extracts file with retry mechanism +func (d *IncrDownloader) verifyAndExtract(file *IncrFileInfo) error { + for attempt := 1; attempt <= maxRetries; attempt++ { + // Verify MD5 + if err := d.verifyHash(file); err != nil { + log.Warn("Hash verification attempt failed", "file", file.Metadata.FileName, "attempt", attempt, + "maxRetries", maxRetries, "error", err) + + if attempt == maxRetries { + return fmt.Errorf("hash verification failed after %d attempts, last error: %w", maxRetries, err) + } + + // Wait before retrying + delay := baseDelay * time.Duration(attempt) + select { + case <-d.ctx.Done(): + return d.ctx.Err() + case <-time.After(delay): + continue + } + } + file.Verified = true + break + } + + // Extract file with retry mechanism + for attempt := 1; attempt <= maxRetries; attempt++ { + if err := d.extractFile(file); err != nil { + log.Warn("Failed to extract file", "file", file.Metadata.FileName, "attempt", attempt, + "maxRetries", maxRetries, "error", err) + + if attempt == maxRetries { + return fmt.Errorf("file extraction failed after %d attempts, last error: %w", maxRetries, err) + } + + // Wait before retrying + delay := baseDelay * time.Duration(attempt) + select { + case <-d.ctx.Done(): + return d.ctx.Err() + case <-time.After(delay): + continue + } + } + file.Extracted = true + break + } + + return nil +} + +// verifyHash verifies file MD5 hash +func (d *IncrDownloader) verifyHash(file *IncrFileInfo) error { + f, err := os.Open(file.LocalPath) + if err != nil { + return err + } + defer f.Close() + + h := md5.New() + if _, err = io.Copy(h, f); err != nil { + return err + } + + actualHash := hex.EncodeToString(h.Sum(nil)) + if actualHash != file.Metadata.MD5Sum { + return fmt.Errorf("hash mismatch for %s: expected %s, got %s", + file.Metadata.FileName, file.Metadata.MD5Sum, actualHash) + } + + log.Debug("Finished verifying md5 hash", "file", file.LocalPath) + return nil +} + +// extractFile extracts tar.lz4 file +func (d *IncrDownloader) extractFile(file *IncrFileInfo) error { + // Extract directory + extractDir := filepath.Join(d.incrPath) + if err := os.MkdirAll(extractDir, 0755); err != nil { + return err + } + log.Debug("Extracting file", "file", file.Metadata.FileName, "extractDir", extractDir) + + // Open the lz4 file + inputFile, err := os.Open(file.LocalPath) + if err != nil { + return fmt.Errorf("failed to open file %s: %v", file.LocalPath, err) + } + defer inputFile.Close() + + // Create lz4 reader + lz4Reader := lz4.NewReader(inputFile) + // Create tar reader from lz4 output + tarReader := tar.NewReader(lz4Reader) + + // Extract tar contents + for { + header, err := tarReader.Next() + if err == io.EOF { + break + } + if err != nil { + return fmt.Errorf("failed to read tar header: %v", err) + } + + // Create the full path for the file + targetPath := filepath.Join(extractDir, header.Name) + + // Ensure the target directory exists + if err = os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %v", filepath.Dir(targetPath), err) + } + + switch header.Typeflag { + case tar.TypeDir: + // Create directory + if err = os.MkdirAll(targetPath, os.FileMode(header.Mode)); err != nil { + return fmt.Errorf("failed to create directory %s: %v", targetPath, err) + } + case tar.TypeReg: + // Create regular file + outFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) + if err != nil { + return fmt.Errorf("failed to create file %s: %v", targetPath, err) + } + + // Copy file content + if _, err = io.Copy(outFile, tarReader); err != nil { + outFile.Close() + return fmt.Errorf("failed to write file %s: %v", targetPath, err) + } + outFile.Close() + case tar.TypeSymlink: + // Create symbolic link + if err = os.Symlink(header.Linkname, targetPath); err != nil { + return fmt.Errorf("failed to create symlink %s: %v", targetPath, err) + } + default: + log.Warn("Unsupported file type in tar", "name", header.Name, "type", header.Typeflag) + } + } + + log.Debug("File extracted successfully", "file", file.Metadata.FileName, "extractDir", extractDir) + return nil +} + +// mergeWorker handles sequential merging of files +func (d *IncrDownloader) mergeWorker() { + defer d.mergeWG.Done() + + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + // Check if there are files ready for merge + d.mergeMutex.Lock() + currFile, exists := d.downloadedFilesMap[d.expectedNextBlockStart] + d.mergeMutex.Unlock() + + if exists { + // Process the next file in sequence + log.Info("Processing file for merge", "file", currFile.Metadata.FileName, + "startBlock", currFile.StartBlock, "endBlock", currFile.EndBlock) + + // Check if file has already been merged + if currFile.Merged { + log.Warn("File already merged, removing from map", "file", currFile.Metadata.FileName) + d.mergeMutex.Lock() + delete(d.downloadedFilesMap, d.expectedNextBlockStart) + d.mergeMutex.Unlock() + d.expectedNextBlockStart = currFile.EndBlock + 1 + continue + } + + // Remove from map before processing to prevent race conditions + d.mergeMutex.Lock() + delete(d.downloadedFilesMap, d.expectedNextBlockStart) + d.mergeMutex.Unlock() + + // Check if this is the last incr snapshot + d.mu.RLock() + isLastSnapshot := (d.mergedFiles + 1) == len(d.files) + d.mu.RUnlock() + + // Perform merge operation + path := filepath.Join(d.incrPath, fmt.Sprintf("incr-%d-%d", currFile.StartBlock, currFile.EndBlock)) + if isLastSnapshot { + log.Info("This is the last incremental snapshot, performing final cleanup and optimization") + complete, err := rawdb.CheckIncrSnapshotComplete(path) + if err != nil { + log.Error("Failed to check last incr snapshot complete", "err", err) + d.errorChan <- err + return + } + if !complete { + log.Warn("Skip last incr snapshot due to data is incomplete") + return + } + } + if err := MergeIncrSnapshot(d.db, d.triedb, path); err != nil { + log.Error("Failed to merge", "file", currFile.Metadata.FileName, "error", err) + d.errorChan <- err + return + } + + currFile.Merged = true + d.markFileAsMerged(currFile.Metadata.FileName) + + d.mu.Lock() + d.mergedFiles++ + d.mu.Unlock() + + // Update expected next start block + d.expectedNextBlockStart = currFile.EndBlock + 1 + log.Info("File merged successfully", "file", currFile.Metadata.FileName, + "progress", fmt.Sprintf("%d/%d", d.mergedFiles, d.totalFiles), + "startBlock", currFile.StartBlock, "endBlock", currFile.EndBlock) + } else { + // Check if all files have been processed + d.mu.RLock() + downloadedCount := d.downloadedFiles + mergedCount := d.mergedFiles + d.mu.RUnlock() + + if mergedCount == downloadedCount && downloadedCount == len(d.files) { + log.Info("All files processed, merge worker exiting") + return + } + } + + case <-d.ctx.Done(): + log.Info("Context cancelled, merge worker exiting") + return + } + } +} + +// progressMonitor monitors and reports download progress +func (d *IncrDownloader) progressMonitor() { + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for { + select { + case progress := <-d.progressChan: + log.Info("Download progress", "file", progress.FileName, + "progress", fmt.Sprintf("%.1f%%", progress.Progress), + "speed", progress.Speed, "status", progress.Status) + case <-ticker.C: + d.mu.RLock() + stats := d.GetFileStatusStats() + log.Info("Overall progress", "downloaded", d.downloadedFiles, + "merged", d.mergedFiles, "total", d.totalFiles, "stats", stats) + d.mu.RUnlock() + case <-d.ctx.Done(): + return + } + } +} + +// progressReader tracks download progress +type progressReader struct { + reader io.Reader + total uint64 + downloaded uint64 + filename string + progress chan<- *DownloadProgress + lastUpdate time.Time +} + +func (pr *progressReader) Read(p []byte) (int, error) { + n, err := pr.reader.Read(p) + pr.downloaded += uint64(n) + + // Update progress every 30 seconds + if time.Since(pr.lastUpdate) > (time.Second * 30) { + progress := &DownloadProgress{ + FileName: pr.filename, + TotalSize: pr.total, + DownloadedSize: pr.downloaded, + Progress: float64(pr.downloaded) / float64(pr.total) * 100, + Status: "downloading", + } + + select { + case pr.progress <- progress: + default: + } + + pr.lastUpdate = time.Now() + } + + return n, err +} + +// Cancel cancels all operations +func (d *IncrDownloader) Cancel() { + d.cancel() +} + +// Close closes all channels and cleans up +func (d *IncrDownloader) Close() { + d.cancel() + + // Clean up pending merge files + d.mergeMutex.Lock() + d.downloadedFilesMap = make(map[uint64]*IncrFileInfo) + d.mergeMutex.Unlock() + + close(d.progressChan) + close(d.errorChan) +} + +// Helper methods +func (d *IncrDownloader) getFirstBlockNum() uint64 { + if len(d.files) == 0 { + return 0 + } + return d.files[0].StartBlock +} + +func (d *IncrDownloader) getLastBlockNum() uint64 { + if len(d.files) == 0 { + return 0 + } + return d.files[len(d.files)-1].EndBlock +} + +// checkRangeSupport checks if server supports range requests +func (d *IncrDownloader) checkRangeSupport(url string) (bool, int64, error) { + req, err := http.NewRequest("HEAD", url, nil) + if err != nil { + return false, 0, err + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return false, 0, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return false, 0, fmt.Errorf("HTTP error: %d", resp.StatusCode) + } + + acceptRanges := resp.Header.Get("Accept-Ranges") + contentLength := resp.ContentLength + + return acceptRanges == "bytes", contentLength, nil +} + +// downloadSingleThreaded downloads file without range requests +func (d *IncrDownloader) downloadSingleThreaded(url string, file *IncrFileInfo) error { + log.Info("Starting single-threaded download", "file", file.Metadata.FileName) + + client := &http.Client{} + resp, err := client.Get(url) + if err != nil { + return fmt.Errorf("HTTP GET failed: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("HTTP error: %d", resp.StatusCode) + } + + out, err := os.Create(file.LocalPath) + if err != nil { + return fmt.Errorf("failed to create file: %v", err) + } + defer out.Close() + + // Track progress + pr := &progressReader{ + reader: resp.Body, + total: uint64(resp.ContentLength), + filename: file.Metadata.FileName, + progress: d.progressChan, + } + + _, err = io.Copy(out, pr) + return err +} + +// checkExistingChunks checks for existing partial downloads +func (d *IncrDownloader) checkExistingChunks(chunks []*ChunkInfo) { + for _, chunk := range chunks { + if info, err := os.Stat(chunk.TempFile); err == nil { + downloaded := info.Size() + expectedSize := chunk.End - chunk.Start + 1 + + if downloaded == expectedSize { + chunk.Completed = true + chunk.Downloaded = downloaded + log.Debug("Found completed chunk", "chunk", chunk.Index, "size", downloaded) + } else if downloaded > 0 { + chunk.Downloaded = downloaded + // Update start position for resume + chunk.Start += downloaded + log.Debug("Found partial chunk", "chunk", chunk.Index, "downloaded", downloaded, "remaining", expectedSize-downloaded) + } + } + } +} + +// downloadChunk downloads a single chunk with retry mechanism +func (d *IncrDownloader) downloadChunk(url string, chunk *ChunkInfo, progressChan chan<- *ChunkProgress) error { + for attempt := 1; attempt <= maxRetries; attempt++ { + err := d.downloadChunkAttempt(url, chunk, progressChan) + if err == nil { + // Success, no need to retry + return nil + } + + // Log the error for this attempt + log.Warn("Failed to download chunk", "chunk", chunk.Index, "attempt", attempt, + "maxRetries", maxRetries, "error", err) + + // If this is the last attempt, return the error + if attempt == maxRetries { + return fmt.Errorf("failed to download chunk after %d attempts, last error: %w", maxRetries, err) + } + + // Calculate delay with exponential backoff + delay := baseDelay * time.Duration(attempt) + log.Info("Retrying chunk download", "chunk", chunk.Index, "attempt", attempt+1, "delay", delay) + + // Wait before retrying + select { + case <-d.ctx.Done(): + return d.ctx.Err() + case <-time.After(delay): + continue + } + } + + return fmt.Errorf("chunk download failed after %d attempts", maxRetries) +} + +// downloadChunkAttempt performs a single attempt to download a chunk +func (d *IncrDownloader) downloadChunkAttempt(url string, chunk *ChunkInfo, progressChan chan<- *ChunkProgress) error { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + // Set range header + rangeHeader := fmt.Sprintf("bytes=%d-%d", chunk.Start, chunk.End) + req.Header.Set("Range", rangeHeader) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusPartialContent && resp.StatusCode != http.StatusOK { + return fmt.Errorf("HTTP error: %d", resp.StatusCode) + } + + // Open temp file for writing (append mode for resume) + var out *os.File + if chunk.Downloaded > 0 { + out, err = os.OpenFile(chunk.TempFile, os.O_WRONLY|os.O_APPEND, 0644) + } else { + out, err = os.Create(chunk.TempFile) + } + if err != nil { + return err + } + defer out.Close() + + // Track progress + chunkSize := chunk.End - chunk.Start + 1 + downloaded := chunk.Downloaded + + buffer := make([]byte, 32*1024) // 32KB buffer + lastProgress := time.Now() + + for { + select { + case <-d.ctx.Done(): + return d.ctx.Err() + default: + } + + n, err := resp.Body.Read(buffer) + if n > 0 { + if _, writeErr := out.Write(buffer[:n]); writeErr != nil { + log.Error("Failed to write to file", "error", writeErr) + return writeErr + } + downloaded += int64(n) + + // Send progress update every 100ms + if time.Since(lastProgress) > 100*time.Millisecond { + select { + case progressChan <- &ChunkProgress{ + ChunkIndex: chunk.Index, + Downloaded: downloaded, + Total: chunkSize, + FileName: "", + }: + default: + } + lastProgress = time.Now() + } + } + + if err == io.EOF { + break + } + if err != nil { + log.Error("Failed to read from response body", "error", err) + return err + } + } + + chunk.Downloaded = downloaded + chunk.Completed = true + return nil +} + +// mergeChunks merges all chunks into final file +func (d *IncrDownloader) mergeChunks(chunks []*ChunkInfo, outputPath string) error { + output, err := os.Create(outputPath) + if err != nil { + return err + } + defer output.Close() + + for _, chunk := range chunks { + chunkFile, err := os.Open(chunk.TempFile) + if err != nil { + return fmt.Errorf("failed to open chunk %d: %v", chunk.Index, err) + } + + _, err = io.Copy(output, chunkFile) + chunkFile.Close() + + if err != nil { + return fmt.Errorf("failed to copy chunk %d: %v", chunk.Index, err) + } + } + + return nil +} + +// cleanupTempFiles removes temporary chunk files +func (d *IncrDownloader) cleanupTempFiles(chunks []*ChunkInfo) { + for _, chunk := range chunks { + if err := os.Remove(chunk.TempFile); err != nil { + log.Warn("Failed to remove temp file", "file", chunk.TempFile, "error", err) + } + } +} + +// GetFileStatusStats returns current file status statistics +func (d *IncrDownloader) GetFileStatusStats() map[string]interface{} { + d.mu.RLock() + defer d.mu.RUnlock() + + downloadedFiles, _ := d.loadDownloadedFiles() + mergedFiles, _ := d.loadMergedFiles() + toDownloadFiles, _ := d.loadToDownloadFiles() + toMergeFiles, _ := d.loadToMergeFiles() + + return map[string]interface{}{ + "totalFiles": d.totalFiles, + "downloadedFiles": len(downloadedFiles), + "mergedFiles": len(mergedFiles), + "toDownloadFiles": len(toDownloadFiles), + "toMergeFiles": len(toMergeFiles), + "downloadedFilesMap": len(d.downloadedFilesMap), + "expectedNextBlockStart": d.expectedNextBlockStart, + } +} diff --git a/core/incr_downloader_test.go b/core/incr_downloader_test.go new file mode 100644 index 0000000000..ab0b1d2509 --- /dev/null +++ b/core/incr_downloader_test.go @@ -0,0 +1,1374 @@ +package core + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/triedb" +) + +const ( + testURL = "http://test.com" +) + +func createTestDB() ethdb.Database { + return rawdb.NewMemoryDatabase() +} + +// Create test HTTP server +func createTestHTTPServer() (*httptest.Server, string) { + metadata := []IncrMetadata{ + { + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", + Size: 1024, + }, + { + FileName: "test-incr-2000-2999.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", + Size: 2048, + }, + } + + testFileContent := "This is a test file content for incremental snapshot download testing." + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + + switch { + case path == "/incr_metadata.json": + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(metadata) + + case strings.HasSuffix(path, ".tar.lz4"): + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(testFileContent))) + w.Header().Set("Accept-Ranges", "bytes") + w.Write([]byte(testFileContent)) + + default: + http.NotFound(w, r) + } + })) + + return server, server.URL +} + +func TestIncrDownloader_HTTPDownload(t *testing.T) { + server, serverURL := createTestHTTPServer() + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, serverURL, tempDir, 1000) + + // Create test file info + file := &IncrFileInfo{ + Metadata: IncrMetadata{ + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", + Size: 70, + }, + StartBlock: 1000, + EndBlock: 1999, + LocalPath: filepath.Join(tempDir, "test-incr-1000-2000.tar.lz4"), + } + + // Test HTTP download + err := downloader.downloadWithHTTP(file) + require.NoError(t, err) + + // Verify file downloaded + _, err = os.Stat(file.LocalPath) + require.NoError(t, err) + + // Verify file content + content, err := os.ReadFile(file.LocalPath) + require.NoError(t, err) + assert.Contains(t, string(content), "test file content") +} + +func TestIncrDownloader_HTTPDownloadWithRangeSupport(t *testing.T) { + // Create test server with Range support + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + testContent := "This is a test file content for range download testing." + + if strings.HasSuffix(path, ".tar.lz4") { + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(testContent))) + w.Header().Set("Accept-Ranges", "bytes") + + // Handle Range requests + if rangeHeader := r.Header.Get("Range"); rangeHeader != "" { + // Simple Range handling + w.WriteHeader(http.StatusPartialContent) + w.Write([]byte(testContent)) + } else { + w.Write([]byte(testContent)) + } + } else { + http.NotFound(w, r) + } + })) + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, server.URL, tempDir, 1000) + + // Create test file info + file := &IncrFileInfo{ + Metadata: IncrMetadata{ + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", + Size: 55, + }, + StartBlock: 1000, + EndBlock: 1999, + LocalPath: filepath.Join(tempDir, "test-incr-1000-1999.tar.lz4"), + } + + // Test Range-supported download + err := downloader.downloadWithHTTP(file) + require.NoError(t, err) + + // Verify file downloaded + _, err = os.Stat(file.LocalPath) + require.NoError(t, err) +} + +func TestIncrDownloader_HTTPDownloadWithoutRangeSupport(t *testing.T) { + // Create test server without Range support + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + testContent := "This is a test file content for single-threaded download testing." + + if strings.HasSuffix(path, ".tar.lz4") { + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(testContent))) + // Don't set Accept-Ranges header + w.Write([]byte(testContent)) + } else { + http.NotFound(w, r) + } + })) + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, server.URL, tempDir, 1000) + + // Create test file info + file := &IncrFileInfo{ + Metadata: IncrMetadata{ + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", + Size: 1024, + }, + StartBlock: 1000, + EndBlock: 1999, + LocalPath: filepath.Join(tempDir, "test-incr-1000-1999.tar.lz4"), + } + + // Test single-threaded download + err := downloader.downloadWithHTTP(file) + require.NoError(t, err) + + // Verify file downloaded + _, err = os.Stat(file.LocalPath) + require.NoError(t, err) +} + +func TestIncrDownloader_HTTPDownloadError(t *testing.T) { + // Create test server that returns errors + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + if strings.HasSuffix(path, ".tar.lz4") { + // Return 404 error + http.NotFound(w, r) + } else { + http.NotFound(w, r) + } + })) + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, server.URL, tempDir, 1000) + + // Create test file info + file := &IncrFileInfo{ + Metadata: IncrMetadata{ + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", + Size: 1024, + }, + StartBlock: 1000, + EndBlock: 1999, + LocalPath: filepath.Join(tempDir, "test-incr-1000-1999.tar.lz4"), + } + + // Test download error + err := downloader.downloadWithHTTP(file) + assert.Error(t, err) + assert.Contains(t, err.Error(), "HTTP error: 404") +} + +func TestIncrDownloader_FetchMetadata(t *testing.T) { + // Create test metadata + metadata := []IncrMetadata{ + { + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", + Size: 1024, + }, + { + FileName: "test-incr-2000-2999.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", + Size: 2048, + }, + } + + // Create test HTTP server + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/incr_metadata.json" { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(metadata) + } else { + http.NotFound(w, r) + } + })) + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, server.URL, tempDir, 1000) + + // Test metadata fetching + fetchedMetadata, err := downloader.fetchMetadata() + require.NoError(t, err) + assert.Len(t, fetchedMetadata, 2) + assert.Equal(t, "test-incr-1000-1999.tar.lz4", fetchedMetadata[0].FileName) + assert.Equal(t, uint64(1024), fetchedMetadata[0].Size) +} + +func TestIncrDownloader_FetchMetadataError(t *testing.T) { + // Create test server that returns errors + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + })) + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, server.URL, tempDir, 1000) + + // Test metadata fetching error + _, err := downloader.fetchMetadata() + assert.Error(t, err) + assert.Contains(t, err.Error(), "HTTP error: 500") +} + +func TestIncrDownloader_CheckRangeSupport(t *testing.T) { + // Create test server with Range support + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("Accept-Ranges", "bytes") + w.Header().Set("Content-Length", "1024") + } else { + w.Write([]byte("test content")) + } + })) + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, server.URL, tempDir, 1000) + + // Test Range support check + supportsRange, contentLength, err := downloader.checkRangeSupport(server.URL + "/test-file.tar.lz4") + require.NoError(t, err) + assert.True(t, supportsRange) + assert.Equal(t, int64(1024), contentLength) +} + +func TestIncrDownloader_CheckRangeSupportNotSupported(t *testing.T) { + // Create test server without Range support + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + // Don't set Accept-Ranges header + w.Header().Set("Content-Length", "1024") + } else { + w.Write([]byte("test content")) + } + })) + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, server.URL, tempDir, 1000) + + // Test Range support check + supportsRange, contentLength, err := downloader.checkRangeSupport(server.URL + "/test-file.tar.lz4") + require.NoError(t, err) + assert.False(t, supportsRange) + assert.Equal(t, int64(1024), contentLength) +} + +func TestIncrDownloader_DownloadChunk(t *testing.T) { + // Create test server + testContent := "This is test content for chunk download testing." + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rangeHeader := r.Header.Get("Range") + if rangeHeader != "" { + w.Header().Set("Content-Range", "bytes 0-1023/1024") + w.WriteHeader(http.StatusPartialContent) + } + w.Write([]byte(testContent)) + })) + defer server.Close() + + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, server.URL, tempDir, 1000) + + // Create test chunk + chunk := &ChunkInfo{ + Index: 0, + Start: 0, + End: 1023, + TempFile: filepath.Join(tempDir, "test-chunk.part0"), + } + progressChan := make(chan *ChunkProgress, 1) + + // Test chunk download + err := downloader.downloadChunk(server.URL+"/test-file.tar.lz4", chunk, progressChan) + require.NoError(t, err) + + // Verify chunk file created + _, err = os.Stat(chunk.TempFile) + require.NoError(t, err) + + // Verify chunk content + content, err := os.ReadFile(chunk.TempFile) + require.NoError(t, err) + assert.Contains(t, string(content), "test content") +} + +func TestIncrDownloader_MergeChunks(t *testing.T) { + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Create test chunks + chunks := []*ChunkInfo{ + { + Index: 0, + Start: 0, + End: 1023, + TempFile: filepath.Join(tempDir, "chunk1.part0"), + }, + { + Index: 1, + Start: 1024, + End: 2047, + TempFile: filepath.Join(tempDir, "chunk2.part1"), + }, + } + + // Create test chunk files + err := os.WriteFile(chunks[0].TempFile, []byte("chunk1 content"), 0644) + require.NoError(t, err) + err = os.WriteFile(chunks[1].TempFile, []byte("chunk2 content"), 0644) + require.NoError(t, err) + + outputPath := filepath.Join(tempDir, "merged-file.tar.lz4") + + // Test chunk merging + err = downloader.mergeChunks(chunks, outputPath) + require.NoError(t, err) + + // Verify merged file + _, err = os.Stat(outputPath) + require.NoError(t, err) + + // Verify merged content + content, err := os.ReadFile(outputPath) + require.NoError(t, err) + assert.Contains(t, string(content), "chunk1 content") + assert.Contains(t, string(content), "chunk2 content") +} + +func TestIncrDownloader_VerifyAndExtract(t *testing.T) { + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + testContent := "test file content" + testFile := filepath.Join(tempDir, "test-file.tar.lz4") + err := os.WriteFile(testFile, []byte(testContent), 0644) + require.NoError(t, err) + + file := &IncrFileInfo{ + Metadata: IncrMetadata{ + FileName: "test-file.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", // Empty file MD5 + Size: uint64(len(testContent)), + }, + LocalPath: testFile, + } + + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + err = downloader.verifyAndExtract(file) + assert.Error(t, err) +} + +func TestIncrDownloader_VerifyHash(t *testing.T) { + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Create test file + testContent := "" + testFile := filepath.Join(tempDir, "test-file.tar.lz4") + err := os.WriteFile(testFile, []byte(testContent), 0644) + require.NoError(t, err) + + // Create test file info (empty file MD5) + file := &IncrFileInfo{ + Metadata: IncrMetadata{ + FileName: "test-file.tar.lz4", + MD5Sum: "d41d8cd98f00b204e9800998ecf8427e", // Empty file MD5 + Size: 0, + }, + LocalPath: testFile, + } + + // Test MD5 verification + err = downloader.verifyHash(file) + require.NoError(t, err) +} + +func TestIncrDownloader_VerifyHashMismatch(t *testing.T) { + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Create test file + testContent := "test content" + testFile := filepath.Join(tempDir, "test-file.tar.lz4") + err := os.WriteFile(testFile, []byte(testContent), 0644) + require.NoError(t, err) + + // Create test file info (wrong MD5) + file := &IncrFileInfo{ + Metadata: IncrMetadata{ + FileName: "test-file.tar.lz4", + MD5Sum: "wrong-md5-hash", + Size: uint64(len(testContent)), + }, + LocalPath: testFile, + } + + // Test MD5 verification failure + err = downloader.verifyHash(file) + assert.Error(t, err) + assert.Contains(t, err.Error(), "hash mismatch for test-file.tar.lz4") +} + +func TestIncrDownloader_Prepare(t *testing.T) { + // Setup test environment + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Create test metadata + metadata := []IncrMetadata{ + { + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "test-md5-1", + Size: 1024, + }, + { + FileName: "test-incr-2000-2999.tar.lz4", + MD5Sum: "test-md5-2", + Size: 2048, + }, + { + FileName: "test-incr-3000-3999.tar.lz4", + MD5Sum: "test-md5-3", + Size: 3072, + }, + } + + // Test preparation with empty state + // Since fetchMetadata is private, we directly test parseFileInfo + files, err := downloader.parseFileInfo(metadata) + require.NoError(t, err) + assert.Len(t, files, 3) + + // Verify file list + assert.Equal(t, "test-incr-1000-1999.tar.lz4", files[0].Metadata.FileName) + assert.Equal(t, uint64(1000), files[0].StartBlock) + assert.Equal(t, uint64(1999), files[0].EndBlock) +} + +func TestIncrDownloader_FileStatusManagement(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Test file status management methods + fileName := "test-file.tar.lz4" + + // Test mark as to download + downloader.markFileAsToDownload(fileName) + toDownloadFiles, err := downloader.loadToDownloadFiles() + require.NoError(t, err) + assert.Contains(t, toDownloadFiles, fileName) + + // Test mark as downloaded + downloader.markFileAsDownloaded(fileName) + downloadedFiles, err := downloader.loadDownloadedFiles() + require.NoError(t, err) + assert.Contains(t, downloadedFiles, fileName) + + // Verify removed from to-download list + toDownloadFiles, err = downloader.loadToDownloadFiles() + require.NoError(t, err) + assert.NotContains(t, toDownloadFiles, fileName) + + // Test mark as to merge + downloader.markFileAsToMerge(fileName) + toMergeFiles, err := downloader.loadToMergeFiles() + require.NoError(t, err) + assert.Contains(t, toMergeFiles, fileName) + + // Test mark as merged + downloader.markFileAsMerged(fileName) + mergedFiles, err := downloader.loadMergedFiles() + require.NoError(t, err) + assert.Contains(t, mergedFiles, fileName) + + // Verify removed from to-merge list + toMergeFiles, err = downloader.loadToMergeFiles() + require.NoError(t, err) + assert.NotContains(t, toMergeFiles, fileName) +} + +func TestIncrDownloader_PrepareWithExistingStatus(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Pre-set some file statuses + err := downloader.saveDownloadedFiles([]string{"test-incr-1000-1999.tar.lz4"}) + assert.NoError(t, err) + err = downloader.saveMergedFiles([]string{"test-incr-2000-2999.tar.lz4"}) + assert.NoError(t, err) + err = downloader.saveToDownloadFiles([]string{"test-incr-3000-3999.tar.lz4"}) + assert.NoError(t, err) + err = downloader.saveToMergeFiles([]string{"test-incr-4000-4999.tar.lz4"}) + assert.NoError(t, err) + + // Create test metadata + metadata := []IncrMetadata{ + { + FileName: "test-incr-1000-1999.tar.lz4", // Downloaded + MD5Sum: "test-md5-1", + Size: 1024, + }, + { + FileName: "test-incr-2000-2999.tar.lz4", // Merged + MD5Sum: "test-md5-2", + Size: 2048, + }, + { + FileName: "test-incr-3000-3999.tar.lz4", // Currently downloading + MD5Sum: "test-md5-3", + Size: 3072, + }, + { + FileName: "test-incr-4000-4999.tar.lz4", // Currently merging + MD5Sum: "test-md5-4", + Size: 4096, + }, + { + FileName: "test-incr-5000-5999.tar.lz4", // New file + MD5Sum: "test-md5-5", + Size: 5120, + }, + } + + // Directly test parseFileInfo and file status management logic + files, err := downloader.parseFileInfo(metadata) + require.NoError(t, err) + assert.Len(t, files, 5) + + // Manually test file status filtering logic + downloadedFiles, err := downloader.loadDownloadedFiles() + require.NoError(t, err) + mergedFiles, err := downloader.loadMergedFiles() + require.NoError(t, err) + toDownloadFiles, err := downloader.loadToDownloadFiles() + require.NoError(t, err) + toMergeFiles, err := downloader.loadToMergeFiles() + require.NoError(t, err) + + // Create lookup sets + downloadedSet := make(map[string]bool) + for _, file := range downloadedFiles { + downloadedSet[file] = true + } + mergedSet := make(map[string]bool) + for _, file := range mergedFiles { + mergedSet[file] = true + } + toDownloadSet := make(map[string]bool) + for _, file := range toDownloadFiles { + toDownloadSet[file] = true + } + toMergeSet := make(map[string]bool) + for _, file := range toMergeFiles { + toMergeSet[file] = true + } + + // Verify file status filtering + var newToDownloadFiles []*IncrFileInfo + for _, file := range files { + fileName := file.Metadata.FileName + if !downloadedSet[fileName] && !mergedSet[fileName] && !toDownloadSet[fileName] && !toMergeSet[fileName] { + newToDownloadFiles = append(newToDownloadFiles, file) + } + } + + // Only new files should be in download queue + assert.Len(t, newToDownloadFiles, 1) + assert.Equal(t, "test-incr-5000-5999.tar.lz4", newToDownloadFiles[0].Metadata.FileName) +} + +func TestIncrDownloader_QueueForMerge(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Create test file + file := &IncrFileInfo{ + Metadata: IncrMetadata{ + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "test-md5", + Size: 1024, + }, + StartBlock: 1000, + EndBlock: 1999, + LocalPath: filepath.Join(tempDir, "test-incr-1000-1999.tar.lz4"), + } + + // Test adding to merge queue + downloader.queueForMerge(file) + + // Verify file added to merge queue + toMergeFiles, err := downloader.loadToMergeFiles() + require.NoError(t, err) + assert.Contains(t, toMergeFiles, file.Metadata.FileName) + + // Verify file added to memory map + downloader.mergeMutex.Lock() + assert.Contains(t, downloader.downloadedFilesMap, uint64(1000)) + assert.Equal(t, file, downloader.downloadedFilesMap[1000]) + downloader.mergeMutex.Unlock() + + // Test duplicate addition (should be ignored) + downloader.queueForMerge(file) + toMergeFiles, err = downloader.loadToMergeFiles() + require.NoError(t, err) + // Should have only one entry + count := 0 + for _, f := range toMergeFiles { + if f == file.Metadata.FileName { + count++ + } + } + assert.Equal(t, 1, count) +} + +func TestIncrDownloader_GetFileStatusStats(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Set some test data + downloader.totalFiles = 5 + downloader.downloadedFiles = 2 + downloader.mergedFiles = 1 + downloader.expectedNextBlockStart = 1000 + + err := downloader.saveDownloadedFiles([]string{"file1.tar.lz4", "file2.tar.lz4"}) + assert.NoError(t, err) + err = downloader.saveMergedFiles([]string{"file3.tar.lz4"}) + assert.NoError(t, err) + err = downloader.saveToDownloadFiles([]string{"file4.tar.lz4"}) + assert.NoError(t, err) + err = downloader.saveToMergeFiles([]string{"file5.tar.lz4"}) + assert.NoError(t, err) + + // Get statistics + stats := downloader.GetFileStatusStats() + + // Verify statistics + assert.Equal(t, 5, stats["totalFiles"]) + assert.Equal(t, 2, stats["downloadedFiles"]) + assert.Equal(t, 1, stats["mergedFiles"]) + assert.Equal(t, 1, stats["toDownloadFiles"]) + assert.Equal(t, 1, stats["toMergeFiles"]) + assert.Equal(t, uint64(1000), stats["expectedNextBlockStart"]) +} + +func TestIncrDownloader_ParseFileInfo(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Test valid metadata + metadata := []IncrMetadata{ + { + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "test-md5-1", + Size: 1024, + }, + { + FileName: "test-incr-2000-2999.tar.lz4", + MD5Sum: "test-md5-2", + Size: 2048, + }, + { + FileName: "test-incr-3000-3999.tar.lz4", + MD5Sum: "test-md5-3", + Size: 3072, + }, + } + + files, err := downloader.parseFileInfo(metadata) + require.NoError(t, err) + assert.Len(t, files, 3) + + // Verify file info + assert.Equal(t, "test-incr-1000-1999.tar.lz4", files[0].Metadata.FileName) + assert.Equal(t, uint64(1000), files[0].StartBlock) + assert.Equal(t, uint64(1999), files[0].EndBlock) + assert.Equal(t, filepath.Join(tempDir, "test-incr-1000-1999.tar.lz4"), files[0].LocalPath) + + // Test invalid filename format + invalidMetadata := []IncrMetadata{ + { + FileName: "invalid-filename.txt", + MD5Sum: "test-md5", + Size: 1024, + }, + } + + _, err = downloader.parseFileInfo(invalidMetadata) + assert.Error(t, err) +} + +func TestIncrDownloader_CheckFileContinuity(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Test continuous files + continuousFiles := []*IncrFileInfo{ + { + Metadata: IncrMetadata{FileName: "test-incr-1000-1999.tar.lz4"}, + StartBlock: 1000, + EndBlock: 1999, + }, + { + Metadata: IncrMetadata{FileName: "test-incr-2000-2999.tar.lz4"}, + StartBlock: 2000, + EndBlock: 2999, + }, + { + Metadata: IncrMetadata{FileName: "test-incr-3000-3999.tar.lz4"}, + StartBlock: 3000, + EndBlock: 3999, + }, + } + + err := downloader.checkFileContinuity(continuousFiles) + assert.NoError(t, err) + + // Test discontinuous files + discontinuousFiles := []*IncrFileInfo{ + { + Metadata: IncrMetadata{FileName: "test-incr-1000-1999.tar.lz4"}, + StartBlock: 1000, + EndBlock: 1999, + }, + { + Metadata: IncrMetadata{FileName: "test-incr-2001-3000.tar.lz4"}, + StartBlock: 2001, // Should be 2001 + EndBlock: 3000, + }, + } + + err = downloader.checkFileContinuity(discontinuousFiles) + assert.Error(t, err) +} + +func TestIncrDownloader_DatabaseOperations(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Test saving and loading various file lists + testFiles := []string{"file1.tar.lz4", "file2.tar.lz4", "file3.tar.lz4"} + + // Test downloaded files list + err := downloader.saveDownloadedFiles(testFiles) + require.NoError(t, err) + + loadedFiles, err := downloader.loadDownloadedFiles() + require.NoError(t, err) + assert.Equal(t, testFiles, loadedFiles) + + // Test merged files list + err = downloader.saveMergedFiles(testFiles) + require.NoError(t, err) + + mergedFiles, err := downloader.loadMergedFiles() + require.NoError(t, err) + assert.Equal(t, testFiles, mergedFiles) + + // Test to-download files list + err = downloader.saveToDownloadFiles(testFiles) + require.NoError(t, err) + + toDownloadFiles, err := downloader.loadToDownloadFiles() + require.NoError(t, err) + assert.Equal(t, testFiles, toDownloadFiles) + + // Test to-merge files list + err = downloader.saveToMergeFiles(testFiles) + require.NoError(t, err) + + toMergeFiles, err := downloader.loadToMergeFiles() + require.NoError(t, err) + assert.Equal(t, testFiles, toMergeFiles) +} + +func TestIncrDownloader_ContextCancellation(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Test cancellation + downloader.Cancel() + + // Verify context is cancelled + select { + case <-downloader.ctx.Done(): + // Expected behavior + default: + t.Error("Context should be cancelled") + } +} + +func TestIncrDownloader_Close(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Add some test data to downloaded files map + file := &IncrFileInfo{ + Metadata: IncrMetadata{FileName: "test.tar.lz4"}, + StartBlock: 1000, + EndBlock: 1999, + } + downloader.downloadedFilesMap[1000] = file + + // Test close operation + downloader.Close() + + // Verify downloaded files map is cleared + assert.Empty(t, downloader.downloadedFilesMap) +} + +// func createTestMetadataFile(t *testing.T, tempDir string, metadata []IncrMetadata) string { +// metadataPath := filepath.Join(tempDir, "incr_metadata.json") +// data, err := json.Marshal(metadata) +// require.NoError(t, err) +// +// err = os.WriteFile(metadataPath, data, 0644) +// require.NoError(t, err) +// +// return metadataPath +// } + +func TestIncrDownloader_Integration(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + metadata := []IncrMetadata{ + { + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "test-md5-1", + Size: 1024, + }, + { + FileName: "test-incr-2000-2999.tar.lz4", + MD5Sum: "test-md5-2", + Size: 2048, + }, + } + + // Directly test parseFileInfo + files, err := downloader.parseFileInfo(metadata) + require.NoError(t, err) + assert.Len(t, files, 2) + + // Verify initial state + assert.Equal(t, uint64(1000), files[0].StartBlock) + + // Simulate file download completion + file := files[0] + downloader.markFileAsDownloaded(file.Metadata.FileName) + downloader.queueForMerge(file) + + // Verify status updates + downloadedFiles, err := downloader.loadDownloadedFiles() + require.NoError(t, err) + assert.Contains(t, downloadedFiles, file.Metadata.FileName) + + toMergeFiles, err := downloader.loadToMergeFiles() + require.NoError(t, err) + assert.Contains(t, toMergeFiles, file.Metadata.FileName) + + // Verify memory map + downloader.mergeMutex.Lock() + assert.Contains(t, downloader.downloadedFilesMap, file.StartBlock) + downloader.mergeMutex.Unlock() + + // Simulate merge completion + downloader.markFileAsMerged(file.Metadata.FileName) + + // Verify final state + mergedFiles, err := downloader.loadMergedFiles() + require.NoError(t, err) + assert.Contains(t, mergedFiles, file.Metadata.FileName) + + toMergeFiles, err = downloader.loadToMergeFiles() + require.NoError(t, err) + assert.NotContains(t, toMergeFiles, file.Metadata.FileName) +} + +func TestIncrDownloader_ProcessFileStatus(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Create test files + files := []*IncrFileInfo{ + { + Metadata: IncrMetadata{ + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "test-md5-1", + Size: 1024, + }, + StartBlock: 1000, + EndBlock: 1999, + }, + { + Metadata: IncrMetadata{ + FileName: "test-incr-2000-2999.tar.lz4", + MD5Sum: "test-md5-2", + Size: 2048, + }, + StartBlock: 2000, + EndBlock: 2999, + }, + { + Metadata: IncrMetadata{ + FileName: "test-incr-3000-3999.tar.lz4", + MD5Sum: "test-md5-3", + Size: 3072, + }, + StartBlock: 3000, + EndBlock: 3999, + }, + { + Metadata: IncrMetadata{ + FileName: "test-incr-4000-4999.tar.lz4", + MD5Sum: "test-md5-4", + Size: 4096, + }, + StartBlock: 4000, + EndBlock: 4999, + }, + } + + // Test different file status scenarios + testCases := []struct { + name string + downloadedFiles []string + mergedFiles []string + toDownloadFiles []string + toMergeFiles []string + expectedToDownloadCount int + expectedToMergeCount int + expectedTotalFiles int + expectedNextBlockStart uint64 + }{ + { + name: "All new files", + downloadedFiles: []string{}, + mergedFiles: []string{}, + toDownloadFiles: []string{}, + toMergeFiles: []string{}, + expectedToDownloadCount: 4, + expectedToMergeCount: 0, + expectedTotalFiles: 4, + expectedNextBlockStart: 1000, + }, + { + name: "Some files already downloaded", + downloadedFiles: []string{"test-incr-1000-1999.tar.lz4"}, + mergedFiles: []string{}, + toDownloadFiles: []string{}, + toMergeFiles: []string{}, + expectedToDownloadCount: 3, + expectedToMergeCount: 1, + expectedTotalFiles: 3, + expectedNextBlockStart: 1000, + }, + { + name: "Some files already merged", + downloadedFiles: []string{}, + mergedFiles: []string{"test-incr-1000-1999.tar.lz4"}, + toDownloadFiles: []string{}, + toMergeFiles: []string{}, + expectedToDownloadCount: 3, + expectedToMergeCount: 0, + expectedTotalFiles: 3, + expectedNextBlockStart: 2000, + }, + { + name: "Some files currently downloading", + downloadedFiles: []string{}, + mergedFiles: []string{}, + toDownloadFiles: []string{"test-incr-1000-1999.tar.lz4"}, + toMergeFiles: []string{}, + expectedToDownloadCount: 4, // Including the one currently downloading + expectedToMergeCount: 0, + expectedTotalFiles: 4, + expectedNextBlockStart: 1000, + }, + { + name: "Some files currently merging", + downloadedFiles: []string{}, + mergedFiles: []string{}, + toDownloadFiles: []string{}, + toMergeFiles: []string{"test-incr-1000-1999.tar.lz4"}, + expectedToDownloadCount: 3, + expectedToMergeCount: 1, // Including the one currently merging + expectedTotalFiles: 3, + expectedNextBlockStart: 1000, + }, + { + name: "Mixed statuses", + downloadedFiles: []string{"test-incr-2000-2999.tar.lz4"}, + mergedFiles: []string{"test-incr-1000-1999.tar.lz4"}, + toDownloadFiles: []string{"test-incr-3000-3999.tar.lz4"}, + toMergeFiles: []string{}, + expectedToDownloadCount: 2, + expectedToMergeCount: 1, + expectedTotalFiles: 2, + expectedNextBlockStart: 2000, + }, + { + name: "All files already merged", + downloadedFiles: []string{}, + mergedFiles: []string{"test-incr-1000-1999.tar.lz4", "test-incr-2000-2999.tar.lz4", "test-incr-3000-3999.tar.lz4", "test-incr-4000-4999.tar.lz4"}, + toDownloadFiles: []string{}, + toMergeFiles: []string{}, + expectedToDownloadCount: 0, + expectedToMergeCount: 0, + expectedTotalFiles: 0, + expectedNextBlockStart: 0, + }, + { + name: "All files currently downloading", + downloadedFiles: []string{}, + mergedFiles: []string{}, + toDownloadFiles: []string{"test-incr-1000-1999.tar.lz4", "test-incr-2000-2999.tar.lz4", "test-incr-3000-3999.tar.lz4", "test-incr-4000-4999.tar.lz4"}, + toMergeFiles: []string{}, + expectedToDownloadCount: 4, + expectedToMergeCount: 0, + expectedTotalFiles: 4, + expectedNextBlockStart: 1000, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Reset downloader state + downloader.downloadedFiles = 0 + downloader.files = nil + downloader.totalFiles = 0 + downloader.downloadedFilesMap = make(map[uint64]*IncrFileInfo) + downloader.expectedNextBlockStart = 0 + + // Pre-set database state for this test case + if len(tc.toDownloadFiles) > 0 { + err := downloader.saveToDownloadFiles(tc.toDownloadFiles) + require.NoError(t, err) + } + if len(tc.downloadedFiles) > 0 { + err := downloader.saveDownloadedFiles(tc.downloadedFiles) + require.NoError(t, err) + } + if len(tc.toMergeFiles) > 0 { + err := downloader.saveToMergeFiles(tc.toMergeFiles) + require.NoError(t, err) + } + if len(tc.mergedFiles) > 0 { + err := downloader.saveMergedFiles(tc.mergedFiles) + require.NoError(t, err) + } + + // Test processFileStatus + err := downloader.processFileStatus(files) + + require.NoError(t, err) + assert.Len(t, downloader.files, tc.expectedToDownloadCount) + assert.Equal(t, tc.expectedTotalFiles, downloader.totalFiles) + assert.Equal(t, tc.expectedNextBlockStart, downloader.expectedNextBlockStart) + + // Verify downloaded files map + assert.Len(t, downloader.downloadedFilesMap, tc.expectedToMergeCount) + + // Verify database state + toDownloadFiles, err := downloader.loadToDownloadFiles() + require.NoError(t, err) + assert.Len(t, toDownloadFiles, tc.expectedToDownloadCount) + + toMergeFiles, err := downloader.loadToMergeFiles() + require.NoError(t, err) + assert.Len(t, toMergeFiles, tc.expectedToMergeCount) + + // Verify specific file assignments + if tc.expectedToMergeCount > 0 { + // Check that files are properly assigned to merge queue + for _, file := range downloader.downloadedFilesMap { + assert.Contains(t, toMergeFiles, file.Metadata.FileName) + } + } + + // Clean up database state for next test + err = downloader.saveDownloadedFiles([]string{}) + assert.NoError(t, err) + err = downloader.saveMergedFiles([]string{}) + assert.NoError(t, err) + err = downloader.saveToDownloadFiles([]string{}) + assert.NoError(t, err) + err = downloader.saveToMergeFiles([]string{}) + assert.NoError(t, err) + }) + } +} + +func TestIncrDownloader_ProcessFileStatus_EmptyFiles(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Test with empty files list + files := []*IncrFileInfo{} + err := downloader.processFileStatus(files) + require.NoError(t, err) + assert.Len(t, downloader.files, 0) + assert.Equal(t, 0, downloader.totalFiles) + assert.Equal(t, uint64(0), downloader.expectedNextBlockStart) +} + +func TestIncrDownloader_ProcessFileStatus_BlockOrdering(t *testing.T) { + db := createTestDB() + defer db.Close() + trieDB := triedb.NewDatabase(db, nil) + defer trieDB.Close() + + tempDir := t.TempDir() + downloader := NewIncrDownloader(db, trieDB, testURL, tempDir, 1000) + + // Create test files with non-sequential block numbers + files := []*IncrFileInfo{ + { + Metadata: IncrMetadata{ + FileName: "test-incr-3000-3999.tar.lz4", + MD5Sum: "test-md5-3", + Size: 3072, + }, + StartBlock: 3000, + EndBlock: 3999, + }, + { + Metadata: IncrMetadata{ + FileName: "test-incr-1000-1999.tar.lz4", + MD5Sum: "test-md5-1", + Size: 1024, + }, + StartBlock: 1000, + EndBlock: 1999, + }, + { + Metadata: IncrMetadata{ + FileName: "test-incr-2000-2999.tar.lz4", + MD5Sum: "test-md5-2", + Size: 2048, + }, + StartBlock: 2000, + EndBlock: 2999, + }, + } + + // Set some files as downloaded + err := downloader.saveDownloadedFiles([]string{"test-incr-1000-1999.tar.lz4", "test-incr-2000-2999.tar.lz4"}) + assert.NoError(t, err) + + // Test processFileStatus + err = downloader.processFileStatus(files) + require.NoError(t, err) + + // Verify that the earliest block start is used for expectedNextBlockStart + assert.Equal(t, uint64(1000), downloader.expectedNextBlockStart) + + // Verify that both downloaded files are in the merge queue + assert.Len(t, downloader.downloadedFilesMap, 2) + assert.Contains(t, downloader.downloadedFilesMap, uint64(1000)) + assert.Contains(t, downloader.downloadedFilesMap, uint64(2000)) +} diff --git a/core/incr_merger.go b/core/incr_merger.go new file mode 100644 index 0000000000..baf903323c --- /dev/null +++ b/core/incr_merger.go @@ -0,0 +1,242 @@ +package core + +import ( + "errors" + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/pebble" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/triedb" +) + +// MergeIncrSnapshot merges the incremental snapshot into local data. +func MergeIncrSnapshot(chainDB ethdb.Database, trieDB *triedb.Database, incrPath string) error { + var wg sync.WaitGroup + errChan := make(chan error, 3) + + if err := updateGenesisMeta(chainDB); err != nil { + return err + } + + // merge incremental state data + wg.Add(1) + go func() { + defer wg.Done() + if err := trieDB.MergeIncrState(incrPath); err != nil { + log.Error("Failed to merge incremental state data", "path", incrPath, "err", err) + errChan <- fmt.Errorf("failed to merge incremental state data: %v", err) + } + }() + + // merge incremental block data + wg.Add(1) + go func() { + defer wg.Done() + if err := mergeIncrBlock(incrPath, chainDB); err != nil { + log.Error("Failed to merge incremental block data", "path", incrPath, "err", err) + errChan <- fmt.Errorf("failed to merge incremental block data: %v", err) + } + }() + + // merge contract codes + wg.Add(1) + go func() { + defer wg.Done() + if err := mergeIncrKV(incrPath, chainDB); err != nil { + log.Error("Failed to merge incremental contract codes", "path", incrPath, "err", err) + errChan <- fmt.Errorf("failed to merge incremental contract codes: %v", err) + } + }() + + go func() { + wg.Wait() + close(errChan) + }() + + var mergeErrors []error + for err := range errChan { + mergeErrors = append(mergeErrors, err) + } + if len(mergeErrors) > 0 { + errs := errors.Join(mergeErrors...) + log.Error("Parallel merge operations failed", "total_errors", len(mergeErrors), "path", incrPath) + return errs + } + + log.Info("All merge operations completed successfully", "path", incrPath) + return nil +} + +func mergeIncrBlock(incrDir string, chainDB ethdb.Database) error { + incrChainFreezer, err := rawdb.OpenIncrChainFreezer(incrDir, true) + if err != nil { + log.Error("Failed to open incremental chain freezer", "err", err) + return err + } + defer incrChainFreezer.Close() + + // Get chain config from database + chainConfig, err := rawdb.GetChainConfig(chainDB) + if err != nil { + log.Error("Failed to get chain config", "err", err) + return err + } + + incrAncients, _ := incrChainFreezer.Ancients() + tail, _ := incrChainFreezer.Tail() + + // delete old block data in pebble + if err = chainDB.CleanBlock(chainDB, tail); err != nil { + log.Error("Failed to force freeze to ancients", "err", err) + return err + } + + baseHead, _ := chainDB.Ancients() + if tail == baseHead && baseHead <= incrAncients { + for number := tail; number < incrAncients-1; number++ { + hashBytes, header, body, receipts, td, err := rawdb.ReadIncrBlock(incrChainFreezer, number) + if err != nil { + log.Error("Failed to read incremental block", "block", number, "err", err) + return err + } + + var h types.Header + if err = rlp.DecodeBytes(header, &h); err != nil { + log.Error("Failed to decode header", "block", number, "err", err) + return err + } + // Check if Cancun hardfork is active for this block + isCancunActive := chainConfig.IsCancun(h.Number, h.Time) + var sidecars rlp.RawValue + if isCancunActive { + sidecars, err = rawdb.ReadIncrChainBlobSideCars(incrChainFreezer, number) + if err != nil { + log.Error("Failed to read increment chain blob side car", "block", number, "err", err) + return err + } + } + + blockBatch := chainDB.NewBatch() + hash := common.BytesToHash(hashBytes) + rawdb.WriteCanonicalHash(blockBatch, hash, number) + rawdb.WriteTdRLP(blockBatch, hash, number, td) + rawdb.WriteBodyRLP(blockBatch, hash, number, body) + rawdb.WriteHeaderRLP(blockBatch, hash, number, header) + rawdb.WriteRawReceipts(blockBatch, hash, number, receipts) + if isCancunActive { + rawdb.WriteBlobSidecarsRLP(blockBatch, hash, number, sidecars) + } + if err = blockBatch.Write(); err != nil { + log.Error("Failed to batch commit block data", "err", err) + return err + } + } + } else { + log.Crit("There are block data gap", "tail", tail, "baseHead", baseHead) + } + + if err = rawdb.FinalizeIncrementalMerge(chainDB, incrChainFreezer, chainConfig, incrAncients-1); err != nil { + log.Error("Failed to finalize incremental data merge", "err", err) + return err + } + + log.Info("Finished merging incremental block data", "merged_number", incrAncients-baseHead) + return nil +} + +// mergeIncrKV merges incr kv: contract codes, parlia snapshot, chain config and genesis state spec.. +func mergeIncrKV(incrDir string, chainDB ethdb.Database) error { + newDB, err := pebble.New(incrDir, 10, 10, "incremental", true) + if err != nil { + log.Error("Failed to open pebble to read incremental data", "err", err) + return err + } + defer newDB.Close() + + it := newDB.NewIterator(rawdb.CodePrefix, nil) + defer it.Release() + + codeCount := 0 + for it.Next() { + key := it.Key() + value := it.Value() + + isCode, hashBytes := rawdb.IsCodeKey(key) + if !isCode { + log.Warn("Invalid code key found", "key", fmt.Sprintf("%x", key)) + continue + } + + codeHash := common.BytesToHash(hashBytes) + if rawdb.HasCodeWithPrefix(chainDB, codeHash) { + log.Debug("Code already exists, skipping", "hash", codeHash.Hex()) + continue + } + rawdb.WriteCode(chainDB, codeHash, value) + codeCount++ + } + + if err = it.Error(); err != nil { + log.Error("Iterator error while reading contract codes", "err", err) + return err + } + + // Merge Parlia snapshots from incremental snapshot to local data + if err = mergeParliaSnapshots(chainDB, newDB); err != nil { + log.Error("Failed to merge Parlia snapshots", "err", err) + return err + } + + if err = updateGenesisMeta(chainDB); err != nil { + log.Error("Failed to merge genesis meta data", "err", err) + return err + } + + log.Info("Complete merging contract codes", "total", codeCount) + return nil +} + +// mergeParliaSnapshots merges Parlia consensus snapshots from incremental snapshot into local data +func mergeParliaSnapshots(chainDB ethdb.Database, incrKV *pebble.Database) error { + log.Info("Starting Parlia snapshots import from incremental snapshot") + iter := incrKV.NewIterator(rawdb.ParliaSnapshotPrefix, nil) + defer iter.Release() + + count := 0 + for iter.Next() { + key := iter.Key() + value := iter.Value() + if err := chainDB.Put(key, value); err != nil { + log.Error("Failed to store Parlia snapshot in main DB", "key", common.Bytes2Hex(key), "err", err) + return err + } + count++ + } + + if iter.Error() != nil { + log.Error("Failed to iterate Parlia snapshot", "error", iter.Error()) + return iter.Error() + } + + log.Info("Completed Parlia snapshots merging", "total_snapshots", count) + return nil +} + +// updateGenesisMeta updates base snapshot chain config +func updateGenesisMeta(chainDB ethdb.Database) error { + stored := rawdb.ReadCanonicalHash(chainDB, 0) + if (stored == common.Hash{}) { + return fmt.Errorf("invalid genesis hash in database: %x", stored) + } + + builtInConf := params.GetBuiltInChainConfig(stored) + rawdb.WriteChainConfig(chainDB, stored, builtInConf) + return nil +} diff --git a/core/opcodeCompiler/compiler/evmByteCode.go b/core/opcodeCompiler/compiler/evmByteCode.go new file mode 100644 index 0000000000..e45f911aec --- /dev/null +++ b/core/opcodeCompiler/compiler/evmByteCode.go @@ -0,0 +1,267 @@ +package compiler + +// This is copied from vm/opcodes.go. + +// ByteCode is an EVM ByteCode +type ByteCode byte + +// 0x0 range - arithmetic ops. +const ( + STOP ByteCode = 0x0 + ADD ByteCode = 0x1 + MUL ByteCode = 0x2 + SUB ByteCode = 0x3 + DIV ByteCode = 0x4 + SDIV ByteCode = 0x5 + MOD ByteCode = 0x6 + SMOD ByteCode = 0x7 + ADDMOD ByteCode = 0x8 + MULMOD ByteCode = 0x9 + EXP ByteCode = 0xa + SIGNEXTEND ByteCode = 0xb +) + +// 0x10 range - comparison ops. +const ( + LT ByteCode = 0x10 + GT ByteCode = 0x11 + SLT ByteCode = 0x12 + SGT ByteCode = 0x13 + EQ ByteCode = 0x14 + ISZERO ByteCode = 0x15 + AND ByteCode = 0x16 + OR ByteCode = 0x17 + XOR ByteCode = 0x18 + NOT ByteCode = 0x19 + BYTE ByteCode = 0x1a + SHL ByteCode = 0x1b + SHR ByteCode = 0x1c + SAR ByteCode = 0x1d +) + +// 0x20 range - crypto. +const ( + KECCAK256 ByteCode = 0x20 +) + +// 0x30 range - closure state. +const ( + ADDRESS ByteCode = 0x30 + BALANCE ByteCode = 0x31 + ORIGIN ByteCode = 0x32 + CALLER ByteCode = 0x33 + CALLVALUE ByteCode = 0x34 + CALLDATALOAD ByteCode = 0x35 + CALLDATASIZE ByteCode = 0x36 + CALLDATACOPY ByteCode = 0x37 + CODESIZE ByteCode = 0x38 + CODECOPY ByteCode = 0x39 + GASPRICE ByteCode = 0x3a + EXTCODESIZE ByteCode = 0x3b + EXTCODECOPY ByteCode = 0x3c + RETURNDATASIZE ByteCode = 0x3d + RETURNDATACOPY ByteCode = 0x3e + EXTCODEHASH ByteCode = 0x3f +) + +// 0x40 range - block operations. +const ( + BLOCKHASH ByteCode = 0x40 + COINBASE ByteCode = 0x41 + TIMESTAMP ByteCode = 0x42 + NUMBER ByteCode = 0x43 + DIFFICULTY ByteCode = 0x44 + RANDOM ByteCode = 0x44 // Same as DIFFICULTY + PREVRANDAO ByteCode = 0x44 // Same as DIFFICULTY + GASLIMIT ByteCode = 0x45 + CHAINID ByteCode = 0x46 + SELFBALANCE ByteCode = 0x47 + BASEFEE ByteCode = 0x48 + BLOBHASH ByteCode = 0x49 + BLOBBASEFEE ByteCode = 0x4a +) + +// 0x50 range - 'storage' and execution. +const ( + POP ByteCode = 0x50 + MLOAD ByteCode = 0x51 + MSTORE ByteCode = 0x52 + MSTORE8 ByteCode = 0x53 + SLOAD ByteCode = 0x54 + SSTORE ByteCode = 0x55 + JUMP ByteCode = 0x56 + JUMPI ByteCode = 0x57 + PC ByteCode = 0x58 + MSIZE ByteCode = 0x59 + GAS ByteCode = 0x5a + JUMPDEST ByteCode = 0x5b + TLOAD ByteCode = 0x5c + TSTORE ByteCode = 0x5d + MCOPY ByteCode = 0x5e + PUSH0 ByteCode = 0x5f +) + +// 0x60 range - pushes. +const ( + PUSH1 ByteCode = 0x60 + iota + PUSH2 + PUSH3 + PUSH4 + PUSH5 + PUSH6 + PUSH7 + PUSH8 + PUSH9 + PUSH10 + PUSH11 + PUSH12 + PUSH13 + PUSH14 + PUSH15 + PUSH16 + PUSH17 + PUSH18 + PUSH19 + PUSH20 + PUSH21 + PUSH22 + PUSH23 + PUSH24 + PUSH25 + PUSH26 + PUSH27 + PUSH28 + PUSH29 + PUSH30 + PUSH31 + PUSH32 +) + +// 0x80 range - dups. +const ( + DUP1 ByteCode = 0x80 + iota + DUP2 + DUP3 + DUP4 + DUP5 + DUP6 + DUP7 + DUP8 + DUP9 + DUP10 + DUP11 + DUP12 + DUP13 + DUP14 + DUP15 + DUP16 +) + +// 0x90 range - swaps. +const ( + SWAP1 ByteCode = 0x90 + iota + SWAP2 + SWAP3 + SWAP4 + SWAP5 + SWAP6 + SWAP7 + SWAP8 + SWAP9 + SWAP10 + SWAP11 + SWAP12 + SWAP13 + SWAP14 + SWAP15 + SWAP16 +) + +// 0xa0 range - logging ops. +const ( + LOG0 ByteCode = 0xa0 + iota + LOG1 + LOG2 + LOG3 + LOG4 +) + +// 0xb0 range - customized instructions. +const ( + Nop ByteCode = 0xb0 + iota + AndSwap1PopSwap2Swap1 + Swap2Swap1PopJump + Swap1PopSwap2Swap1 + PopSwap2Swap1Pop + Push2Jump + Push2JumpI + Push1Push1 + Push1Add + Push1Shl + Push1Dup1 + Swap1Pop + PopJump + Pop2 + Swap2Swap1 + Swap2Pop + Dup2LT + JumpIfZero + + IsZeroPush2 + Dup2MStorePush1Add + Dup1Push4EqPush2 + Push1CalldataloadPush1ShrDup1Push4GtPush2 + Push1Push1Push1SHLSub + AndDup2AddSwap1Dup2LT + Swap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT + Dup3And + Swap2Swap1Dup3SubSwap2Dup3GtPush2 + Swap1Dup2 + SHRSHRDup1MulDup1 + Swap3PopPopPop + SubSLTIsZeroPush2 + Dup11MulDup3SubMulDup1 // 0xcf +) + +// 0xd0 range - eof operations. +const ( + DATALOAD ByteCode = 0xd0 + DATALOADN ByteCode = 0xd1 + DATASIZE ByteCode = 0xd2 + DATACOPY ByteCode = 0xd3 +) + +// 0xe0 range - eof operations. +const ( + RJUMP ByteCode = 0xe0 + RJUMPI ByteCode = 0xe1 + RJUMPV ByteCode = 0xe2 + CALLF ByteCode = 0xe3 + RETF ByteCode = 0xe4 + JUMPF ByteCode = 0xe5 + DUPN ByteCode = 0xe6 + SWAPN ByteCode = 0xe7 + EXCHANGE ByteCode = 0xe8 + EOFCREATE ByteCode = 0xec + RETURNCONTRACT ByteCode = 0xee +) + +// 0xf0 range - closures. +const ( + CREATE ByteCode = 0xf0 + CALL ByteCode = 0xf1 + CALLCODE ByteCode = 0xf2 + RETURN ByteCode = 0xf3 + DELEGATECALL ByteCode = 0xf4 + CREATE2 ByteCode = 0xf5 + + RETURNDATALOAD ByteCode = 0xf7 + EXTCALL ByteCode = 0xf8 + EXTDELEGATECALL ByteCode = 0xf9 + + STATICCALL ByteCode = 0xfa + EXTSTATICCALL ByteCode = 0xfb + REVERT ByteCode = 0xfd + INVALID ByteCode = 0xfe + SELFDESTRUCT ByteCode = 0xff +) diff --git a/core/opcodeCompiler/compiler/opCodeCache.go b/core/opcodeCompiler/compiler/opCodeCache.go new file mode 100644 index 0000000000..033c530522 --- /dev/null +++ b/core/opcodeCompiler/compiler/opCodeCache.go @@ -0,0 +1,51 @@ +package compiler + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" +) + +type OpCodeCache struct { + optimizedCodeCache *lru.Cache[common.Hash, []byte] + bitvecCache *lru.Cache[common.Hash, []byte] +} + +func (c *OpCodeCache) GetCachedBitvec(codeHash common.Hash) []byte { + bitvec, _ := c.bitvecCache.Get(codeHash) + return bitvec +} + +func (c *OpCodeCache) AddBitvecCache(codeHash common.Hash, bitvec []byte) { + c.bitvecCache.Add(codeHash, bitvec) +} + +func (c *OpCodeCache) RemoveCachedCode(hash common.Hash) { + c.optimizedCodeCache.Remove(hash) +} + +func (c *OpCodeCache) GetCachedCode(hash common.Hash) []byte { + processedCode, _ := c.optimizedCodeCache.Get(hash) + return processedCode +} + +func (c *OpCodeCache) AddCodeCache(hash common.Hash, optimizedCode []byte) { + c.optimizedCodeCache.Add(hash, optimizedCode) +} + +var opcodeCache *OpCodeCache + +const ( + optimizedCodeCacheCap = 128 * 1024 + bitvecCacheCap = 128 * 1024 +) + +func init() { + opcodeCache = &OpCodeCache{ + optimizedCodeCache: lru.NewCache[common.Hash, []byte](optimizedCodeCacheCap), + bitvecCache: lru.NewCache[common.Hash, []byte](bitvecCacheCap), + } +} + +func getOpCodeCacheInstance() *OpCodeCache { + return opcodeCache +} diff --git a/core/opcodeCompiler/compiler/opCodeProcessor.go b/core/opcodeCompiler/compiler/opCodeProcessor.go new file mode 100644 index 0000000000..ae2113da38 --- /dev/null +++ b/core/opcodeCompiler/compiler/opCodeProcessor.go @@ -0,0 +1,786 @@ +package compiler + +import ( + "errors" + "runtime" + + "github.com/ethereum/go-ethereum/common" +) + +var ( + enabled bool + codeCache *OpCodeCache + taskChannel chan optimizeTask +) + +var ( + ErrFailPreprocessing = errors.New("fail to do preprocessing") + ErrOptimizedDisabled = errors.New("opcode optimization is disabled") +) + +const taskChannelSize = 128 * 1024 + +const ( + generate optimizeTaskType = 1 + flush optimizeTaskType = 2 + + minOptimizedOpcode = 0xb0 + maxOptimizedOpcode = 0xcf +) + +type OpCodeProcessorConfig struct { + DoOpcodeFusion bool +} + +type optimizeTaskType byte + +type CodeType uint8 + +type optimizeTask struct { + taskType optimizeTaskType + hash common.Hash + rawCode []byte +} + +func init() { + taskChannel = make(chan optimizeTask, taskChannelSize) + taskNumber := runtime.NumCPU() / 8 // No need to use too many threads. + if taskNumber < 1 { + taskNumber = 1 + } + codeCache = getOpCodeCacheInstance() + + for i := 0; i < taskNumber; i++ { + go taskProcessor() + } +} + +func EnableOptimization() { + if enabled { + return + } + enabled = true +} + +func DisableOptimization() { + enabled = false +} + +func IsEnabled() bool { + return enabled +} + +func LoadOptimizedCode(hash common.Hash) []byte { + if !enabled { + return nil + } + processedCode := codeCache.GetCachedCode(hash) + return processedCode +} + +func LoadBitvec(codeHash common.Hash) []byte { + if !enabled { + return nil + } + bitvec := codeCache.GetCachedBitvec(codeHash) + return bitvec +} + +func StoreBitvec(codeHash common.Hash, bitvec []byte) { + if !enabled { + return + } + codeCache.AddBitvecCache(codeHash, bitvec) +} + +func GenOrLoadOptimizedCode(hash common.Hash, code []byte) { + if !enabled { + return + } + task := optimizeTask{generate, hash, code} + taskChannel <- task +} + +func taskProcessor() { + for { + task := <-taskChannel + // Process the message here + handleOptimizationTask(task) + } +} + +func handleOptimizationTask(task optimizeTask) { + switch task.taskType { + case generate: + TryGenerateOptimizedCode(task.hash, task.rawCode) + case flush: + DeleteCodeCache(task.hash) + } +} + +// GenOrRewriteOptimizedCode generate the optimized code and refresh the code cache. +func GenOrRewriteOptimizedCode(hash common.Hash, code []byte) ([]byte, error) { + if !enabled { + return nil, ErrOptimizedDisabled + } + processedCode, err := processByteCodes(code) + if err != nil { + return nil, err + } + codeCache.AddCodeCache(hash, processedCode) + return processedCode, err +} + +func TryGenerateOptimizedCode(hash common.Hash, code []byte) ([]byte, error) { + processedCode := codeCache.GetCachedCode(hash) + var err error = nil + if len(processedCode) == 0 { + processedCode, err = GenOrRewriteOptimizedCode(hash, code) + } + return processedCode, err +} + +func DeleteCodeCache(hash common.Hash) { + if !enabled { + return + } + // flush in case there are invalid cached code + codeCache.RemoveCachedCode(hash) +} + +func processByteCodes(code []byte) ([]byte, error) { + //return doOpcodesProcess(code) + return DoCFGBasedOpcodeFusion(code) +} + +// Exported version of doCodeFusion for use in benchmarks and external tests +func DoCodeFusion(code []byte) ([]byte, error) { + // return doCodeFusion(code) + return DoCFGBasedOpcodeFusion(code) +} + +// DoCFGBasedOpcodeFusion performs opcode fusion within basic blocks, skipping blocks of type "others" +func DoCFGBasedOpcodeFusion(code []byte) ([]byte, error) { + // Generate basic blocks + blocks := GenerateBasicBlocks(code) + if len(blocks) == 0 { + return nil, ErrFailPreprocessing + } + + // Create a copy of the original code (only after checking for optimized opcodes) + fusedCode := make([]byte, len(code)) + copy(fusedCode, code) + + // Process each basic block + for i, block := range blocks { + // Skip blocks of type "others" + blockType := getBlockType(block, blocks, i) + if blockType == "others" { + continue + } + + // Check if the block contains optimized opcodes in the original code + hasOptimized := false + for pc := block.StartPC; pc < block.EndPC && pc < uint64(len(code)); { + if code[pc] >= minOptimizedOpcode && code[pc] <= maxOptimizedOpcode { + hasOptimized = true + break + } + // Skip data bytes for PUSH instructions + skip, steps := calculateSkipSteps(code, int(pc)) + if skip { + pc += uint64(steps) + 1 // Add 1 for the opcode byte + } else { + pc++ + } + } + if hasOptimized { + // If any block being processed contains optimized opcodes, return nil, ErrFailPreprocessing + return nil, ErrFailPreprocessing + } + + // Check if the block contains INVALID opcodes in the original code + hasInvalid := false + for pc := block.StartPC; pc < block.EndPC && pc < uint64(len(code)); { + if ByteCode(code[pc]) == INVALID { + hasInvalid = true + break + } + // Skip data bytes for PUSH instructions + skip, steps := calculateSkipSteps(code, int(pc)) + if skip { + pc += uint64(steps) + 1 // Add 1 for the opcode byte + } else { + pc++ + } + } + if hasInvalid { + // Skip processing this block if it contains INVALID opcodes + continue + } + + // Apply fusion within this block + err := fuseBlock(fusedCode, block) + if err != nil { + return code, err + } + } + + return fusedCode, nil +} + +// fuseBlock applies opcode fusion to a single basic block +func fuseBlock(code []byte, block BasicBlock) error { + startPC := int(block.StartPC) + endPC := int(block.EndPC) + + // Process the block's opcodes + for i := startPC; i < endPC; { + if i >= len(code) { + break + } + + // Apply fusion patterns within the block + skipSteps := applyFusionPatterns(code, i, endPC) + if skipSteps > 0 { + i += skipSteps + 1 // Add 1 for the opcode byte + } else { + // Skip data bytes for PUSH instructions + skip, steps := calculateSkipSteps(code, i) + if skip { + i += steps + 1 // Add 1 for the opcode byte + } else { + i++ + } + } + } + + return nil +} + +// applyFusionPatterns applies known fusion patterns and returns the number of steps to skip +func applyFusionPatterns(code []byte, cur int, endPC int) int { + length := len(code) + + // Pattern 1: 15-byte pattern + if length > cur+15 && cur+15 < endPC { + code0 := ByteCode(code[cur+0]) + code2 := ByteCode(code[cur+2]) + code3 := ByteCode(code[cur+3]) + code5 := ByteCode(code[cur+5]) + code6 := ByteCode(code[cur+6]) + code7 := ByteCode(code[cur+7]) + code12 := ByteCode(code[cur+12]) + code13 := ByteCode(code[cur+13]) + + if code0 == PUSH1 && code2 == CALLDATALOAD && code3 == PUSH1 && code5 == SHR && + code6 == DUP1 && code7 == PUSH4 && code12 == GT && code13 == PUSH2 { + op := Push1CalldataloadPush1ShrDup1Push4GtPush2 + code[cur] = byte(op) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + code[cur+5] = byte(Nop) + code[cur+6] = byte(Nop) + code[cur+7] = byte(Nop) + code[cur+12] = byte(Nop) + code[cur+13] = byte(Nop) + return 15 + } + } + + // Pattern 2: 12-byte pattern + if length > cur+12 && cur+12 < endPC { + code0 := ByteCode(code[cur+0]) + code1 := ByteCode(code[cur+1]) + code3 := ByteCode(code[cur+3]) + code4 := ByteCode(code[cur+4]) + code5 := ByteCode(code[cur+5]) + code6 := ByteCode(code[cur+6]) + code7 := ByteCode(code[cur+7]) + code8 := ByteCode(code[cur+8]) + code9 := ByteCode(code[cur+9]) + code10 := ByteCode(code[cur+10]) + code11 := ByteCode(code[cur+11]) + code12 := ByteCode(code[cur+12]) + + if code0 == SWAP1 && code1 == PUSH1 && code3 == DUP1 && code4 == NOT && + code5 == SWAP2 && code6 == ADD && code7 == AND && code8 == DUP2 && + code9 == ADD && code10 == SWAP1 && code11 == DUP2 && code12 == LT { + op := Swap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+3] = byte(Nop) + code[cur+4] = byte(Nop) + code[cur+5] = byte(Nop) + code[cur+6] = byte(Nop) + code[cur+7] = byte(Nop) + code[cur+8] = byte(Nop) + code[cur+9] = byte(Nop) + code[cur+10] = byte(Nop) + code[cur+11] = byte(Nop) + code[cur+12] = byte(Nop) + return 12 + } + } + + // Pattern 3: 9-byte pattern + if length > cur+9 && cur+9 < endPC { + code0 := ByteCode(code[cur+0]) + code1 := ByteCode(code[cur+1]) + code2 := ByteCode(code[cur+2]) + code3 := ByteCode(code[cur+3]) + code4 := ByteCode(code[cur+4]) + code5 := ByteCode(code[cur+5]) + code6 := ByteCode(code[cur+6]) + code7 := ByteCode(code[cur+7]) + + if code0 == DUP1 && code1 == PUSH4 && code6 == EQ && code7 == PUSH2 { + op := Dup1Push4EqPush2 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+6] = byte(Nop) + code[cur+7] = byte(Nop) + return 9 + } + if code0 == SWAP2 && code1 == SWAP1 && code2 == DUP3 && code3 == SUB && code4 == SWAP2 && code5 == DUP3 && code6 == GT && code7 == PUSH2 { + op := Swap2Swap1Dup3SubSwap2Dup3GtPush2 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + code[cur+4] = byte(Nop) + code[cur+5] = byte(Nop) + code[cur+6] = byte(Nop) + code[cur+7] = byte(Nop) + return 9 + } + } + + // Pattern 4: 7-byte pattern + if length > cur+7 && cur+7 < endPC { + code0 := ByteCode(code[cur+0]) + code2 := ByteCode(code[cur+2]) + code4 := ByteCode(code[cur+4]) + code6 := ByteCode(code[cur+6]) + code7 := ByteCode(code[cur+7]) + + if code0 == PUSH1 && code2 == PUSH1 && code4 == PUSH1 && code6 == SHL && code7 == SUB { + op := Push1Push1Push1SHLSub + code[cur] = byte(op) + code[cur+2] = byte(Nop) + code[cur+4] = byte(Nop) + code[cur+6] = byte(Nop) + code[cur+7] = byte(Nop) + return 7 + } + } + + // Pattern 5: 5-byte pattern + if length > cur+5 && cur+5 < endPC { + code0 := ByteCode(code[cur+0]) + code1 := ByteCode(code[cur+1]) + code2 := ByteCode(code[cur+2]) + code3 := ByteCode(code[cur+3]) + code4 := ByteCode(code[cur+4]) + code5 := ByteCode(code[cur+5]) + + if code0 == AND && code1 == DUP2 && code2 == ADD && code3 == SWAP1 && code4 == DUP2 && code5 == LT { + op := AndDup2AddSwap1Dup2LT + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + code[cur+4] = byte(Nop) + code[cur+5] = byte(Nop) + return 5 + } + + if code0 == SUB && code1 == SLT && code2 == ISZERO && code3 == PUSH2 { + op := SubSLTIsZeroPush2 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + return 5 + } + + if code0 == DUP11 && code1 == MUL && code2 == DUP3 && code3 == SUB && code4 == MUL && code5 == DUP1 { + op := Dup11MulDup3SubMulDup1 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + code[cur+4] = byte(Nop) + code[cur+5] = byte(Nop) + return 5 + } + } + + // Pattern 6: 4-byte pattern + if length > cur+4 && cur+4 < endPC { + code0 := ByteCode(code[cur+0]) + code1 := ByteCode(code[cur+1]) + code2 := ByteCode(code[cur+2]) + code3 := ByteCode(code[cur+3]) + code4 := ByteCode(code[cur+4]) + + if code0 == AND && code1 == SWAP1 && code2 == POP && code3 == SWAP2 && code4 == SWAP1 { + op := AndSwap1PopSwap2Swap1 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + code[cur+4] = byte(Nop) + return 4 + } + + // Test zero and Jump. target offset at code[2-3] + if code0 == ISZERO && code1 == PUSH2 && code4 == JUMPI { + op := JumpIfZero + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+4] = byte(Nop) + return 4 + } + + if code0 == DUP2 && code1 == MSTORE && code2 == PUSH1 && code4 == ADD { + op := Dup2MStorePush1Add + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+4] = byte(Nop) + return 4 + } + + if code0 == SHR && code1 == SHR && code2 == DUP1 && code3 == MUL && code4 == DUP1 { + op := SHRSHRDup1MulDup1 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + code[cur+4] = byte(Nop) + return 4 + } + } + + // Pattern 7: 3-byte pattern + if length > cur+3 && cur+3 < endPC { + code0 := ByteCode(code[cur+0]) + code1 := ByteCode(code[cur+1]) + code2 := ByteCode(code[cur+2]) + code3 := ByteCode(code[cur+3]) + + if code0 == SWAP2 && code1 == SWAP1 && code2 == POP && code3 == JUMP { + op := Swap2Swap1PopJump + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + return 3 + } + + if code0 == SWAP1 && code1 == POP && code2 == SWAP2 && code3 == SWAP1 { + op := Swap1PopSwap2Swap1 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + return 3 + } + + if code0 == POP && code1 == SWAP2 && code2 == SWAP1 && code3 == POP { + op := PopSwap2Swap1Pop + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + return 3 + } + + // push and jump + if code0 == PUSH2 && code3 == JUMP { + op := Push2Jump + code[cur] = byte(op) + code[cur+3] = byte(Nop) + return 3 + } + + if code0 == PUSH2 && code3 == JUMPI { + op := Push2JumpI + code[cur] = byte(op) + code[cur+3] = byte(Nop) + return 3 + } + + if code0 == PUSH1 && code2 == PUSH1 { + op := Push1Push1 + code[cur] = byte(op) + code[cur+2] = byte(Nop) + return 3 + } + + if code0 == ISZERO && code1 == PUSH2 { + op := IsZeroPush2 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 3 + } + + if code0 == SWAP3 && code1 == POP && code2 == POP && code3 == POP { + op := Swap3PopPopPop + code[cur] = byte(op) + code[cur+1] = byte(Nop) + code[cur+2] = byte(Nop) + code[cur+3] = byte(Nop) + return 3 + } + } + + // Pattern 8: 2-byte pattern + if length > cur+2 && cur+2 < endPC { + code0 := ByteCode(code[cur+0]) + code2 := ByteCode(code[cur+2]) + + if code0 == PUSH1 { + if code2 == ADD { + op := Push1Add + code[cur] = byte(op) + code[cur+2] = byte(Nop) + return 2 + } + if code2 == SHL { + op := Push1Shl + code[cur] = byte(op) + code[cur+2] = byte(Nop) + return 2 + } + if code2 == DUP1 { + op := Push1Dup1 + code[cur] = byte(op) + code[cur+2] = byte(Nop) + return 2 + } + } + } + + // Pattern 9: 1-byte pattern + if length > cur+1 && cur+1 < endPC { + code0 := ByteCode(code[cur+0]) + code1 := ByteCode(code[cur+1]) + + if code0 == SWAP1 && code1 == POP { + op := Swap1Pop + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 1 + } + if code0 == POP && code1 == JUMP { + op := PopJump + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 1 + } + if code0 == POP && code1 == POP { + op := Pop2 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 1 + } + if code0 == SWAP2 && code1 == SWAP1 { + op := Swap2Swap1 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 1 + } + if code0 == SWAP2 && code1 == POP { + op := Swap2Pop + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 1 + } + if code0 == DUP2 && code1 == LT { + op := Dup2LT + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 1 + } + if code0 == DUP3 && code1 == AND { + op := Dup3And + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 1 + } + if code0 == SWAP1 && code1 == DUP2 { + op := Swap1Dup2 + code[cur] = byte(op) + code[cur+1] = byte(Nop) + return 1 + } + } + + return 0 +} + +// getBlockType categorizes a basic block based on its content +func getBlockType(block BasicBlock, blocks []BasicBlock, blockIndex int) string { + if len(block.Opcodes) == 0 { + return "Empty" + } + + // Check for entry basic block (first block) + if block.StartPC == 0 { + return "entryBB" + } + + // Check for jump destination blocks (begin with JUMPDEST) + if block.IsJumpDest { + return "JumpDest" + } + + // Check for conditional fallthrough (previous block ends with JUMPI) + if blockIndex > 0 { + prevBlock := blocks[blockIndex-1] + if len(prevBlock.Opcodes) > 0 { + lastOp := ByteCode(prevBlock.Opcodes[len(prevBlock.Opcodes)-1]) + if lastOp == JUMPI { + return "conditional fallthrough" + } + } + } + + // Default categorization + return "others" +} + +func calculateSkipSteps(code []byte, cur int) (skip bool, steps int) { + inst := ByteCode(code[cur]) + if inst >= PUSH1 && inst <= PUSH32 { + // skip the data. + steps = int(inst - PUSH1 + 1) + skip = true + return skip, steps + } + + switch inst { + case Push2Jump, Push2JumpI: + steps = 3 + skip = true + case Push1Push1: + steps = 3 + skip = true + case Push1Add, Push1Shl, Push1Dup1: + steps = 2 + skip = true + case JumpIfZero: + steps = 4 + skip = true + default: + return false, 0 + } + return skip, steps +} + +// BasicBlock represents a sequence of opcodes that can be executed linearly +// without any jumps in or out except at the beginning and end. +type BasicBlock struct { + StartPC uint64 // Program counter where this block starts + EndPC uint64 // Program counter where this block ends (exclusive) + Opcodes []byte // The actual opcodes in this block + JumpTarget *uint64 // If this block ends with a jump, the target PC + IsJumpDest bool // Whether this block starts with a JUMPDEST +} + +// GenerateBasicBlocks takes a byte array of opcodes and returns an array of BasicBlocks. +// This function parses the opcodes to identify basic blocks - sequences of instructions +// that can be executed linearly without jumps in the middle. +func GenerateBasicBlocks(code []byte) []BasicBlock { + if len(code) == 0 { + return nil + } + + var blocks []BasicBlock + jumpDests := make(map[uint64]bool) + var pc uint64 + + // First pass: identify all JUMPDEST locations + for pc < uint64(len(code)) { + op := ByteCode(code[pc]) + if op == JUMPDEST { + jumpDests[pc] = true + } + skip, steps := calculateSkipSteps(code, int(pc)) + if skip { + pc += uint64(steps) + 1 // Add 1 for the opcode byte + } else { + pc++ + } + } + + // Second pass: build basic blocks + pc = 0 + var currentBlock *BasicBlock + for pc < uint64(len(code)) { + op := ByteCode(code[pc]) + + // Start a new block if we encounter INVALID or if we're at a JUMPDEST + if op == INVALID || jumpDests[pc] { + if currentBlock != nil && len(currentBlock.Opcodes) > 0 { + currentBlock.EndPC = pc + blocks = append(blocks, *currentBlock) + } + currentBlock = &BasicBlock{ + StartPC: pc, + IsJumpDest: op == JUMPDEST, // Fix: set IsJumpDest if first opcode is JUMPDEST + } + } else if currentBlock == nil { + currentBlock = &BasicBlock{ + StartPC: pc, + IsJumpDest: op == JUMPDEST, // Fix: set IsJumpDest if first opcode is JUMPDEST + } + } + + // Determine instruction length + skip, steps := calculateSkipSteps(code, int(pc)) + instLen := uint64(1) + if skip { + instLen += uint64(steps) + } + // Check bounds before accessing + if pc+instLen > uint64(len(code)) { + // If we can't read the full instruction, just add what we can + instLen = uint64(len(code)) - pc + } + // Add instruction bytes to block + currentBlock.Opcodes = append(currentBlock.Opcodes, code[pc:pc+instLen]...) + pc += instLen + + // If this is a block terminator (other than INVALID since we already handled it), end the block + if isBlockTerminator(op) { + currentBlock.EndPC = pc + blocks = append(blocks, *currentBlock) + currentBlock = nil + } + } + // If there's a block in progress, add it + if currentBlock != nil && len(currentBlock.Opcodes) > 0 { + currentBlock.EndPC = pc + blocks = append(blocks, *currentBlock) + } + return blocks +} + +// isBlockTerminator checks if an opcode terminates a basic block +func isBlockTerminator(op ByteCode) bool { + switch op { + case STOP, RETURN, REVERT, SELFDESTRUCT: + return true + case JUMP, JUMPI: + return true + case RJUMP, RJUMPI, RJUMPV: + return true + case CALLF, RETF, JUMPF: + return true + default: + return false + } +} diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index d1af413455..d7250128d5 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -418,6 +418,18 @@ func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) { } } +// WriteHeaderRLP stores a RLP encoded block header into the database and also stores the +// hash-to-number mapping. +func WriteHeaderRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, header rlp.RawValue) { + // Write the hash -> number mapping + WriteHeaderNumber(db, hash, number) + + key := headerKey(number, hash) + if err := db.Put(key, header); err != nil { + log.Crit("Failed to store header", "err", err) + } +} + // DeleteHeader removes all block header data associated with a hash. func DeleteHeader(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { deleteHeaderWithoutNumber(db, hash, number) @@ -576,6 +588,13 @@ func WriteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64, td *big.I } } +// WriteTd stores the rlp encoded total difficulty of a block into the database. +func WriteTdRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, td rlp.RawValue) { + if err := db.Put(headerTDKey(number, hash), td); err != nil { + log.Crit("Failed to store block total difficulty", "err", err) + } +} + // DeleteTd removes all block total difficulty data associated with a hash. func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { if err := db.Delete(headerTDKey(number, hash)); err != nil { @@ -913,6 +932,14 @@ func ReadBlobSidecars(db ethdb.Reader, hash common.Hash, number uint64) types.Bl return ret } +// WriteBlobSidecarsRLP stores all the RLP encoded transaction blobs belonging to a block. +// It could input nil for empty blobs. +func WriteBlobSidecarsRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, blobs rlp.RawValue) { + if err := db.Put(blockBlobSidecarsKey(number, hash), blobs); err != nil { + log.Crit("Failed to store block blobs", "err", err) + } +} + // WriteBlobSidecars stores all the transaction blobs belonging to a block. // It could input nil for empty blobs. func WriteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64, blobs types.BlobSidecars) { @@ -933,6 +960,47 @@ func DeleteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64 } } +// ReadBALRLP retrieves all the block access list belonging to a block in RLP encoding. +func ReadBALRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { + // BAL is only in kv DB, will not be put into ancient DB + data, _ := db.Get(blockBALKey(number, hash)) + return data +} + +// ReadBAL retrieves the block access list belonging to a block. +func ReadBAL(db ethdb.Reader, hash common.Hash, number uint64) *types.BlockAccessListEncode { + data := ReadBALRLP(db, hash, number) + if len(data) == 0 { + return nil + } + var ret types.BlockAccessListEncode + if err := rlp.DecodeBytes(data, &ret); err != nil { + log.Error("Invalid BAL RLP", "hash", hash, "err", err) + return nil + } + return &ret +} + +func WriteBAL(db ethdb.KeyValueWriter, hash common.Hash, number uint64, bal *types.BlockAccessListEncode) { + if bal == nil { + return + } + data, err := rlp.EncodeToBytes(bal) + if err != nil { + log.Crit("Failed to encode block BAL", "err", err) + } + + if err := db.Put(blockBALKey(number, hash), data); err != nil { + log.Crit("Failed to store block BAL", "err", err) + } +} + +func DeleteBAL(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + if err := db.Delete(blockBALKey(number, hash)); err != nil { + log.Crit("Failed to delete block BAL", "err", err) + } +} + // WriteAncientHeaderChain writes the supplied headers along with nil block // bodies and receipts into the ancient store. It's supposed to be used for // storing chain segment before the chain cutoff. @@ -975,6 +1043,7 @@ func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { DeleteBody(db, hash, number) DeleteTd(db, hash, number) DeleteBlobSidecars(db, hash, number) // it is safe to delete non-exist blob + DeleteBAL(db, hash, number) } // DeleteBlockWithoutNumber removes all block data associated with a hash, except @@ -985,6 +1054,7 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number DeleteBody(db, hash, number) DeleteTd(db, hash, number) DeleteBlobSidecars(db, hash, number) + DeleteBAL(db, hash, number) } const badBlockToKeep = 10 diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index bc4f45a364..9b7cd9ae12 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -1122,3 +1122,376 @@ func TestHeadersRLPStorage(t *testing.T) { checkSequence(1, 1) // Only block 1 checkSequence(1, 2) // Genesis + block 1 } + +// Tests BAL (Block Access List) storage and retrieval operations. +func TestBALStorage(t *testing.T) { + db := NewMemoryDatabase() + + // Create test BAL data + bal := &types.BlockAccessListEncode{ + Version: 1, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{ + { + TxIndex: 0, + Address: common.HexToAddress("0x1234567890123456789012345678901234567890"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + {Key: common.HexToHash("0x02"), TxIndex: 1, Dirty: true}, + }, + }, + { + TxIndex: 1, + Address: common.HexToAddress("0x2222222222222222222222222222222222222222"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x03"), TxIndex: 2, Dirty: false}, + }, + }, + }, + } + + // Fill SignData with test data + copy(bal.SignData, []byte("test_signature_data_for_bal_testing_12345678901234567890123456789")) + + hash := common.HexToHash("0x123456789abcdef") + number := uint64(42) + + // Test non-existent BAL retrieval + if entry := ReadBAL(db, hash, number); entry != nil { + t.Fatalf("Non-existent BAL returned: %v", entry) + } + if entry := ReadBALRLP(db, hash, number); len(entry) != 0 { + t.Fatalf("Non-existent raw BAL returned: %v", entry) + } + + // Test BAL storage and retrieval + WriteBAL(db, hash, number, bal) + if entry := ReadBAL(db, hash, number); entry == nil { + t.Fatalf("Stored BAL not found") + } else if !balEqual(entry, bal) { + t.Fatalf("Retrieved BAL mismatch: have %v, want %v", entry, bal) + } + + // Test raw BAL retrieval + if entry := ReadBALRLP(db, hash, number); len(entry) == 0 { + t.Fatalf("Stored raw BAL not found") + } + + // Test BAL deletion + DeleteBAL(db, hash, number) + if entry := ReadBAL(db, hash, number); entry != nil { + t.Fatalf("Deleted BAL still returned: %v", entry) + } + if entry := ReadBALRLP(db, hash, number); len(entry) != 0 { + t.Fatalf("Deleted raw BAL still returned: %v", entry) + } +} + +func TestBALRLPStorage(t *testing.T) { + db := NewMemoryDatabase() + + // Test different BAL configurations + testCases := []struct { + name string + bal *types.BlockAccessListEncode + hash common.Hash + number uint64 + }{ + { + name: "empty accounts", + bal: &types.BlockAccessListEncode{ + Version: 0, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{}, + }, + hash: common.HexToHash("0x1111"), + number: 1, + }, + { + name: "single account with multiple storage items", + bal: &types.BlockAccessListEncode{ + Version: 2, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{ + { + TxIndex: 0, + Address: common.HexToAddress("0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x0a"), TxIndex: 0, Dirty: true}, + {Key: common.HexToHash("0x0b"), TxIndex: 1, Dirty: false}, + {Key: common.HexToHash("0x0c"), TxIndex: 2, Dirty: true}, + }, + }, + }, + }, + hash: common.HexToHash("0x2222"), + number: 2, + }, + { + name: "multiple accounts", + bal: &types.BlockAccessListEncode{ + Version: ^uint32(0), // Max uint32 value + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{ + { + TxIndex: 0, + Address: common.HexToAddress("0x1111111111111111111111111111111111111111"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + }, + }, + { + TxIndex: 1, + Address: common.HexToAddress("0x3333333333333333333333333333333333333333"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x04"), TxIndex: 3, Dirty: true}, + {Key: common.HexToHash("0x05"), TxIndex: 4, Dirty: false}, + }, + }, + { + TxIndex: 2, + Address: common.HexToAddress("0x4444444444444444444444444444444444444444"), + StorageItems: []types.StorageAccessItem{}, + }, + }, + }, + hash: common.HexToHash("0x3333"), + number: 100, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Fill SignData with unique test data + sigData := fmt.Sprintf("test_signature_for_%s_123456789012345678901234567890123456789012345678901234567890", tc.name) + copy(tc.bal.SignData, []byte(sigData)) + + // Store BAL + WriteBAL(db, tc.hash, tc.number, tc.bal) + + // Test RLP retrieval + rawData := ReadBALRLP(db, tc.hash, tc.number) + if len(rawData) == 0 { + t.Fatalf("Failed to store/retrieve raw BAL data") + } + + // Test structured retrieval + retrieved := ReadBAL(db, tc.hash, tc.number) + if retrieved == nil { + t.Fatalf("Failed to retrieve structured BAL") + } + + // Compare values + if !balEqual(retrieved, tc.bal) { + t.Fatalf("Retrieved BAL doesn't match stored BAL") + } + + // Test deletion + DeleteBAL(db, tc.hash, tc.number) + if ReadBAL(db, tc.hash, tc.number) != nil { + t.Fatalf("BAL not properly deleted") + } + }) + } +} + +func TestBALCorruptedData(t *testing.T) { + db := NewMemoryDatabase() + hash := common.HexToHash("0x9999") + number := uint64(123) + + // Store corrupted RLP data directly + corruptedData := []byte{0xff, 0xff, 0xff, 0xff} // Invalid RLP + if err := db.Put(blockBALKey(number, hash), corruptedData); err != nil { + t.Fatalf("Failed to store corrupted data: %v", err) + } + + // ReadBALRLP should return the corrupted data + rawData := ReadBALRLP(db, hash, number) + if !bytes.Equal(rawData, corruptedData) { + t.Fatalf("ReadBALRLP should return raw data even if corrupted") + } + + // ReadBAL should return nil for corrupted data + bal := ReadBAL(db, hash, number) + if bal != nil { + t.Fatalf("ReadBAL should return nil for corrupted data, got: %v", bal) + } +} + +func TestBALLargeData(t *testing.T) { + db := NewMemoryDatabase() + + // Create BAL with large amount of data + accounts := make([]types.AccountAccessListEncode, 1000) + for i := 0; i < 1000; i++ { + storageItems := make([]types.StorageAccessItem, 10) + for j := 0; j < 10; j++ { + storageItems[j] = types.StorageAccessItem{ + Key: common.BigToHash(big.NewInt(int64(i*10 + j))), + TxIndex: uint32(i*10 + j), + Dirty: (i+j)%2 == 0, + } + } + accounts[i] = types.AccountAccessListEncode{ + TxIndex: uint32(i), + Address: common.BigToAddress(big.NewInt(int64(i))), + StorageItems: storageItems, + } + } + + bal := &types.BlockAccessListEncode{ + Version: 12345, + SignData: make([]byte, 65), + Accounts: accounts, + } + + // Fill SignData + copy(bal.SignData, []byte("large_data_test_signature_123456789012345678901234567890123456789")) + + hash := common.HexToHash("0xaaaa") + number := uint64(999) + + // Test storage and retrieval of large data + WriteBAL(db, hash, number, bal) + + retrieved := ReadBAL(db, hash, number) + if retrieved == nil { + t.Fatalf("Failed to retrieve large BAL data") + } + + if !balEqual(retrieved, bal) { + t.Fatalf("Large BAL data integrity check failed") + } + + // Test deletion + DeleteBAL(db, hash, number) + if ReadBAL(db, hash, number) != nil { + t.Fatalf("Large BAL data not properly deleted") + } +} + +func TestBALMultipleBlocks(t *testing.T) { + db := NewMemoryDatabase() + + // Store BALs for multiple blocks + blocks := []struct { + hash common.Hash + number uint64 + bal *types.BlockAccessListEncode + }{ + { + hash: common.HexToHash("0xaaaa"), + number: 1, + bal: &types.BlockAccessListEncode{ + Version: 1, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{ + { + TxIndex: 0, + Address: common.HexToAddress("0x1111111111111111111111111111111111111111"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x01"), TxIndex: 0, Dirty: false}, + }, + }, + }, + }, + }, + { + hash: common.HexToHash("0xbbbb"), + number: 2, + bal: &types.BlockAccessListEncode{ + Version: 2, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{ + { + TxIndex: 0, + Address: common.HexToAddress("0x2222222222222222222222222222222222222222"), + StorageItems: []types.StorageAccessItem{ + {Key: common.HexToHash("0x02"), TxIndex: 1, Dirty: true}, + }, + }, + }, + }, + }, + { + hash: common.HexToHash("0xcccc"), + number: 3, + bal: &types.BlockAccessListEncode{ + Version: 3, + SignData: make([]byte, 65), + Accounts: []types.AccountAccessListEncode{}, + }, + }, + } + + // Store all BALs + for i, block := range blocks { + sigData := fmt.Sprintf("signature_for_block_%d_123456789012345678901234567890123456789012345678901234567890", i) + copy(block.bal.SignData, []byte(sigData)) + WriteBAL(db, block.hash, block.number, block.bal) + } + + // Verify all can be retrieved independently + for i, block := range blocks { + retrieved := ReadBAL(db, block.hash, block.number) + if retrieved == nil { + t.Fatalf("Failed to retrieve BAL for block %d", i) + } + if !balEqual(retrieved, block.bal) { + t.Fatalf("BAL mismatch for block %d", i) + } + } + + // Delete middle block + DeleteBAL(db, blocks[1].hash, blocks[1].number) + + // Verify first and third blocks still exist + if ReadBAL(db, blocks[0].hash, blocks[0].number) == nil { + t.Fatalf("Block 0 BAL was incorrectly deleted") + } + if ReadBAL(db, blocks[1].hash, blocks[1].number) != nil { + t.Fatalf("Block 1 BAL was not deleted") + } + if ReadBAL(db, blocks[2].hash, blocks[2].number) == nil { + t.Fatalf("Block 2 BAL was incorrectly deleted") + } +} + +// Helper function to compare two BlockAccessListEncode structs +func balEqual(a, b *types.BlockAccessListEncode) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil { + return false + } + if a.Version != b.Version { + return false + } + if !bytes.Equal(a.SignData, b.SignData) { + return false + } + if len(a.Accounts) != len(b.Accounts) { + return false + } + for i, accountA := range a.Accounts { + accountB := b.Accounts[i] + if accountA.TxIndex != accountB.TxIndex { + return false + } + if accountA.Address != accountB.Address { + return false + } + if len(accountA.StorageItems) != len(accountB.StorageItems) { + return false + } + for j, storageA := range accountA.StorageItems { + storageB := accountB.StorageItems[j] + if storageA.Key != storageB.Key || storageA.TxIndex != storageB.TxIndex || storageA.Dirty != storageB.Dirty { + return false + } + } + } + return true +} diff --git a/core/rawdb/accessors_increment.go b/core/rawdb/accessors_increment.go new file mode 100644 index 0000000000..a75926a509 --- /dev/null +++ b/core/rawdb/accessors_increment.go @@ -0,0 +1,435 @@ +package rawdb + +import ( + "encoding/binary" + "errors" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +// ContractCode represents a contract code with associated metadata. +type ContractCode struct { + Hash common.Hash // hash is the cryptographic hash of the contract code. + Blob []byte // blob is the binary representation of the contract code. +} + +// IncrStateMetadata represents metadata for incremental state data. +type IncrStateMetadata struct { + Root common.Hash + HasStates bool + NodeCount uint64 + StateCount uint64 + Layers uint64 + StateIDArray [2]uint64 + BlockNumberArray [2]uint64 +} + +// WriteIncrState writes the provided state data into the database. +// Compute the position of state history in freezer by minus one since the id of first state +// history starts from one(zero for initial state). +func WriteIncrTrieNodes(db ethdb.AncientWriter, id uint64, meta, trieNodes []byte) error { + _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if err := op.AppendRaw(incrStateHistoryMeta, id-1, meta); err != nil { + return err + } + if err := op.AppendRaw(incrStateHistoryTrieNodesData, id-1, trieNodes); err != nil { + return err + } + if err := op.AppendRaw(incrStateHistoryStatesData, id-1, []byte{}); err != nil { + return err + } + return nil + }) + return err +} + +// WriteIncrState writes the provided state data into the database. +// Compute the position of state history in freezer by minus one since the id of first state +// history starts from one(zero for initial state). +func WriteIncrState(db ethdb.AncientWriter, id uint64, meta, states []byte) error { + _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if err := op.AppendRaw(incrStateHistoryMeta, id-1, meta); err != nil { + return err + } + if err := op.AppendRaw(incrStateHistoryTrieNodesData, id-1, []byte{}); err != nil { + return err + } + if err := op.AppendRaw(incrStateHistoryStatesData, id-1, states); err != nil { + return err + } + return nil + }) + return err +} + +// ReadIncrStateTrieNodes retrieves the trie nodes corresponding to the specified +// state history. Compute the position of state history in freezer by minus one +// since the id of first state history starts from one(zero for initial state). +func ReadIncrStateTrieNodes(db ethdb.AncientReaderOp, id uint64) ([]byte, error) { + blob, err := db.Ancient(incrStateHistoryTrieNodesData, id-1) + if err != nil { + return nil, err + } + return blob, nil +} + +// ReadIncrStatesData retrieves the states corresponding to the specified +// state history. Compute the position of state history in freezer by minus one +// since the id of first state history starts from one(zero for initial state). +func ReadIncrStatesData(db ethdb.AncientReaderOp, id uint64) ([]byte, error) { + blob, err := db.Ancient(incrStateHistoryStatesData, id-1) + if err != nil { + return nil, err + } + return blob, nil +} + +// WriteIncrBlockData writes the provided block data to the database. +func WriteIncrBlockData(db ethdb.AncientWriter, number, stateID uint64, hash, header, body, receipts, td, sidecars []byte, + isEmptyBlock, isCancun bool) error { + _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error { + if err := op.AppendRaw(ChainFreezerHashTable, number, hash); err != nil { + return err + } + if err := op.AppendRaw(ChainFreezerHeaderTable, number, header); err != nil { + return err + } + if err := op.AppendRaw(ChainFreezerBodiesTable, number, body); err != nil { + return err + } + if err := op.AppendRaw(ChainFreezerReceiptTable, number, receipts); err != nil { + return err + } + if err := op.AppendRaw(ChainFreezerDifficultyTable, number, td); err != nil { + return err + } + if err := op.AppendRaw(IncrBlockStateIDMappingTable, number, encodeBlockNumber(stateID)); err != nil { + return err + } + if err := op.AppendRaw(IncrEmptyBlockTable, number, boolToBytes(isEmptyBlock)); err != nil { + return err + } + if isCancun { + if err := op.AppendRaw(ChainFreezerBlobSidecarTable, number, sidecars); err != nil { + return err + } + } + return nil + }) + return err +} + +// ReadIncrBlock read the block data with the provided block number. +func ReadIncrBlock(db ethdb.AncientReaderOp, number uint64) ([]byte, []byte, []byte, []byte, []byte, error) { + hashBytes, err := ReadIncrChainHash(db, number) + if err != nil { + return nil, nil, nil, nil, nil, err + } + header, err := ReadIncrChainHeader(db, number) + if err != nil { + return nil, nil, nil, nil, nil, err + } + body, err := ReadIncrChainBodies(db, number) + if err != nil { + return nil, nil, nil, nil, nil, err + } + receipts, err := ReadIncrChainReceipts(db, number) + if err != nil { + return nil, nil, nil, nil, nil, err + } + td, err := ReadIncrChainDifficulty(db, number) + if err != nil { + return nil, nil, nil, nil, nil, err + } + return hashBytes, header, body, receipts, td, nil +} + +// FinalizeIncrementalMerge is ued to write last block data from incremental db into blockchain db. +// Blockchain metadata: head block, head hash, canonical hash, etc. +func FinalizeIncrementalMerge(db ethdb.Database, incrChainFreezer ethdb.AncientReaderOp, chainConfig *params.ChainConfig, + number uint64) error { + hashBytes, header, body, receipts, td, err := ReadIncrBlock(incrChainFreezer, number) + if err != nil { + log.Error("Failed to read incremental block", "block", number, "error", err) + return err + } + hash := common.BytesToHash(hashBytes) + + var h types.Header + if err = rlp.DecodeBytes(header, &h); err != nil { + log.Error("Failed to decode header", "block", number, "error", err) + return err + } + isCancunActive := chainConfig.IsCancun(h.Number, h.Time) + + var sidecars rlp.RawValue + if isCancunActive { + sidecars, err = ReadIncrChainBlobSideCars(incrChainFreezer, number) + if err != nil { + log.Error("Failed to read increment chain blob side car", "block", number, "error", err) + return err + } + } + + blockBatch := db.NewBatch() + + // write block data + WriteTdRLP(blockBatch, hash, number, td) + WriteBodyRLP(blockBatch, hash, number, body) + WriteHeaderRLP(blockBatch, hash, number, header) + WriteRawReceipts(blockBatch, hash, number, receipts) + if isCancunActive { + WriteBlobSidecarsRLP(blockBatch, hash, number, sidecars) + } + + // update blockchain metadata + WriteCanonicalHash(blockBatch, hash, number) + WriteHeadBlockHash(blockBatch, hash) + WriteHeadHeaderHash(blockBatch, hash) + WriteHeadFastBlockHash(blockBatch, hash) + WriteFinalizedBlockHash(blockBatch, hash) + if err = blockBatch.Write(); err != nil { + log.Error("Failed to update block metadata into disk", "error", err) + return err + } + + return nil +} + +// ReadIncrChainHash retrieves the incremental hash history from the database with the provided block number. +func ReadIncrChainHash(db ethdb.AncientReaderOp, number uint64) ([]byte, error) { + blob, err := db.Ancient(ChainFreezerHashTable, number) + if err != nil { + return nil, err + } + return blob, nil +} + +// ReadIncrChainHeader retrieves the incremental header history from the database with the provided block number. +func ReadIncrChainHeader(db ethdb.AncientReaderOp, number uint64) ([]byte, error) { + blob, err := db.Ancient(ChainFreezerHeaderTable, number) + if err != nil { + return nil, err + } + return blob, nil +} + +// ReadIncrChainBodies retrieves the incremental bodies history from the database with the provided block number. +func ReadIncrChainBodies(db ethdb.AncientReaderOp, number uint64) ([]byte, error) { + blob, err := db.Ancient(ChainFreezerBodiesTable, number) + if err != nil { + return nil, err + } + return blob, nil +} + +// ReadIncrChainReceipts retrieves the incremental receipts history from the database with the provided block number. +func ReadIncrChainReceipts(db ethdb.AncientReaderOp, number uint64) ([]byte, error) { + blob, err := db.Ancient(ChainFreezerReceiptTable, number) + if err != nil { + return nil, err + } + return blob, nil +} + +// ReadIncrChainDifficulty retrieves the incremental difficulty history from the database with the provided block number. +func ReadIncrChainDifficulty(db ethdb.AncientReaderOp, number uint64) ([]byte, error) { + blob, err := db.Ancient(ChainFreezerDifficultyTable, number) + if err != nil { + return nil, err + } + return blob, nil +} + +// ReadIncrChainBlobSideCars retrieves the incremental blob history from the database with the provided block number. +func ReadIncrChainBlobSideCars(db ethdb.AncientReaderOp, number uint64) ([]byte, error) { + blobs, err := db.Ancient(ChainFreezerBlobSidecarTable, number) + if err != nil { + return nil, err + } + return blobs, nil +} + +// ReadIncrChainMapping retrieves the state id from the incremental database with the provided block number. +func ReadIncrChainMapping(db ethdb.AncientReaderOp, number uint64) (uint64, error) { + blob, err := db.Ancient(IncrBlockStateIDMappingTable, number) + if err != nil { + return 0, err + } + id := binary.BigEndian.Uint64(blob) + return id, nil +} + +// ReadIncrStateHistoryMeta retrieves the incremental metadata corresponding to the +// specified state history. Compute the position of state history in freezer by minus +// one since the id of first state history starts from one(zero for initial state). +func ReadIncrStateHistoryMeta(db ethdb.AncientReaderOp, id uint64) *IncrStateMetadata { + blob, err := db.Ancient(incrStateHistoryMeta, id-1) + if err != nil { + return nil + } + m := new(IncrStateMetadata) + if err = rlp.DecodeBytes(blob, m); err != nil { + log.Error("Failed to decode incr state history", "error", err) + return nil + } + return m +} + +// ResetEmptyIncrChainTable resets the empty incremental chain table to the new start point. +func ResetEmptyIncrChainTable(db ethdb.AncientWriter, next uint64, isCancun bool) error { + if err := db.ResetTable(ChainFreezerHeaderTable, next, true); err != nil { + return err + } + if err := db.ResetTable(ChainFreezerHashTable, next, true); err != nil { + return err + } + if err := db.ResetTable(ChainFreezerBodiesTable, next, true); err != nil { + return err + } + if err := db.ResetTable(ChainFreezerReceiptTable, next, true); err != nil { + return err + } + if err := db.ResetTable(ChainFreezerDifficultyTable, next, true); err != nil { + return err + } + if err := db.ResetTable(IncrBlockStateIDMappingTable, next, true); err != nil { + return err + } + if err := db.ResetTable(IncrEmptyBlockTable, next, true); err != nil { + return err + } + if isCancun { + if err := db.ResetTable(ChainFreezerBlobSidecarTable, next, true); err != nil { + return err + } + } + return nil +} + +// ResetChainTable resets the chain table to the new start point. +// It's used in merging the incremental snapshot case. +func ResetChainTable(db ethdb.AncientWriter, next uint64, isCancun bool) error { + if err := db.ResetTableForIncr(ChainFreezerHeaderTable, next, true); err != nil { + return err + } + if err := db.ResetTableForIncr(ChainFreezerHashTable, next, true); err != nil { + return err + } + if err := db.ResetTableForIncr(ChainFreezerBodiesTable, next, true); err != nil { + return err + } + if err := db.ResetTableForIncr(ChainFreezerReceiptTable, next, true); err != nil { + return err + } + if err := db.ResetTableForIncr(ChainFreezerDifficultyTable, next, true); err != nil { + return err + } + if isCancun { + if err := db.ResetTableForIncr(ChainFreezerBlobSidecarTable, next, true); err != nil { + return err + } + } + return nil +} + +// ResetStateTableToNewStartPoint resets the entire state tables and sets a new start point for an empty state freezer. +func ResetStateTableToNewStartPoint(db ethdb.ResettableAncientStore, startPoint uint64) error { + if err := db.Reset(); err != nil { + log.Error("Failed to reset state freezer", "error", err) + return err + } + + if err := db.ResetTableForIncr(stateHistoryMeta, startPoint, true); err != nil { + return err + } + if err := db.ResetTableForIncr(stateHistoryAccountIndex, startPoint, true); err != nil { + return err + } + if err := db.ResetTableForIncr(stateHistoryStorageIndex, startPoint, true); err != nil { + return err + } + if err := db.ResetTableForIncr(stateHistoryAccountData, startPoint, true); err != nil { + return err + } + if err := db.ResetTableForIncr(stateHistoryStorageData, startPoint, true); err != nil { + return err + } + + log.Info("Successfully set state freezer start point", "startPoint", startPoint) + return nil +} + +// GetChainConfig reads chain config from db. +func GetChainConfig(db ethdb.Reader) (*params.ChainConfig, error) { + genesisHash := ReadCanonicalHash(db, 0) + if genesisHash == (common.Hash{}) { + return nil, errors.New("genesis hash not found") + } + + chainConfig := ReadChainConfig(db, genesisHash) + if chainConfig == nil { + return nil, errors.New("chain config not found") + } + + return chainConfig, nil +} + +// CheckIncrSnapshotComplete check the incr snapshot is complete for force kill or graceful kill +// True is graceful kill, false is force kill. +func CheckIncrSnapshotComplete(incrDir string) (bool, error) { + cf, err := OpenIncrChainFreezer(incrDir, true) + if err != nil { + if strings.Contains(err.Error(), "garbage data bytes") { + return false, nil + } + return false, err + } + defer cf.Close() + sf, err := OpenIncrStateFreezer(incrDir, true) + if err != nil { + if strings.Contains(err.Error(), "garbage data bytes") { + return false, nil + } + return false, err + } + defer sf.Close() + + chainAncients, err := cf.Ancients() + if err != nil { + return false, err + } + stateAncients, err := sf.Ancients() + if err != nil { + return false, err + } + if chainAncients == 0 || stateAncients == 0 { + return false, nil + } + + // Read last state metadata + m := ReadIncrStateHistoryMeta(sf, stateAncients) + if m == nil { + return false, fmt.Errorf("last incr state history not found: %d", stateAncients) + } + + if chainAncients-1 != m.BlockNumberArray[1] { + return false, nil + } + return true, nil +} + +func boolToBytes(b bool) []byte { + buf := make([]byte, 1) + if b { + buf[0] = 1 + } + return buf +} diff --git a/core/rawdb/ancient_scheme.go b/core/rawdb/ancient_scheme.go index 6620e9bd4c..2218836058 100644 --- a/core/rawdb/ancient_scheme.go +++ b/core/rawdb/ancient_scheme.go @@ -17,6 +17,7 @@ package rawdb import ( + "errors" "path/filepath" "github.com/ethereum/go-ethereum/ethdb" @@ -41,6 +42,12 @@ const ( // ChainFreezerBlobSidecarTable indicates the name of the freezer total blob table. ChainFreezerBlobSidecarTable = "blobs" + + // IncrBlockStateIDMappingTable indicates the mapping table between block numbers and state IDs. + IncrBlockStateIDMappingTable = "mapping" + + // IncrEmptyBlockTable indicates the block has a state transition. + IncrEmptyBlockTable = "empty" ) // chainFreezerTableConfigs configures the settings for tables in the chain freezer. @@ -55,6 +62,19 @@ var chainFreezerTableConfigs = map[string]freezerTableConfig{ } var additionTables = []string{ChainFreezerBlobSidecarTable} +// incrChainFreezerTableConfigs configures the settings for tables in the incr chain freezer. +// Hashes and difficulties don't compress well. +var incrChainFreezerTableConfigs = map[string]freezerTableConfig{ + ChainFreezerHeaderTable: {noSnappy: false, prunable: true}, + ChainFreezerHashTable: {noSnappy: true, prunable: true}, + ChainFreezerBodiesTable: {noSnappy: false, prunable: true}, + ChainFreezerReceiptTable: {noSnappy: false, prunable: true}, + ChainFreezerDifficultyTable: {noSnappy: true, prunable: true}, + ChainFreezerBlobSidecarTable: {noSnappy: false, prunable: true}, + IncrBlockStateIDMappingTable: {noSnappy: false, prunable: true}, // block number -> state id + IncrEmptyBlockTable: {noSnappy: false, prunable: true}, +} + // freezerTableConfig contains the settings for a freezer table. type freezerTableConfig struct { noSnappy bool // disables item compression @@ -71,6 +91,11 @@ const ( stateHistoryStorageIndex = "storage.index" stateHistoryAccountData = "account.data" stateHistoryStorageData = "storage.data" + + // indicates the name of the freezer incremental state history table. + incrStateHistoryMeta = "incrhistory.meta" + incrStateHistoryTrieNodesData = "trienodes.data" + incrStateHistoryStatesData = "states.data" ) // stateFreezerTableConfigs configures the settings for tables in the state freezer. @@ -82,11 +107,23 @@ var stateFreezerTableConfigs = map[string]freezerTableConfig{ stateHistoryStorageData: {noSnappy: false, prunable: true}, } +var additionIncrTables = []string{ChainFreezerHeaderTable, ChainFreezerHashTable, ChainFreezerBodiesTable, ChainFreezerReceiptTable, + ChainFreezerDifficultyTable, IncrBlockStateIDMappingTable, IncrEmptyBlockTable} + +// incrStateFreezerTableConfigs configures the settings for tables in the incr state freezer. +var incrStateFreezerTableConfigs = map[string]freezerTableConfig{ + incrStateHistoryMeta: {noSnappy: true, prunable: true}, + incrStateHistoryTrieNodesData: {noSnappy: false, prunable: true}, + incrStateHistoryStatesData: {noSnappy: false, prunable: true}, +} + // The list of identifiers of ancient stores. var ( ChainFreezerName = "chain" // the folder name of chain segment ancient store. MerkleStateFreezerName = "state" // the folder name of state history ancient store. VerkleStateFreezerName = "state_verkle" // the folder name of state history ancient store. + + IncrementalPath = "incremental" // the folder name of incremental ancient store ) // freezers the collections of all builtin freezers. @@ -108,5 +145,25 @@ func NewStateFreezer(ancientDir string, verkle bool, readOnly bool) (ethdb.Reset } else { name = filepath.Join(ancientDir, MerkleStateFreezerName) } - return newResettableFreezer(name, "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerTableConfigs) + return newResettableFreezer(name, "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerTableConfigs, false) +} + +// OpenIncrStateFreezer opens the incremental state freezer. +func OpenIncrStateFreezer(incrStateDir string, readOnly bool) (ethdb.ResettableAncientStore, error) { + if incrStateDir == "" { + return nil, errors.New("empty incr state directory") + } + + name := filepath.Join(incrStateDir, MerkleStateFreezerName) + return newResettableFreezer(name, "eth/db/incr/state", readOnly, stateHistoryTableSize, incrStateFreezerTableConfigs, true) +} + +// OpenIncrChainFreezer opens the incremental chain freezer. +func OpenIncrChainFreezer(incrChainDir string, readOnly bool) (ethdb.ResettableAncientStore, error) { + if incrChainDir == "" { + return nil, errors.New("empty incr chain directory") + } + + name := filepath.Join(incrChainDir, ChainFreezerName) + return newResettableFreezer(name, "eth/db/incr/chain", readOnly, stateHistoryTableSize, incrChainFreezerTableConfigs, true) } diff --git a/core/rawdb/ancient_utils.go b/core/rawdb/ancient_utils.go index 422c869d8d..36df5f9f1b 100644 --- a/core/rawdb/ancient_utils.go +++ b/core/rawdb/ancient_utils.go @@ -129,6 +129,33 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) { return infos, nil } +func inspectIncrFreezers(db *snapDBWrapper) ([]freezerInfo, error) { + var infos []freezerInfo + for _, freezer := range freezers { + switch freezer { + case ChainFreezerName: + info, err := inspect(ChainFreezerName, incrChainFreezerTableConfigs, db.chainFreezer) + if err != nil { + return nil, err + } + infos = append(infos, info) + + case MerkleStateFreezerName: + info, err := inspect(freezer, incrStateFreezerTableConfigs, db.stateFreezer) + if err != nil { + return nil, err + } + infos = append(infos, info) + case VerkleStateFreezerName: + continue + + default: + return nil, fmt.Errorf("unknown freezer, supported ones: %v", freezers) + } + } + return infos, nil +} + // InspectFreezerTable dumps out the index of a specific freezer table. The passed // ancient indicates the path of root ancient directory where the chain freezer can // be opened. Start and end specify the range for dumping out indexes. diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index 8fe8e5c115..6e02d63ba2 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "math/big" + "os" "sync" "sync/atomic" "time" @@ -73,6 +74,10 @@ type chainFreezer struct { freezeEnv atomic.Value blockHistory atomic.Uint64 waitEnvTimes int + + // used to reset chain freezer in the incremental case + datadir string + opener freezerOpenFunc } // newChainFreezer initializes the freezer for ancient chain segment. @@ -85,11 +90,15 @@ func newChainFreezer(datadir string, eraDir string, namespace string, readonly b var ( err error freezer ethdb.AncientStore + opener freezerOpenFunc ) if datadir == "" { freezer = NewMemoryFreezer(readonly, chainFreezerTableConfigs) } else { - freezer, err = NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerTableConfigs) + freezer, err = NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerTableConfigs, false) + opener = func() (*Freezer, error) { + return NewFreezer(datadir, namespace, readonly, freezerTableSize, chainFreezerTableConfigs, false) + } } if err != nil { return nil, err @@ -103,6 +112,10 @@ func newChainFreezer(datadir string, eraDir string, namespace string, readonly b eradb: edb, quit: make(chan struct{}), trigger: make(chan chan struct{}), + datadir: datadir, + } + if opener != nil { + cf.opener = opener } // After enabling pruneAncient, the ancient data is not retained. In some specific scenarios where it is // necessary to roll back to blocks prior to the finalized block, it is mandatory to keep the most recent 90,000 blocks in the database to ensure proper functionality and rollback capability. @@ -116,7 +129,7 @@ func resetFreezerMeta(datadir string, namespace string, legacyOffset uint64) err return nil } - freezer, err := NewFreezer(datadir, namespace, false, freezerTableSize, chainFreezerTableConfigs) + freezer, err := NewFreezer(datadir, namespace, false, freezerTableSize, chainFreezerTableConfigs, false) if err != nil { return err } @@ -747,3 +760,89 @@ func trySlowdownFreeze(head *types.Header) { log.Info("Freezer need to slow down", "number", head.Number, "time", head.Time, "new", SlowFreezerBatchLimit) freezerBatchLimit = SlowFreezerBatchLimit } + +func (f *chainFreezer) getAllHashes(nfdb *nofreezedb, number, limit uint64) ([]common.Hash, error) { + lastHash := ReadCanonicalHash(nfdb, limit) + if lastHash == (common.Hash{}) { + return nil, fmt.Errorf("canonical hash missing, can't freeze block %d", limit) + } + + hashes := make([]common.Hash, 0, limit-number+1) + for ; number <= limit; number++ { + // Retrieve all the components of the canonical block. + hash := ReadCanonicalHash(nfdb, number) + if hash == (common.Hash{}) { + return nil, fmt.Errorf("canonical hash missing, can't freeze block %d", number) + } + hashes = append(hashes, hash) + } + return hashes, nil +} + +// CleanBlock clean block data in pebble and chain freezer, except genesis block. +func (f *chainFreezer) CleanBlock(kvStore ethdb.KeyValueStore, start uint64) error { + log.Info("Start cleaning old blocks") + nfdb := &nofreezedb{KeyValueStore: kvStore} + frozen, _ := f.Ancients() // no error will occur, safe to ignore + head := f.readHeadNumber(nfdb) + + first := frozen + last := head + hashes, err := f.getAllHashes(nfdb, first, last) + if err != nil { + log.Error("Failed to freeze block forcefully", "error", err) + return err + } + // Wipe out all data from the active database + batch := kvStore.NewBatch() + for i := 0; i < len(hashes); i++ { + // Always keep the genesis block in the active database + if first+uint64(i) != 0 { + DeleteBlockWithoutNumber(batch, hashes[i], first+uint64(i)) + DeleteCanonicalHash(batch, first+uint64(i)) + } + } + if err = batch.Write(); err != nil { + log.Crit("Failed to delete frozen canonical blocks", "error", err) + } + batch.Reset() + + if err = f.resetToNewStartPoint(start); err != nil { + log.Error("Failed to reset frozen blocks", "error", err) + return err + } + + log.Info("Finished cleaning blocks", "num", len(hashes)) + return nil +} + +func (f *chainFreezer) resetToNewStartPoint(start uint64) error { + if err := cleanup(f.datadir); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + tmp := tmpName(f.datadir) + if err := os.Rename(f.datadir, tmp); err != nil { + return err + } + if err := os.RemoveAll(tmp); err != nil { + return err + } + freezer, err := f.opener() + if err != nil { + return err + } + f.ancients = freezer + + if err = ResetChainTable(f, start, false); err != nil { + log.Error("Failed to reset chain freezer to the start point", "error", err, "start", start) + return err + } + return nil +} + +func (f *chainFreezer) ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error { + return f.ancients.ResetTableForIncr(kind, startAt, onlyEmpty) +} diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 3192ff3542..9af890216e 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -178,6 +178,10 @@ func (db *nofreezedb) ResetTable(kind string, startAt uint64, onlyEmpty bool) er return errNotSupported } +func (db *nofreezedb) ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error { + return errNotSupported +} + // SyncAncient returns an error as we don't have a backing chain freezer. func (db *nofreezedb) SyncAncient() error { return errNotSupported @@ -229,6 +233,9 @@ func (db *nofreezedb) AncientDatadir() (string, error) { func (db *nofreezedb) SetupFreezerEnv(env *ethdb.FreezerEnv, blockHistory uint64) error { return nil } +func (db *nofreezedb) CleanBlock(ethdb.KeyValueStore, uint64) error { + return nil +} // NewDatabase creates a high level database on top of a given key-value data // store without a freezer moving immutable chain segments into cold storage. @@ -290,6 +297,10 @@ func (db *emptyfreezedb) ResetTable(kind string, startAt uint64, onlyEmpty bool) return nil } +func (db *emptyfreezedb) ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error { + return nil +} + // SyncAncient returns nil for pruned db that we don't have a backing chain freezer. func (db *emptyfreezedb) SyncAncient() error { return nil @@ -310,6 +321,9 @@ func (db *emptyfreezedb) AncientDatadir() (string, error) { func (db *emptyfreezedb) SetupFreezerEnv(env *ethdb.FreezerEnv, blockHistory uint64) error { return nil } +func (db *emptyfreezedb) CleanBlock(ethdb.KeyValueStore, uint64) error { + return nil +} // NewEmptyFreezeDB is used for CLI such as `geth db inspect` in pruned db that we don't // have a backing chain freezer. @@ -638,6 +652,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { tds stat numHashPairings stat blobSidecars stat + bals stat hashNumPairings stat legacyTries stat stateLookups stat @@ -692,6 +707,8 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { tds.Add(size) case bytes.HasPrefix(key, BlockBlobSidecarsPrefix): blobSidecars.Add(size) + case bytes.HasPrefix(key, BlockBALPrefix): + bals.Add(size) case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix): numHashPairings.Add(size) case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength): @@ -828,6 +845,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { {"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()}, {"Key-Value store", "Difficulties", tds.Size(), tds.Count()}, {"Key-Value store", "BlobSidecars", blobSidecars.Size(), blobSidecars.Count()}, + {"Key-Value store", "Block access list", bals.Size(), bals.Count()}, {"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()}, {"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()}, {"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()}, @@ -903,6 +921,128 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { return nil } +// InspectAncients +func InspectAncients(db ethdb.Database) error { + // Totals + var ( + total common.StorageSize + stats [][]string + ) + ancients, err := inspectFreezers(db) + if err != nil { + return err + } + for _, ancient := range ancients { + for _, t := range ancient.sizes { + stats = append(stats, []string{ + fmt.Sprintf("Ancient store (%s)", strings.Title(ancient.name)), + strings.Title(t.name), + t.size.String(), + fmt.Sprintf("%d", ancient.count()), + }) + } + total += ancient.size() + } + t := tablewriter.NewWriter(os.Stdout) + t.SetHeader([]string{"Database", "Category", "Size", "Items"}) + t.SetFooter([]string{"", "Total", total.String(), " "}) + t.AppendBulk(stats) + t.Render() + + return nil +} + +// InspectIncrStore traverses the entire incr db and checks the size +// of all different categories of data. +func InspectIncrStore(baseDir string) error { + dirs, err := GetAllIncrDirs(baseDir) + if err != nil { + return err + } + fmt.Println(dirs) + + var ( + total common.StorageSize + stats [][]string + unaccounted stat + info = incrSnapDBInfo{ + readonly: true, + namespace: "eth/db/incremental/", + offset: 0, + maxTableSize: stateHistoryTableSize, + chainTables: incrChainFreezerTableConfigs, + stateTables: incrStateFreezerTableConfigs, + blockInterval: 0, + } + ) + + complete, err := CheckIncrSnapshotComplete(dirs[len(dirs)-1].Path) + if err != nil { + return err + } + if !complete { + log.Info("Skip last incremental directory", "dir", dirs[len(dirs)-1].Path) + dirs = dirs[:len(dirs)-1] + } + + for _, dir := range dirs { + db, err := newSnapDBWrapper(dir.Path, &info) + if err != nil { + return err + } + var ( + codes, parliaSnaps stat + ) + it := db.kvDB.NewIterator(nil, nil) + for it.Next() { + var ( + key = it.Key() + size = common.StorageSize(len(key) + len(it.Value())) + ) + switch { + case bytes.HasPrefix(key, ParliaSnapshotPrefix) && len(key) == 7+common.HashLength: + parliaSnaps.Add(size) + case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength: + codes.Add(size) + default: + unaccounted.Add(size) + } + } + title := fmt.Sprintf("%s/KV store", dir.Name) + stats = append(stats, [][]string{ + {title, "Contract codes", codes.Size(), codes.Count()}, + {title, "Parlia snapshots", parliaSnaps.Size(), parliaSnaps.Count()}, + }...) + + ancients, err := inspectIncrFreezers(db) + if err != nil { + return err + } + for _, ancient := range ancients { + for _, table := range ancient.sizes { + stats = append(stats, []string{ + fmt.Sprintf("%s/%s", dir.Name, strings.Title(ancient.name)), + strings.Title(table.name), + table.size.String(), + fmt.Sprintf("%d", ancient.count()), + }) + } + total += ancient.size() + } + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Database", "Category", "Size", "Items"}) + table.SetFooter([]string{"", "Total", total.String(), " "}) + table.AppendBulk(stats) + table.Render() + + if unaccounted.size > 0 { + log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count) + } + return nil +} + func DeleteTrieState(db ethdb.Database) error { var ( it ethdb.Iterator diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go index 778d9b8cbe..a644759a6d 100644 --- a/core/rawdb/freezer.go +++ b/core/rawdb/freezer.go @@ -62,6 +62,7 @@ type Freezer struct { datadir string frozen atomic.Uint64 // Number of items already frozen tail atomic.Uint64 // Number of the first stored item in the freezer + isIncr bool // This lock synchronizes writers and the truncate operation, as well as // the "atomic" (batched) read operations. @@ -80,7 +81,7 @@ type Freezer struct { // The 'tables' argument defines the data tables. If the value of a map // entry is true, snappy compression is disabled for the table. // additionTables indicates the new add tables for freezerDB, it has some special rules. -func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]freezerTableConfig) (*Freezer, error) { +func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]freezerTableConfig, isIncr bool) (*Freezer, error) { // Create the initial freezer object var ( readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil) @@ -120,6 +121,7 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui readonly: readonly, tables: make(map[string]*freezerTable), instanceLock: lock, + isIncr: isIncr, } // Create the tables. @@ -162,7 +164,8 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui // Create the write batch. freezer.writeBatch = newFreezerBatch(freezer) - log.Info("Opened ancient database", "database", datadir, "readonly", readonly, "tail", freezer.tail.Load(), "frozen", freezer.frozen.Load()) + log.Info("Opened ancient database", "database", datadir, "readonly", readonly, "tail", freezer.tail.Load(), + "frozen", freezer.frozen.Load(), "isIncr", isIncr) return freezer, nil } @@ -316,7 +319,7 @@ func (f *Freezer) TruncateHead(items uint64) (uint64, error) { // This often happens in chain rewinds, but the blob table is special. // It has the same head, but a different tail from other tables (like bodies, receipts). // So if the chain is rewound to head below the blob's tail, it needs to reset again. - if kind != ChainFreezerBlobSidecarTable { + if kind != ChainFreezerBlobSidecarTable || (f.isIncr && slices.Contains(additionIncrTables, kind)) { return 0, err } nt, err := table.resetItems(items) @@ -457,20 +460,38 @@ func (f *Freezer) repair() error { head = min(head, table.items.Load()) continue } + // addition incremental tables only align head + if f.isIncr && slices.Contains(additionIncrTables, kind) { + if EmptyTable(table) { + continue + } + head = min(head, table.items.Load()) + prunedTail = max(prunedTail, table.itemHidden.Load()) + continue + } + head = min(head, table.items.Load()) prunedTail = max(prunedTail, table.itemHidden.Load()) } + if f.isIncr && (head == math.MaxUint64) { + head = 0 + } for kind, table := range f.tables { // try to align with exist tables, skip empty table if slices.Contains(additionTables, kind) && EmptyTable(table) { continue } + // try to align with exist tables, skip empty table + if f.isIncr && slices.Contains(additionIncrTables, kind) && EmptyTable(table) { + continue + } + err := table.truncateHead(head) if err == errTruncationBelowTail { // This often happens in chain rewinds, but the blob table is special. // It has the same head, but a different tail from other tables (like bodies, receipts). // So if the chain is rewound to head below the blob's tail, it needs to reset again. - if kind != ChainFreezerBlobSidecarTable { + if (kind != ChainFreezerBlobSidecarTable) || (f.isIncr && slices.Contains(additionIncrTables, kind)) { return err } nt, err := table.resetItems(head) @@ -566,6 +587,127 @@ func (f *Freezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error return nil } +func (f *Freezer) ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error { + if f.readonly { + return errReadOnly + } + + f.writeLock.Lock() + defer f.writeLock.Unlock() + + t, exist := f.tables[kind] + if !exist { + return errors.New("you reset a non-exist table") + } + + // if you reset a non empty table just skip + if onlyEmpty && !EmptyTable(t) { + return nil + } + + if err := f.SyncAncient(); err != nil { + return err + } + nt, err := t.resetItems(startAt) + if err != nil { + return err + } + f.tables[kind] = nt + + // repair all tables with same tail & head + if err = f.repairForIncr(); err != nil { + for _, t = range f.tables { + t.Close() + } + return err + } + f.writeBatch = newFreezerBatch(f) + log.Debug("Reset Table for incremental snapshot merge", "kind", kind, "tail", f.tables[kind].itemHidden.Load(), "frozen", f.tables[kind].items.Load()) + return nil +} + +func (f *Freezer) repairForIncr() error { + var ( + head = uint64(math.MaxUint64) + tail = uint64(0) + ) + for kind, table := range f.tables { + // addition tables only align head + if slices.Contains(additionTables, kind) { + if EmptyTable(table) { + continue + } + head = min(head, table.items.Load()) + continue + } + + // addition incremental tables only align head + if _, ok := stateFreezerTableConfigs[kind]; ok { + if EmptyTable(table) { + continue + } + head = min(head, table.items.Load()) + tail = max(tail, table.itemHidden.Load()) + continue + } + + if slices.Contains(additionIncrTables, kind) { + if EmptyTable(table) { + continue + } + head = min(head, table.items.Load()) + tail = max(tail, table.itemHidden.Load()) + continue + } + + head = min(head, table.items.Load()) + tail = max(tail, table.itemHidden.Load()) + } + if head == math.MaxUint64 { + head = 0 + } + for kind, table := range f.tables { + // try to align with exist tables, skip empty table + if slices.Contains(additionTables, kind) && EmptyTable(table) { + continue + } + // try to align with exist tables, skip empty table + _, ok := stateFreezerTableConfigs[kind] + if ok && EmptyTable(table) { + continue + } + if slices.Contains(additionIncrTables, kind) && EmptyTable(table) { + continue + } + + err := table.truncateHead(head) + if err == errTruncationBelowTail { + // This often happens in chain rewinds, but the blob table is special. + // It has the same head, but a different tail from other tables (like bodies, receipts). + // So if the chain is rewound to head below the blob's tail, it needs to reset again. + _, ok = stateFreezerTableConfigs[kind] + if (kind != ChainFreezerBlobSidecarTable) || slices.Contains(additionIncrTables, kind) || ok { + return err + } + nt, err := table.resetItems(head) + if err != nil { + return err + } + f.tables[kind] = nt + continue + } + if err != nil { + return err + } + if err := table.truncateTail(tail); err != nil { + return err + } + } + f.frozen.Store(head) + f.tail.Store(tail) + return nil +} + // resetTailMeta will reset tail meta with legacyOffset // Caution: the freezer cannot be used anymore, it will sync/close all data files func (f *Freezer) resetTailMeta(legacyOffset uint64) error { diff --git a/core/rawdb/freezer_memory.go b/core/rawdb/freezer_memory.go index f71d4c4eea..cc4921cb66 100644 --- a/core/rawdb/freezer_memory.go +++ b/core/rawdb/freezer_memory.go @@ -416,12 +416,16 @@ func (f *MemoryFreezer) Reset() error { } func (f *MemoryFreezer) TruncateTableTail(kind string, tail uint64) (uint64, error) { - //TODO implement me + // TODO implement me panic("implement me") } func (f *MemoryFreezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error { - //TODO implement me + // TODO implement me + panic("not supported") +} + +func (f *MemoryFreezer) ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error { panic("not supported") } diff --git a/core/rawdb/freezer_resettable.go b/core/rawdb/freezer_resettable.go index 8017900a47..b88ba76c06 100644 --- a/core/rawdb/freezer_resettable.go +++ b/core/rawdb/freezer_resettable.go @@ -49,12 +49,12 @@ type resettableFreezer struct { // // The reset function will delete directory atomically and re-create the // freezer from scratch. -func newResettableFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]freezerTableConfig) (*resettableFreezer, error) { +func newResettableFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]freezerTableConfig, isIncr bool) (*resettableFreezer, error) { if err := cleanup(datadir); err != nil { return nil, err } opener := func() (*Freezer, error) { - return NewFreezer(datadir, namespace, readonly, maxTableSize, tables) + return NewFreezer(datadir, namespace, readonly, maxTableSize, tables, isIncr) } freezer, err := opener() if err != nil { @@ -201,6 +201,13 @@ func (f *resettableFreezer) ResetTable(kind string, startAt uint64, onlyEmpty bo return f.freezer.ResetTable(kind, startAt, onlyEmpty) } +func (f *resettableFreezer) ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error { + f.lock.RLock() + defer f.lock.RUnlock() + + return f.freezer.ResetTableForIncr(kind, startAt, onlyEmpty) +} + // SyncAncient flushes all data tables to disk. func (f *resettableFreezer) SyncAncient() error { f.lock.RLock() diff --git a/core/rawdb/freezer_resettable_test.go b/core/rawdb/freezer_resettable_test.go index 61dc23d798..38e4ce7dee 100644 --- a/core/rawdb/freezer_resettable_test.go +++ b/core/rawdb/freezer_resettable_test.go @@ -33,7 +33,7 @@ func TestResetFreezer(t *testing.T) { {1, bytes.Repeat([]byte{1}, 2048)}, {2, bytes.Repeat([]byte{2}, 2048)}, } - f, _ := newResettableFreezer(t.TempDir(), "", false, 2048, freezerTestTableDef) + f, _ := newResettableFreezer(t.TempDir(), "", false, 2048, freezerTestTableDef, false) defer f.Close() f.ModifyAncients(func(op ethdb.AncientWriteOp) error { @@ -87,7 +87,7 @@ func TestFreezerCleanup(t *testing.T) { {2, bytes.Repeat([]byte{2}, 2048)}, } datadir := t.TempDir() - f, _ := newResettableFreezer(datadir, "", false, 2048, freezerTestTableDef) + f, _ := newResettableFreezer(datadir, "", false, 2048, freezerTestTableDef, false) f.ModifyAncients(func(op ethdb.AncientWriteOp) error { for _, item := range items { op.AppendRaw("test", item.id, item.blob) @@ -98,7 +98,7 @@ func TestFreezerCleanup(t *testing.T) { os.Rename(datadir, tmpName(datadir)) // Open the freezer again, trigger cleanup operation - f, _ = newResettableFreezer(datadir, "", false, 2048, freezerTestTableDef) + f, _ = newResettableFreezer(datadir, "", false, 2048, freezerTestTableDef, false) f.Close() if _, err := os.Lstat(tmpName(datadir)); !os.IsNotExist(err) { diff --git a/core/rawdb/freezer_table_test.go b/core/rawdb/freezer_table_test.go index 9cb46333c4..64180ef67b 100644 --- a/core/rawdb/freezer_table_test.go +++ b/core/rawdb/freezer_table_test.go @@ -51,13 +51,13 @@ func TestFreezerBasics(t *testing.T) { // Write 15 bytes 255 times, results in 85 files writeChunks(t, f, 255, 15) - //print(t, f, 0) - //print(t, f, 1) - //print(t, f, 2) + // print(t, f, 0) + // print(t, f, 1) + // print(t, f, 2) // - //db[0] = 000000000000000000000000000000 - //db[1] = 010101010101010101010101010101 - //db[2] = 020202020202020202020202020202 + // db[0] = 000000000000000000000000000000 + // db[1] = 010101010101010101010101010101 + // db[2] = 020202020202020202020202020202 for y := 0; y < 255; y++ { exp := getChunk(15, y) diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go index 7a2360ff3a..546dcc3931 100644 --- a/core/rawdb/freezer_test.go +++ b/core/rawdb/freezer_test.go @@ -116,7 +116,7 @@ func TestFreezerModifyRollback(t *testing.T) { // Reopen and check that the rolled-back data doesn't reappear. tables := map[string]freezerTableConfig{"test": {noSnappy: true}} - f2, err := NewFreezer(dir, "", false, 2049, tables) + f2, err := NewFreezer(dir, "", false, 2049, tables, false) if err != nil { t.Fatalf("can't reopen freezer after failed ModifyAncients: %v", err) } @@ -257,7 +257,7 @@ func TestFreezerReadonlyValidate(t *testing.T) { dir := t.TempDir() // Open non-readonly freezer and fill individual tables // with different amount of data. - f, err := NewFreezer(dir, "", false, 2049, tables) + f, err := NewFreezer(dir, "", false, 2049, tables, false) if err != nil { t.Fatal("can't open freezer", err) } @@ -280,7 +280,7 @@ func TestFreezerReadonlyValidate(t *testing.T) { // Re-opening as readonly should fail when validating // table lengths. - _, err = NewFreezer(dir, "", true, 2049, tables) + _, err = NewFreezer(dir, "", true, 2049, tables, false) if err == nil { t.Fatal("readonly freezer should fail with differing table lengths") } @@ -292,7 +292,7 @@ func TestFreezerConcurrentReadonly(t *testing.T) { tables := map[string]freezerTableConfig{"a": {noSnappy: true}} dir := t.TempDir() - f, err := NewFreezer(dir, "", false, 2049, tables) + f, err := NewFreezer(dir, "", false, 2049, tables, false) if err != nil { t.Fatal("can't open freezer", err) } @@ -318,7 +318,7 @@ func TestFreezerConcurrentReadonly(t *testing.T) { go func(i int) { defer wg.Done() - f, err := NewFreezer(dir, "", true, 2049, tables) + f, err := NewFreezer(dir, "", true, 2049, tables, false) if err == nil { fs[i] = f } else { @@ -341,7 +341,7 @@ func TestFreezer_AdditionTables(t *testing.T) { dir := t.TempDir() // Open non-readonly freezer and fill individual tables // with different amount of data. - f, err := NewFreezer(dir, "", false, 2049, o1o2TableDef) + f, err := NewFreezer(dir, "", false, 2049, o1o2TableDef, false) if err != nil { t.Fatal("can't open freezer", err) } @@ -367,11 +367,11 @@ func TestFreezer_AdditionTables(t *testing.T) { // check read only additionTables = []string{"a1"} - f, err = NewFreezer(dir, "", true, 2049, o1o2a1TableDef) + f, err = NewFreezer(dir, "", true, 2049, o1o2a1TableDef, false) require.NoError(t, err) require.NoError(t, f.Close()) - f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef) + f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef, false) require.NoError(t, err) frozen, _ := f.Ancients() require.NoError(t, f.ResetTable("a1", frozen, true)) @@ -414,7 +414,7 @@ func TestFreezer_AdditionTables(t *testing.T) { require.NoError(t, f.Close()) // reopen and read - f, err = NewFreezer(dir, "", true, 2049, o1o2a1TableDef) + f, err = NewFreezer(dir, "", true, 2049, o1o2a1TableDef, false) require.NoError(t, err) // recheck additional table boundary @@ -431,7 +431,7 @@ func TestFreezer_AdditionTables(t *testing.T) { func TestFreezer_ResetTailMeta_WithAdditionTable(t *testing.T) { dir := t.TempDir() - f, err := NewFreezer(dir, "", false, 2049, o1o2TableDef) + f, err := NewFreezer(dir, "", false, 2049, o1o2TableDef, false) if err != nil { t.Fatal("can't open freezer", err) } @@ -456,7 +456,7 @@ func TestFreezer_ResetTailMeta_WithAdditionTable(t *testing.T) { require.NoError(t, f.Close()) additionTables = []string{"a1"} - f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef) + f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef, false) require.NoError(t, err) frozen, _ := f.Ancients() require.NoError(t, f.ResetTable("a1", frozen, true)) @@ -480,7 +480,7 @@ func TestFreezer_ResetTailMeta_WithAdditionTable(t *testing.T) { f.Close() // check items - f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef) + f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef, false) require.NoError(t, err) _, err = f.Ancient("o1", 0) require.Error(t, err) @@ -506,7 +506,7 @@ func TestFreezer_ResetTailMeta_WithAdditionTable(t *testing.T) { func TestFreezer_ResetTailMeta_EmptyTable(t *testing.T) { dir := t.TempDir() - f, err := NewFreezer(dir, "", false, 2049, o1o2TableDef) + f, err := NewFreezer(dir, "", false, 2049, o1o2TableDef, false) if err != nil { t.Fatal("can't open freezer", err) } @@ -516,7 +516,7 @@ func TestFreezer_ResetTailMeta_EmptyTable(t *testing.T) { // try to append the ancient additionTables = []string{"a1"} - f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef) + f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef, false) require.NoError(t, err) var item = make([]byte, 1024) _, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error { @@ -537,7 +537,7 @@ func TestFreezer_ResetTailMeta_EmptyTable(t *testing.T) { require.NoError(t, err) require.NoError(t, f.Close()) - f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef) + f, err = NewFreezer(dir, "", false, 2049, o1o2a1TableDef, false) require.NoError(t, err) frozen, _ := f.Ancients() require.NoError(t, f.ResetTable("a1", frozen, true)) @@ -582,7 +582,7 @@ func newFreezerForTesting(t *testing.T, tables map[string]freezerTableConfig) (* dir := t.TempDir() // note: using low max table size here to ensure the tests actually // switch between multiple files. - f, err := NewFreezer(dir, "", false, 2049, tables) + f, err := NewFreezer(dir, "", false, 2049, tables, false) if err != nil { t.Fatal("can't open freezer", err) } @@ -656,7 +656,7 @@ func TestFreezerSuite(t *testing.T) { prunable: true, } } - f, _ := newResettableFreezer(t.TempDir(), "", false, 2048, tables) + f, _ := newResettableFreezer(t.TempDir(), "", false, 2048, tables, false) return f }) } diff --git a/core/rawdb/incr_snap_db.go b/core/rawdb/incr_snap_db.go new file mode 100644 index 0000000000..c0a94b3071 --- /dev/null +++ b/core/rawdb/incr_snap_db.go @@ -0,0 +1,605 @@ +package rawdb + +import ( + "encoding/binary" + "errors" + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strconv" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/pebble" + "github.com/ethereum/go-ethereum/log" +) + +// AsyncWriteManager defines the interface for async write manager +type AsyncWriteManager interface { + // ForceFlushStateBuffer forces all buffered incr state data to be flushed before directory switch + ForceFlushStateBuffer() error +} + +const ( + incrDirNameRegexPattern = `^incr-(\d+)-(\d+)$` + incrDirNamePattern = "incr-%d-%d" +) + +// FirstStateID is used to record the first state id in each incr snapshot +var FirstStateID = []byte("firstStateID") + +type IncrSnapDB struct { + currSnapDB *snapDBWrapper + info incrSnapDBInfo + baseDir string + currentDir string + lastBlock uint64 // last updated block number + blockCount uint64 // record the stored number of blocks in current snap db + lock sync.RWMutex + + // directory switching control + switching bool + switchCond *sync.Cond + switchMutex sync.Mutex +} + +type snapDBWrapper struct { + chainFreezer ethdb.ResettableAncientStore + stateFreezer ethdb.ResettableAncientStore + kvDB ethdb.KeyValueStore +} + +type incrSnapDBInfo struct { + readonly bool + namespace string + offset uint64 + maxTableSize uint32 + chainTables map[string]freezerTableConfig + stateTables map[string]freezerTableConfig + blockInterval uint64 // write needs to set it; 0 is used in reading data from incr db +} + +// IncrDirInfo holds information about an incremental directory +type IncrDirInfo struct { + Name string + Path string + StartBlockNum uint64 + EndBlockNum uint64 +} + +// NewIncrSnapDB creates a new incremental snap database +func NewIncrSnapDB(baseDir string, readonly bool, startBlock, blockInterval uint64) (*IncrSnapDB, error) { + info := incrSnapDBInfo{ + readonly: readonly, + namespace: "eth/db/incremental/", + maxTableSize: stateHistoryTableSize, + chainTables: incrChainFreezerTableConfigs, + stateTables: incrStateFreezerTableConfigs, + blockInterval: blockInterval, + } + + // Find the latest directory or create the first one + currentDir, err := findLatestIncrDir(baseDir, startBlock, blockInterval) + if err != nil { + return nil, err + } + + db, err := newSnapDBWrapper(currentDir, &info) + if err != nil { + return nil, err + } + + incrDB := &IncrSnapDB{ + currSnapDB: db, + info: info, + baseDir: baseDir, + currentDir: currentDir, + switching: false, + } + incrDB.switchCond = sync.NewCond(&incrDB.switchMutex) + + log.Info("New incr snap db", "baseDir", baseDir, "currentDir", currentDir, "blockInterval", blockInterval, + "startBlock", startBlock) + return incrDB, nil +} + +func newSnapDBWrapper(incrDir string, info *incrSnapDBInfo) (*snapDBWrapper, error) { + if incrDir == "" { + return &snapDBWrapper{ + chainFreezer: NewMemoryFreezer(info.readonly, incrChainFreezerTableConfigs), + stateFreezer: NewMemoryFreezer(info.readonly, incrStateFreezerTableConfigs), + kvDB: NewMemoryDatabase(), + }, nil + } + + chainPath := filepath.Join(incrDir, ChainFreezerName) + chainNamespace := fmt.Sprintf("%s%s", info.namespace, "ChainFreezer") + cFreezer, err := newResettableFreezer(chainPath, chainNamespace, info.readonly, info.maxTableSize, + info.chainTables, true) + if err != nil { + return nil, fmt.Errorf("failed to create incr chain freezer: %w", err) + } + + statePath := filepath.Join(incrDir, MerkleStateFreezerName) + stateNamespace := fmt.Sprintf("%s%s", info.namespace, "MerkleStateFreezer") + sFreezer, err := newResettableFreezer(statePath, stateNamespace, info.readonly, info.maxTableSize, + info.stateTables, true) + if err != nil { + return nil, fmt.Errorf("failed to create incr state freezer: %w", err) + } + + kvNamespace := fmt.Sprintf("%s%s", info.namespace, "kv") + db, err := pebble.New(incrDir, 10, 10, kvNamespace, info.readonly) + if err != nil { + return nil, fmt.Errorf("failed to create incr KV database: %w", err) + } + + return &snapDBWrapper{ + chainFreezer: cFreezer, + stateFreezer: sFreezer, + kvDB: NewDatabase(db), + }, nil +} + +// SetBlockCount sets the block count +func (idb *IncrSnapDB) SetBlockCount(blockCount uint64) { + idb.blockCount = blockCount +} + +// waitForSwitchComplete waits until directory switching is complete +func (idb *IncrSnapDB) waitForSwitchComplete() { + const ( + pollInterval = 100 * time.Millisecond + timeout = 60 * time.Second + ) + + deadline := time.Now().Add(timeout) + ticker := time.NewTicker(pollInterval) + defer ticker.Stop() + + for time.Now().Before(deadline) { + idb.switchMutex.Lock() + if !idb.switching { + idb.switchMutex.Unlock() + return + } + idb.switchMutex.Unlock() + + <-ticker.C + log.Debug("Waiting for directory switch to complete") + } + + // Timeout occurred + log.Error("Timeout waiting for directory switch to complete") +} + +// WriteIncrBlockData writes incremental block data and checks if directory switch is needed +func (idb *IncrSnapDB) WriteIncrBlockData(number, id uint64, hash, header, body, receipts, td, sidecars []byte, + isEmptyBlock, isCancun bool) error { + idb.lock.Lock() + defer idb.lock.Unlock() + + err := WriteIncrBlockData(idb.currSnapDB.chainFreezer, number, id, hash, header, body, receipts, td, sidecars, isEmptyBlock, isCancun) + if err != nil { + return fmt.Errorf("failed to write block %d: %w", number, err) + } + idb.blockCount++ + log.Debug("Block written to IncrDB", "blockNum", number, "currentDir", idb.currentDir, "blockCount", idb.blockCount) + + return nil +} + +// WriteIncrState writes incremental state data +func (idb *IncrSnapDB) WriteIncrTrieNodes(id uint64, meta, trieNodes []byte) error { + idb.lock.Lock() + defer idb.lock.Unlock() + + if err := WriteIncrTrieNodes(idb.currSnapDB.stateFreezer, id, meta, trieNodes); err != nil { + return fmt.Errorf("failed to write incr trie nodes: %w", err) + } + return nil +} + +// WriteIncrState writes incremental state data +func (idb *IncrSnapDB) WriteIncrState(id uint64, meta, states []byte) error { + idb.lock.Lock() + defer idb.lock.Unlock() + + if err := WriteIncrState(idb.currSnapDB.stateFreezer, id, meta, states); err != nil { + return fmt.Errorf("failed to write incr state: %w", err) + } + return nil +} + +// WriteIncrContractCodes writes contract codes +func (idb *IncrSnapDB) WriteIncrContractCodes(codes map[common.Address]ContractCode) error { + idb.waitForSwitchComplete() + + idb.lock.Lock() + defer idb.lock.Unlock() + + batch := idb.currSnapDB.kvDB.NewBatch() + for _, code := range codes { + WriteCode(batch, code.Hash, code.Blob) + } + if err := batch.Write(); err != nil { + return fmt.Errorf("failed to write incr contract codes: %w", err) + } + return nil +} + +// WriteParliaSnapshot stores parlia snapshot into pebble. +func (idb *IncrSnapDB) WriteParliaSnapshot(hash common.Hash, blob []byte) { + idb.lock.Lock() + defer idb.lock.Unlock() + + if err := idb.currSnapDB.kvDB.Put(append(ParliaSnapshotPrefix, hash[:]...), blob); err != nil { + log.Crit("Failed to write parlia snapshot", "error", err) + } +} + +func (idb *IncrSnapDB) NewIterator(prefix []byte, start []byte) ethdb.Iterator { + idb.lock.Lock() + defer idb.lock.Unlock() + + return idb.currSnapDB.kvDB.NewIterator(prefix, start) +} + +func (idb *IncrSnapDB) WriteFirstStateID(id uint64) { + idb.lock.Lock() + defer idb.lock.Unlock() + + enc := make([]byte, 8) + binary.BigEndian.PutUint64(enc, id) + if err := idb.currSnapDB.kvDB.Put(FirstStateID, enc); err != nil { + log.Crit("Failed to write first state ID", "id", id, "error", err) + } +} + +// switchToNewDirectoryWithAsyncManager performs directory switch with async write manager coordination +func (idb *IncrSnapDB) switchToNewDirectoryWithAsyncManager(blockNum uint64, asyncManager AsyncWriteManager) error { + log.Info("Starting coordinated directory switch", "blockNum", blockNum) + + // Set switching flag to block new writes + idb.switchMutex.Lock() + idb.switching = true + idb.switchMutex.Unlock() + + defer func() { + // Clear switching flag and notify waiting writers + idb.switchMutex.Lock() + idb.switching = false + idb.switchCond.Broadcast() + idb.switchMutex.Unlock() + }() + + log.Info("Force flushing all incr state data before directory switch") + if err := asyncManager.ForceFlushStateBuffer(); err != nil { + return err + } + + idb.lock.Lock() + defer idb.lock.Unlock() + + // Record the last block in old chain freezer before switching + // It's used to detect and handle empty blocks that might be skipped + if idb.currSnapDB != nil && idb.currSnapDB.chainFreezer != nil { + ancients, err := idb.currSnapDB.chainFreezer.Ancients() + if err != nil { + log.Error("Failed to get ancients from old chain freezer", "err", err) + return err + } + tail, err := idb.currSnapDB.chainFreezer.Tail() + if err != nil { + log.Error("Failed to get tail from old chain freezer", "err", err) + return err + } + + // Record the last block that was actually written to the old chain freezer + // ancients represents the count of blocks, so the last written block is ancients - 1 + idb.lastBlock = ancients - 1 + log.Info("Recorded old chain freezer state", "lastBlock", idb.lastBlock, "ancients", ancients, + "tail", tail, "switchTriggerBlock", blockNum) + } + + if err := idb.closeCurrentDatabases(); err != nil { + return err + } + + newDir := filepath.Join(idb.baseDir, fmt.Sprintf(incrDirNamePattern, blockNum, blockNum+idb.info.blockInterval-1)) + db, err := newSnapDBWrapper(newDir, &idb.info) + if err != nil { + return fmt.Errorf("failed to create new snap db wrapper in directory %s: %v", newDir, err) + } + + idb.currSnapDB = db + idb.currentDir = newDir + idb.blockCount = 0 + log.Info("Successfully completed coordinated directory switch", "newDir", newDir, + "oldLastBlock", idb.lastBlock, "startBlock", blockNum) + + return nil +} + +// closeCurrentDatabases safely closes all current databases +func (idb *IncrSnapDB) closeCurrentDatabases() error { + if idb.currSnapDB == nil { + return nil + } + + var errs []error + + // Close chain freezer + if idb.currSnapDB.chainFreezer != nil { + if err := idb.currSnapDB.chainFreezer.Close(); err != nil { + log.Error("Failed to close chain freezer", "err", err) + errs = append(errs, fmt.Errorf("chain freezer: %v", err)) + } + } + + // Close state freezer + if idb.currSnapDB.stateFreezer != nil { + if err := idb.currSnapDB.stateFreezer.Close(); err != nil { + log.Error("Failed to close state freezer", "err", err) + errs = append(errs, fmt.Errorf("state freezer: %v", err)) + } + } + + // Close KV database + if idb.currSnapDB.kvDB != nil { + if err := idb.currSnapDB.kvDB.Close(); err != nil { + log.Error("Failed to close kv db", "err", err) + errs = append(errs, fmt.Errorf("kv database: %v", err)) + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + + log.Info("All databases closed successfully") + return nil +} + +// GetChainFreezer returns the current chain freezer +func (idb *IncrSnapDB) GetChainFreezer() ethdb.ResettableAncientStore { + idb.lock.RLock() + defer idb.lock.RUnlock() + + if idb.currSnapDB != nil { + return idb.currSnapDB.chainFreezer + } + return nil +} + +// GetStateFreezer returns the current state freezer +func (idb *IncrSnapDB) GetStateFreezer() ethdb.ResettableAncientStore { + idb.lock.RLock() + defer idb.lock.RUnlock() + + if idb.currSnapDB != nil { + return idb.currSnapDB.stateFreezer + } + return nil +} + +// GetKVDB returns the current kv db +func (idb *IncrSnapDB) GetKVDB() ethdb.KeyValueStore { + idb.lock.RLock() + defer idb.lock.RUnlock() + + if idb.currSnapDB != nil { + return idb.currSnapDB.kvDB + } + return nil +} + +// GetLastBlock returns the last block number +func (idb *IncrSnapDB) GetLastBlock() uint64 { + idb.lock.RLock() + defer idb.lock.RUnlock() + + return idb.lastBlock +} + +// Full returns the last block number +func (idb *IncrSnapDB) Full() bool { + idb.lock.RLock() + defer idb.lock.RUnlock() + + return idb.info.blockInterval > 0 && idb.blockCount >= idb.info.blockInterval +} + +// Close closes the IncrDB and all underlying databases +func (idb *IncrSnapDB) Close() error { + idb.lock.Lock() + defer idb.lock.Unlock() + + log.Info("Closing IncrDB", "currentDir", idb.currentDir) + return idb.closeCurrentDatabases() +} + +// IsSwitching returns true if directory switching is in progress +func (idb *IncrSnapDB) IsSwitching() bool { + idb.switchMutex.Lock() + defer idb.switchMutex.Unlock() + + return idb.switching +} + +// CheckAndInitiateSwitch safely checks if directory switch is needed and initiates it +// Returns true if switch was initiated, false if not needed or already in progress +func (idb *IncrSnapDB) CheckAndInitiateSwitch(blockNum uint64, asyncManager AsyncWriteManager) (bool, error) { + // First check without lock (fast path) + if idb.IsSwitching() { + return false, nil + } + + // Double-checked locking to prevent race conditions + idb.switchMutex.Lock() + if idb.switching { + // Another goroutine already initiated the switch + idb.switchMutex.Unlock() + return false, nil + } + + // Check limit again with proper lock to ensure consistency + idb.lock.RLock() + limitReached := idb.info.blockInterval > 0 && idb.blockCount >= idb.info.blockInterval + idb.lock.RUnlock() + + if !limitReached { + idb.switchMutex.Unlock() + return false, nil + } + + // We need to switch and we're the first to acquire the switch lock + idb.switchMutex.Unlock() + + log.Info("Initiating directory switch", "blockNum", blockNum) + err := idb.switchToNewDirectoryWithAsyncManager(blockNum, asyncManager) + return true, err +} + +// Reset all incremental directories. +func (idb *IncrSnapDB) ResetAllIncr(block uint64) error { + idb.lock.RLock() + defer idb.lock.RUnlock() + + if err := idb.reset(block); err != nil { + return err + } + return nil +} + +// reset the specified directory and create a new snap db. +func (idb *IncrSnapDB) reset(block uint64) error { + if err := idb.closeCurrentDatabases(); err != nil { + return err + } + if err := os.RemoveAll(idb.currentDir); err != nil { + return err + } + if err := os.MkdirAll(idb.baseDir, 0755); err != nil { + return fmt.Errorf("failed to create base directory %s: %v", idb.baseDir, err) + } + + newDir := filepath.Join(idb.baseDir, fmt.Sprintf(incrDirNamePattern, block, block+idb.info.blockInterval-1)) + db, err := newSnapDBWrapper(newDir, &idb.info) + if err != nil { + return fmt.Errorf("failed to create new snap db wrapper in directory %s: %v", newDir, err) + } + + idb.currSnapDB = db + idb.currentDir = newDir + idb.blockCount = 0 + return nil +} + +func (idb *IncrSnapDB) ParseCurrDirBlockNumber() (uint64, uint64, error) { + return parseDirBlockNumber(idb.currentDir) +} + +// parseDirBlockNumber parses the start and end block number from directory path +func parseDirBlockNumber(dirPath string) (uint64, uint64, error) { + path := filepath.Base(dirPath) + pattern := regexp.MustCompile(incrDirNameRegexPattern) + matches := pattern.FindStringSubmatch(path) + if len(matches) != 3 { + return 0, 0, fmt.Errorf("invalid directory name format: %s", path) + } + + startBlock, err := strconv.ParseUint(matches[1], 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse start block from directory name %s: %v", path, err) + } + endBlock, err := strconv.ParseUint(matches[2], 10, 64) + if err != nil { + return 0, 0, fmt.Errorf("failed to parse end block from directory name %s: %v", path, err) + } + + return startBlock, endBlock, nil +} + +// findLatestIncrDir finds the latest incremental directory or creates the first one +func findLatestIncrDir(baseDir string, startBlock, blockLimit uint64) (string, error) { + if err := os.MkdirAll(baseDir, 0755); err != nil { + return "", fmt.Errorf("failed to create base directory %s: %v", baseDir, err) + } + + entries, err := os.ReadDir(baseDir) + if err != nil { + return "", fmt.Errorf("failed to read base directory %s: %v", baseDir, err) + } + + var incrDirs []IncrDirInfo + for _, entry := range entries { + if !entry.IsDir() { + continue + } + start, end, err := parseDirBlockNumber(entry.Name()) + if err != nil { + log.Warn("Invalid incremental directory name", "dir", entry.Name(), "err", err) + continue + } + incrDirs = append(incrDirs, IncrDirInfo{ + Name: entry.Name(), + Path: filepath.Join(baseDir, entry.Name()), + StartBlockNum: start, + EndBlockNum: end, + }) + } + + // If no existing directories found, create the first one + if len(incrDirs) == 0 { + firstDir := filepath.Join(baseDir, fmt.Sprintf(incrDirNamePattern, startBlock, startBlock+blockLimit-1)) + log.Info("No existing incremental directories found, creating first one", "dir", firstDir) + return firstDir, nil + } + + // Sort by block number and return the latest one + sort.Slice(incrDirs, func(i, j int) bool { + return incrDirs[i].StartBlockNum < incrDirs[j].StartBlockNum + }) + + latestDir := incrDirs[len(incrDirs)-1] + log.Info("Found latest incremental directory", "dir", latestDir.Path, "startBlockNum", latestDir.StartBlockNum, + "endBlockNum", latestDir.EndBlockNum) + return latestDir.Path, nil +} + +// GetAllIncrDirs returns all incremental directories sorted by block number +func GetAllIncrDirs(baseDir string) ([]IncrDirInfo, error) { + entries, err := os.ReadDir(baseDir) + if err != nil { + return nil, fmt.Errorf("failed to read base directory %s: %v", baseDir, err) + } + + var incrDirs []IncrDirInfo + for _, entry := range entries { + if !entry.IsDir() { + continue + } + start, end, err := parseDirBlockNumber(entry.Name()) + if err != nil { + log.Warn("Invalid incremental directory name", "dir", entry.Name(), "err", err) + continue + } + incrDirs = append(incrDirs, IncrDirInfo{ + Name: entry.Name(), + Path: filepath.Join(baseDir, entry.Name()), + StartBlockNum: start, + EndBlockNum: end, + }) + } + + // Sort by block number + sort.Slice(incrDirs, func(i, j int) bool { + return incrDirs[i].StartBlockNum < incrDirs[j].StartBlockNum + }) + return incrDirs, nil +} diff --git a/core/rawdb/incr_snap_db_test.go b/core/rawdb/incr_snap_db_test.go new file mode 100644 index 0000000000..0a38c6eaf1 --- /dev/null +++ b/core/rawdb/incr_snap_db_test.go @@ -0,0 +1,435 @@ +package rawdb + +import ( + "os" + "path/filepath" + "testing" +) + +func TestParseDirBlockNumber(t *testing.T) { + tests := []struct { + name string + dirPath string + wantStart uint64 + wantEnd uint64 + wantErr bool + expectedErr string + }{ + { + name: "valid directory path", + dirPath: "/path/to/incr-1000-1999", + wantStart: 1000, + wantEnd: 1999, + wantErr: false, + }, + { + name: "valid directory path with different numbers", + dirPath: "/some/path/incr-5000-5999", + wantStart: 5000, + wantEnd: 5999, + wantErr: false, + }, + { + name: "valid directory path with single digit", + dirPath: "/test/incr-1-9", + wantStart: 1, + wantEnd: 9, + wantErr: false, + }, + { + name: "invalid directory name format", + dirPath: "/path/to/invalid_dir", + wantErr: true, + expectedErr: "invalid directory name format: invalid_dir", + }, + { + name: "invalid directory name with wrong pattern", + dirPath: "/path/to/incr-abc-def", + wantErr: true, + expectedErr: "invalid directory name format: incr-abc-def", + }, + { + name: "invalid directory name with missing parts", + dirPath: "/path/to/incr_1000", + wantErr: true, + expectedErr: "invalid directory name format: incr_1000", + }, + { + name: "invalid start block number", + dirPath: "/path/to/incr_abc-1999", + wantErr: true, + expectedErr: "invalid directory name format: incr_abc-1999", + }, + { + name: "invalid end block number", + dirPath: "/path/to/incr-1000_def", + wantErr: true, + expectedErr: "invalid directory name format: incr-1000_def", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + start, end, err := parseDirBlockNumber(tt.dirPath) + + if tt.wantErr { + if err == nil { + t.Fatalf("parseDirBlockNumber() expected error but got none") + } + if tt.expectedErr != "" && err.Error() != tt.expectedErr { + t.Fatalf("parseDirBlockNumber() error = %v, expected %v", err.Error(), tt.expectedErr) + } + } + + if start != tt.wantStart { + t.Fatalf("parseDirBlockNumber() start = %v, want %v", start, tt.wantStart) + } + if end != tt.wantEnd { + t.Fatalf("parseDirBlockNumber() end = %v, want %v", end, tt.wantEnd) + } + }) + } +} + +func TestFindLatestIncrDir(t *testing.T) { + tests := []struct { + name string + setupDirs []string + startBlock uint64 + blockLimit uint64 + expectedDir string + wantErr bool + }{ + { + name: "no existing directories - should create first one", + setupDirs: []string{}, + startBlock: 1000, + blockLimit: 1000, + expectedDir: "incr-1000-1999", + wantErr: false, + }, + { + name: "existing directories - should return latest", + setupDirs: []string{ + "incr-1000-1999", + "incr-2000-2999", + "incr-3000-3999", + }, + startBlock: 1000, + blockLimit: 1000, + expectedDir: "incr-3000-3999", + wantErr: false, + }, + { + name: "existing directories with gaps - should return latest", + setupDirs: []string{ + "incr-1000-1999", + "incr-3000-3999", + "incr-5000-5999", + }, + startBlock: 1000, + blockLimit: 1000, + expectedDir: "incr-5000-5999", + wantErr: false, + }, + { + name: "existing directories with invalid names - should ignore invalid ones", + setupDirs: []string{ + "incr-1000-1999", + "invalid_dir", + "incr-2000-2999", + "another_invalid", + }, + startBlock: 1000, + blockLimit: 1000, + expectedDir: "incr-2000-2999", + wantErr: false, + }, + { + name: "existing directories with invalid block numbers - should ignore invalid ones", + setupDirs: []string{ + "incr-1000-1999", + "incr_abc-def", + "incr-2000-2999", + }, + startBlock: 1000, + blockLimit: 1000, + expectedDir: "incr-2000-2999", + wantErr: false, + }, + { + name: "single existing directory", + setupDirs: []string{ + "incr-1000-1999", + }, + startBlock: 1000, + blockLimit: 1000, + expectedDir: "incr-1000-1999", + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create independent test directory for each test case + testDir := t.TempDir() + + // Create setup directories + for _, dirName := range tt.setupDirs { + dirPath := filepath.Join(testDir, dirName) + if err := os.MkdirAll(dirPath, 0755); err != nil { + t.Fatalf("Failed to create setup directory %s: %v", dirPath, err) + } + } + + // Test the function + gotDir, err := findLatestIncrDir(testDir, tt.startBlock, tt.blockLimit) + + if tt.wantErr { + if err == nil { + t.Fatalf("findLatestIncrDir() expected error but got none") + } + return + } + + if err != nil { + t.Fatalf("findLatestIncrDir() unexpected error = %v", err) + } + + // Verify the result + expectedDir := filepath.Join(testDir, tt.expectedDir) + if gotDir != expectedDir { + t.Fatalf("findLatestIncrDir() = %v, want %v", gotDir, expectedDir) + } + }) + } +} + +func TestFindLatestIncrDirWithFiles(t *testing.T) { + // Test with files in directory (should be ignored) + tempDir := t.TempDir() + + // Create a file in the directory + filePath := filepath.Join(tempDir, "test_file.txt") + if err := os.WriteFile(filePath, []byte("test"), 0644); err != nil { + t.Fatalf("Failed to create test file: %v", err) + } + + // Create a valid directory + validDir := filepath.Join(tempDir, "incr-1000-1999") + if err := os.MkdirAll(validDir, 0755); err != nil { + t.Fatalf("Failed to create valid directory: %v", err) + } + + // Test that files are ignored and only directories are considered + gotDir, err := findLatestIncrDir(tempDir, 1000, 1000) + if err != nil { + t.Errorf("findLatestIncrDir() unexpected error = %v", err) + return + } + + expectedDir := filepath.Join(tempDir, "incr-1000-1999") + if gotDir != expectedDir { + t.Errorf("findLatestIncrDir() = %v, want %v", gotDir, expectedDir) + } +} + +func TestGetAllIncrDirs(t *testing.T) { + tests := []struct { + name string + setupDirs []string + expectedDirs []IncrDirInfo + wantErr bool + }{ + { + name: "no directories", + setupDirs: []string{}, + expectedDirs: []IncrDirInfo{}, + wantErr: false, + }, + { + name: "valid incremental directories", + setupDirs: []string{ + "incr-1000-1999", + "incr-2000-2999", + "incr-3000-3999", + }, + expectedDirs: []IncrDirInfo{ + {Name: "incr-1000-1999", StartBlockNum: 1000, EndBlockNum: 1999}, + {Name: "incr-2000-2999", StartBlockNum: 2000, EndBlockNum: 2999}, + {Name: "incr-3000-3999", StartBlockNum: 3000, EndBlockNum: 3999}, + }, + wantErr: false, + }, + { + name: "mixed valid and invalid directories", + setupDirs: []string{ + "incr-1000-1999", + "invalid_dir", + "incr-2000-2999", + "another_invalid", + "incr-3000-3999", + }, + expectedDirs: []IncrDirInfo{ + {Name: "incr-1000-1999", StartBlockNum: 1000, EndBlockNum: 1999}, + {Name: "incr-2000-2999", StartBlockNum: 2000, EndBlockNum: 2999}, + {Name: "incr-3000-3999", StartBlockNum: 3000, EndBlockNum: 3999}, + }, + wantErr: false, + }, + { + name: "directories with invalid block numbers", + setupDirs: []string{ + "incr-1000-1999", + "incr_abc_def", + "incr-2000-2999", + "incr-xyz-123", + }, + expectedDirs: []IncrDirInfo{ + {Name: "incr-1000-1999", StartBlockNum: 1000, EndBlockNum: 1999}, + {Name: "incr-2000-2999", StartBlockNum: 2000, EndBlockNum: 2999}, + }, + wantErr: false, + }, + { + name: "unsorted directories - should return sorted", + setupDirs: []string{ + "incr-3000-3999", + "incr-1000-1999", + "incr-2000-2999", + }, + expectedDirs: []IncrDirInfo{ + {Name: "incr-1000-1999", StartBlockNum: 1000, EndBlockNum: 1999}, + {Name: "incr-2000-2999", StartBlockNum: 2000, EndBlockNum: 2999}, + {Name: "incr-3000-3999", StartBlockNum: 3000, EndBlockNum: 3999}, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create independent test directory for each test case + testDir := t.TempDir() + + // Create setup directories + for _, dirName := range tt.setupDirs { + dirPath := filepath.Join(testDir, dirName) + if err := os.MkdirAll(dirPath, 0755); err != nil { + t.Fatalf("Failed to create setup directory %s: %v", dirPath, err) + } + } + + // Test the function + gotDirs, err := GetAllIncrDirs(testDir) + + if tt.wantErr { + if err == nil { + t.Fatalf("GetAllIncrDirs() expected error but got none") + } + return + } + if err != nil { + t.Fatalf("GetAllIncrDirs() unexpected error = %v", err) + return + } + // Verify the result + if len(gotDirs) != len(tt.expectedDirs) { + t.Fatalf("GetAllIncrDirs() returned %d directories, want %d", len(gotDirs), len(tt.expectedDirs)) + } + + // Build expected directories with full paths + expectedDirs := make([]IncrDirInfo, len(tt.expectedDirs)) + for i, expected := range tt.expectedDirs { + expectedDirs[i] = IncrDirInfo{ + Name: expected.Name, + Path: filepath.Join(testDir, expected.Name), + StartBlockNum: expected.StartBlockNum, + EndBlockNum: expected.EndBlockNum, + } + } + + verifyDirs(t, gotDirs, expectedDirs) + }) + } +} + +// verifyDirs is a helper function to verify directory information +func verifyDirs(t *testing.T, gotDirs, expectedDirs []IncrDirInfo) { + for i, gotDir := range gotDirs { + expectedDir := expectedDirs[i] + if gotDir.Name != expectedDir.Name { + t.Fatalf("GetAllIncrDirs()[%d].Name = %v, want %v", i, gotDir.Name, expectedDir.Name) + } + if gotDir.Path != expectedDir.Path { + t.Fatalf("GetAllIncrDirs()[%d].Path = %v, want %v", i, gotDir.Path, expectedDir.Path) + } + if gotDir.StartBlockNum != expectedDir.StartBlockNum { + t.Fatalf("GetAllIncrDirs()[%d].StartBlockNum = %v, want %v", i, gotDir.StartBlockNum, expectedDir.StartBlockNum) + } + if gotDir.EndBlockNum != expectedDir.EndBlockNum { + t.Fatalf("GetAllIncrDirs()[%d].EndBlockNum = %v, want %v", i, gotDir.EndBlockNum, expectedDir.EndBlockNum) + } + } +} + +func TestGetAllIncrDirsWithFiles(t *testing.T) { + // Test with files in directory (should be ignored) + tempDir := t.TempDir() + + // Create files in the directory + files := []string{"test1.txt", "test2.dat", "incr_1000_1999.txt"} + for _, fileName := range files { + filePath := filepath.Join(tempDir, fileName) + if err := os.WriteFile(filePath, []byte("test"), 0644); err != nil { + t.Fatalf("Failed to create test file %s: %v", fileName, err) + } + } + + // Create a valid directory + validDir := filepath.Join(tempDir, "incr-1000-1999") + if err := os.MkdirAll(validDir, 0755); err != nil { + t.Fatalf("Failed to create valid directory: %v", err) + } + + // Test that files are ignored and only directories are returned + gotDirs, err := GetAllIncrDirs(tempDir) + if err != nil { + t.Errorf("GetAllIncrDirs() unexpected error = %v", err) + return + } + + if len(gotDirs) != 1 { + t.Errorf("GetAllIncrDirs() returned %d directories, want 1", len(gotDirs)) + return + } + + expectedDir := IncrDirInfo{ + Name: "incr-1000-1999", + Path: filepath.Join(tempDir, "incr-1000-1999"), + StartBlockNum: 1000, + EndBlockNum: 1999, + } + + if gotDirs[0].Name != expectedDir.Name { + t.Errorf("GetAllIncrDirs()[0].Name = %v, want %v", gotDirs[0].Name, expectedDir.Name) + } + if gotDirs[0].Path != expectedDir.Path { + t.Errorf("GetAllIncrDirs()[0].Path = %v, want %v", gotDirs[0].Path, expectedDir.Path) + } + if gotDirs[0].StartBlockNum != expectedDir.StartBlockNum { + t.Errorf("GetAllIncrDirs()[0].StartBlockNum = %v, want %v", gotDirs[0].StartBlockNum, expectedDir.StartBlockNum) + } + if gotDirs[0].EndBlockNum != expectedDir.EndBlockNum { + t.Errorf("GetAllIncrDirs()[0].EndBlockNum = %v, want %v", gotDirs[0].EndBlockNum, expectedDir.EndBlockNum) + } +} + +func TestGetAllIncrDirsError(t *testing.T) { + // Test with non-existent directory + nonExistentDir := "/non/existent/directory" + _, err := GetAllIncrDirs(nonExistentDir) + if err == nil { + t.Errorf("GetAllIncrDirs() expected error for non-existent directory but got none") + } +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index ca2462f14d..beb97f5b42 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -143,6 +143,8 @@ var ( BlockBlobSidecarsPrefix = []byte("blobs") + BlockBALPrefix = []byte("bal") // blockBALPrefix + blockNumber (uint64 big endian) + blockHash -> block access list + // new log index filterMapsPrefix = "fm-" filterMapsRangeKey = []byte(filterMapsPrefix + "R") @@ -213,6 +215,11 @@ func blockBlobSidecarsKey(number uint64, hash common.Hash) []byte { return append(append(BlockBlobSidecarsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) } +// blockBALKey = blockBALPrefix + blockNumber (uint64 big endian) + blockHash +func blockBALKey(number uint64, hash common.Hash) []byte { + return append(append(BlockBALPrefix, encodeBlockNumber(number)...), hash.Bytes()...) +} + // txLookupKey = txLookupPrefix + hash func txLookupKey(hash common.Hash) []byte { return append(txLookupPrefix, hash.Bytes()...) diff --git a/core/rawdb/table.go b/core/rawdb/table.go index 5d8006ea70..f0bd02a7a6 100644 --- a/core/rawdb/table.go +++ b/core/rawdb/table.go @@ -95,6 +95,10 @@ func (t *table) ResetTable(kind string, startAt uint64, onlyEmpty bool) error { return t.db.ResetTable(kind, startAt, onlyEmpty) } +func (t *table) ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error { + return t.db.ResetTableForIncr(kind, startAt, onlyEmpty) +} + func (t *table) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) { return t.db.ReadAncients(fn) } @@ -235,6 +239,10 @@ func (t *table) SetupFreezerEnv(env *ethdb.FreezerEnv, blockHistory uint64) erro return nil } +func (t *table) CleanBlock(kvStore ethdb.KeyValueStore, start uint64) error { + return nil +} + // tableBatch is a wrapper around a database batch that prefixes each key access // with a pre-configured string. type tableBatch struct { diff --git a/core/state/state_object.go b/core/state/state_object.go index 1fbf3a1bc6..1dadc9b822 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/opcodeCompiler/compiler" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -586,6 +587,7 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) { s.code = code s.data.CodeHash = codeHash[:] s.dirtyCode = true + compiler.GenOrLoadOptimizedCode(codeHash, s.code) } func (s *stateObject) SetNonce(nonce uint64) { diff --git a/core/state/statedb.go b/core/state/statedb.go index 09803b2fd4..11744ef91e 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -138,6 +138,9 @@ type StateDB struct { accessList *accessList accessEvents *AccessEvents + // block level access list + blockAccessList *types.BlockAccessListRecord + // Transient storage transientStorage transientStorage @@ -190,6 +193,7 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro preimages: make(map[common.Hash][]byte), journal: newJournal(), accessList: newAccessList(), + blockAccessList: nil, transientStorage: newTransientStorage(), } if db.TrieDB().IsVerkle() { @@ -198,6 +202,13 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro return sdb, nil } +func (s *StateDB) InitBlockAccessList() { + if s.blockAccessList != nil { + log.Warn("prepareBAL blockAccessList is not nil") + } + s.blockAccessList = &types.BlockAccessListRecord{Accounts: make(map[common.Address]types.AccountAccessListRecord)} +} + func (s *StateDB) SetNeedBadSharedStorage(needBadSharedStorage bool) { s.needBadSharedStorage = needBadSharedStorage } @@ -375,6 +386,43 @@ func (s *StateDB) GetNonce(addr common.Address) uint64 { return 0 } +func (s *StateDB) PreloadAccount(addr common.Address) { + if s.Empty(addr) { + return + } + s.GetCode(addr) +} + +func (s *StateDB) PreloadStorage(addr common.Address, key common.Hash) { + if s.Empty(addr) { + return + } + s.GetState(addr, key) +} +func (s *StateDB) PreloadAccountTrie(addr common.Address) { + if s.prefetcher == nil { + return + } + + addressesToPrefetch := []common.Address{addr} + if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, addressesToPrefetch, nil, false); err != nil { + log.Error("Failed to prefetch addresses", "addresses", len(addressesToPrefetch), "err", err) + } +} + +func (s *StateDB) PreloadStorageTrie(addr common.Address, key common.Hash) { + if s.prefetcher == nil { + return + } + obj := s.getStateObject(addr) + if obj == nil { + return + } + if err := s.prefetcher.prefetch(obj.addrHash, obj.origin.Root, obj.address, nil, []common.Hash{key}, true); err != nil { + log.Error("Failed to prefetch storage slot", "addr", obj.address, "key", key, "err", err) + } +} + // GetStorageRoot retrieves the storage root from the given address or empty // if object not found. func (s *StateDB) GetStorageRoot(addr common.Address) common.Hash { @@ -424,6 +472,7 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash { func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash { stateObject := s.getStateObject(addr) if stateObject != nil { + s.blockAccessList.AddStorage(addr, hash, uint32(s.txIndex), false) return stateObject.GetState(hash) } return common.Hash{} @@ -506,6 +555,7 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) (prev []byte) { } func (s *StateDB) SetState(addr common.Address, key, value common.Hash) common.Hash { + s.blockAccessList.AddStorage(addr, key, uint32(s.txIndex), true) if stateObject := s.getOrNewStateObject(addr); stateObject != nil { return stateObject.SetState(key, value) } @@ -635,6 +685,7 @@ func (s *StateDB) deleteStateObject(addr common.Address) { // getStateObject retrieves a state object given by the address, returning nil if // the object is not found or was deleted in this execution context. func (s *StateDB) getStateObject(addr common.Address) *stateObject { + s.blockAccessList.AddAccount(addr, uint32(s.txIndex)) // Prefer live objects if any is available if obj := s.stateObjects[addr]; obj != nil { return obj @@ -714,6 +765,28 @@ func (s *StateDB) CreateContract(addr common.Address) { } } +// StateForPrefetch creates a mirrored StateDB instance that shares the same +// underlying state reader and cache as the current one. It is typically used +// for state prefetching. +// +// Note: If the reader implements readerWithCacheStats, a new wrapper is created +// to maintain independent cache statistics while reusing the same cache source. +func (s *StateDB) StateForPrefetch() *StateDB { + reader := s.reader + if readerWithCacheStats, ok := s.reader.(*readerWithCacheStats); ok { + reader = newReaderWithCacheStats(readerWithCacheStats.readerWithCache) + } + state, err := NewWithReader(s.originalRoot, s.db, reader) + if err != nil { + log.Error("Failed to create StateDB for prefetch", "err", err) + return nil + } + + state.prefetcher = s.prefetcher + + return state +} + // Copy creates a deep, independent copy of the state. // Snapshots of the copied state cannot be applied to the copy. func (s *StateDB) Copy() *StateDB { @@ -725,6 +798,14 @@ func (s *StateDB) CopyDoPrefetch() *StateDB { return s.copyInternal(true) } +func (s *StateDB) TransferBlockAccessList(prev *StateDB) { + if prev == nil { + return + } + s.blockAccessList = prev.blockAccessList + prev.blockAccessList = nil +} + // If doPrefetch is true, it tries to reuse the prefetcher, the copied StateDB will do active trie prefetch. // otherwise, just do inactive copy trie prefetcher. func (s *StateDB) copyInternal(doPrefetch bool) *StateDB { @@ -753,6 +834,7 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB { // empty lists, so we do it anyway to not blow up if we ever decide copy them // in the middle of a transaction. accessList: s.accessList.Copy(), + blockAccessList: nil, transientStorage: s.transientStorage.Copy(), journal: s.journal.copy(), } @@ -1419,6 +1501,20 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorag return nil, err } } + // Write dirty contract code into incremental db if any exists and incr is enabled + if db := s.db.TrieDB(); db != nil && len(ret.codes) > 0 && db.IsIncrEnabled() { + codes := make(map[common.Address]rawdb.ContractCode) + for hash, code := range ret.codes { + codes[hash] = rawdb.ContractCode{ + Hash: code.hash, + Blob: code.blob, + } + } + if err = db.WriteContractCodes(codes); err != nil { + return nil, err + } + } + if !ret.empty() { // If snapshotting is enabled, update the snapshot tree with this new version if snap := s.db.Snapshot(); snap != nil && snap.Snapshot(ret.originRoot) != nil { @@ -1595,3 +1691,53 @@ func (s *StateDB) IsAddressInMutations(addr common.Address) bool { _, ok := s.mutations[addr] return ok } + +func (s *StateDB) DumpAccessList(block *types.Block) { + if s.blockAccessList == nil { + return + } + accountCount := 0 + storageCount := 0 + dirtyStorageCount := 0 + for addr, account := range s.blockAccessList.Accounts { + accountCount++ + log.Debug(" DumpAccessList Address", "address", addr.Hex(), "txIndex", account.TxIndex) + for _, storageItem := range account.StorageItems { + log.Debug(" DumpAccessList Storage Item", "key", storageItem.Key.Hex(), "txIndex", storageItem.TxIndex, "dirty", storageItem.Dirty) + storageCount++ + if storageItem.Dirty { + dirtyStorageCount++ + } + } + } + log.Info("DumpAccessList", "blockNumber", block.NumberU64(), "GasUsed", block.GasUsed(), + "accountCount", accountCount, "storageCount", storageCount, "dirtyStorageCount", dirtyStorageCount) +} + +// GetEncodedBlockAccessList: convert BlockAccessListRecord to BlockAccessListEncode +func (s *StateDB) GetEncodedBlockAccessList(block *types.Block) *types.BlockAccessListEncode { + if s.blockAccessList == nil { + return nil + } + // encode block access list to rlp to propagate with the block + blockAccessList := types.BlockAccessListEncode{ + Version: 0, + Number: block.NumberU64(), + Hash: block.Hash(), + SignData: make([]byte, 65), + Accounts: make([]types.AccountAccessListEncode, 0), + } + for addr, account := range s.blockAccessList.Accounts { + accountAccessList := types.AccountAccessListEncode{ + TxIndex: account.TxIndex, + Address: addr, + StorageItems: make([]types.StorageAccessItem, 0), + } + for _, storageItem := range account.StorageItems { + accountAccessList.StorageItems = append(accountAccessList.StorageItems, storageItem) + } + blockAccessList.Accounts = append(blockAccessList.Accounts, accountAccessList) + } + + return &blockAccessList +} diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index b777b195bc..4f71e33cb1 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -25,11 +25,14 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "golang.org/x/sync/errgroup" ) const prefetchMiningThread = 3 +const prefetchThreadBALSnapshot = 8 +const prefetchThreadBALTrie = 8 const checkInterval = 10 // statePrefetcher is a basic Prefetcher that executes transactions from a block @@ -129,11 +132,158 @@ func (p *statePrefetcher) Prefetch(transactions types.Transactions, header *type return } +func (p *statePrefetcher) PrefetchBALSnapshot(balPrefetch *types.BlockAccessListPrefetch, block *types.Block, txSize int, statedb *state.StateDB, interruptChan <-chan struct{}) { + accChan := make(chan struct { + txIndex uint32 + accAddr common.Address + }, prefetchThreadBALSnapshot) + + keyChan := make(chan struct { + txIndex uint32 + accAddr common.Address + key common.Hash + }, prefetchThreadBALSnapshot) + + // prefetch snapshot cache + for i := 0; i < prefetchThreadBALSnapshot; i++ { + go func() { + newStatedb := statedb.CopyDoPrefetch() + for { + select { + case accAddr := <-accChan: + log.Debug("PrefetchBALSnapshot", "txIndex", accAddr.txIndex, "accAddr", accAddr.accAddr) + newStatedb.PreloadAccount(accAddr.accAddr) + case item := <-keyChan: + log.Debug("PrefetchBALSnapshot", "txIndex", item.txIndex, "accAddr", item.accAddr, "key", item.key) + newStatedb.PreloadStorage(item.accAddr, item.key) + case <-interruptChan: + return + } + } + }() + } + for txIndex := 0; txIndex < txSize; txIndex++ { + txAccessList := balPrefetch.AccessListItems[uint32(txIndex)] + for accAddr, storageItems := range txAccessList.Accounts { + select { + case accChan <- struct { + txIndex uint32 + accAddr common.Address + }{ + txIndex: uint32(txIndex), + accAddr: accAddr, + }: + case <-interruptChan: + return + } + for _, storageItem := range storageItems { + select { + case keyChan <- struct { + txIndex uint32 + accAddr common.Address + key common.Hash + }{ + txIndex: uint32(txIndex), + accAddr: accAddr, + key: storageItem.Key, + }: + case <-interruptChan: + return + } + } + } + } + log.Debug("PrefetchBALSnapshot dispatch finished") +} + +func (p *statePrefetcher) PrefetchBALTrie(balPrefetch *types.BlockAccessListPrefetch, block *types.Block, statedb *state.StateDB, interruptChan <-chan struct{}) { + accItemsChan := make(chan struct { + txIndex uint32 + accAddr common.Address + items []types.StorageAccessItemPrefetch + }, prefetchThreadBALTrie) + + for i := 0; i < prefetchThreadBALTrie; i++ { + go func() { + newStatedb := statedb.CopyDoPrefetch() + for { + select { + case accItem := <-accItemsChan: + newStatedb.PreloadAccountTrie(accItem.accAddr) + log.Debug("PrefetchBALTrie", "txIndex", accItem.txIndex, "accAddr", accItem.accAddr) + for _, storageItem := range accItem.items { + if storageItem.Dirty { + log.Debug("PrefetchBALTrie", "txIndex", accItem.txIndex, "accAddr", accItem.accAddr, "storageItem", storageItem.Key, "dirty", storageItem.Dirty) + statedb.PreloadStorageTrie(accItem.accAddr, storageItem.Key) + } + } + case <-interruptChan: + return + } + } + }() + } + + for txIndex, txAccessList := range balPrefetch.AccessListItems { + for accAddr, storageItems := range txAccessList.Accounts { + select { + case accItemsChan <- struct { + txIndex uint32 + accAddr common.Address + items []types.StorageAccessItemPrefetch + }{ + txIndex: txIndex, + accAddr: accAddr, + items: storageItems, + }: + case <-interruptChan: + log.Warn("PrefetchBALTrie interrupted") + return + } + } + } + log.Debug("PrefetchBALTrie dispatch finished") +} + +func (p *statePrefetcher) PrefetchBAL(block *types.Block, statedb *state.StateDB, interruptChan <-chan struct{}) { + if block.BAL() == nil { + return + } + transactions := block.Transactions() + blockAccessList := block.BAL() + + // get index sorted block access list, each transaction has a list of accounts, each account has a list of storage items + // txIndex 0: + // account1: storage1_1, storage1_2, storage1_3 + // account2: storage2_1, storage2_2, storage2_3 + // txIndex 1: + // account3: storage3_1, storage3_2, storage3_3 + // ... + balPrefetch := types.BlockAccessListPrefetch{ + AccessListItems: make(map[uint32]types.TxAccessListPrefetch), + } + for _, account := range blockAccessList.Accounts { + balPrefetch.Update(&account) + } + + // prefetch snapshot cache + go p.PrefetchBALSnapshot(&balPrefetch, block, len(transactions), statedb, interruptChan) + + // prefetch MPT trie node cache + go p.PrefetchBALTrie(&balPrefetch, block, statedb, interruptChan) +} + // PrefetchMining processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb, but any changes are discarded. The // only goal is to warm the state caches. Only used for mining stage. func (p *statePrefetcher) PrefetchMining(txs TransactionsByPriceAndNonce, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg vm.Config, interruptCh <-chan struct{}, txCurr **types.Transaction) { - var signer = types.MakeSigner(p.config, header.Number, header.Time) + if statedb == nil { + return + } + var ( + reader = statedb.Reader() + signer = types.MakeSigner(p.config, header.Number, header.Time) + ) txCh := make(chan *types.Transaction, 2*prefetchMiningThread) for i := 0; i < prefetchMiningThread; i++ { @@ -145,6 +295,29 @@ func (p *statePrefetcher) PrefetchMining(txs TransactionsByPriceAndNonce, header for { select { case tx := <-startCh: + // Preload the touched accounts and storage slots in advance + sender, err := types.Sender(signer, tx) + if err == nil { + reader.Account(sender) + } + + if tx.To() != nil { + account, _ := reader.Account(*tx.To()) + + // Preload the contract code if the destination has non-empty code + if account != nil && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { + reader.Code(*tx.To(), common.BytesToHash(account.CodeHash)) + } + } + for _, list := range tx.AccessList() { + reader.Account(list.Address) + if len(list.StorageKeys) > 0 { + for _, slot := range list.StorageKeys { + reader.Storage(list.Address, slot) + } + } + } + // Convert the transaction into an executable message and pre-cache its sender msg, err := TransactionToMessage(tx, signer, header.BaseFee) if err != nil { diff --git a/core/state_processor.go b/core/state_processor.go index b995b9f4e9..9d3240036d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -181,7 +181,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg for _, receipt := range receipts { allLogs = append(allLogs, receipt.Logs...) } - + statedb.DumpAccessList(block) return &ProcessResult{ Receipts: receipts, Requests: requests, diff --git a/core/systemcontracts/fermi/chapel/StakeHubContract b/core/systemcontracts/fermi/chapel/StakeHubContract new file mode 100644 index 0000000000..b32135527a --- /dev/null +++ b/core/systemcontracts/fermi/chapel/StakeHubContract @@ -0,0 +1 @@ +6080604052600436106200043b5760003560e01c806386d545061162000233578063ca47908f116200012f578063dd42a1dd11620000b9578063f1f74d841162000084578063f1f74d841462000d75578063f80a34021462000d8d578063fb50b31f1462000db2578063fc0c5ff11462000dd7578063ff69ab611462000def57600080fd5b8063dd42a1dd1462000ce2578063e8f67c3b1462000d09578063e992aaf51462000d21578063efdbf0e11462000d3957600080fd5b8063d7c2dfc811620000fa578063d7c2dfc81462000c68578063d8ca511f1462000c8d578063daacdb661462000ca5578063dbda7fb31462000cbd57600080fd5b8063ca47908f1462000bd1578063cbb04d9d1462000be9578063d115a2061462000c2a578063d6ca429d1462000c4357600080fd5b8063b187bd2611620001bd578063bfff04751162000188578063bfff04751462000b58578063c166f58a1462000b7d578063c38fbec81462000b94578063c473318f1462000bb9578063c8509d81146200095157600080fd5b8063b187bd261462000ac5578063baa7199e1462000ae5578063bdceadf31462000b0a578063bff02e201462000b2257600080fd5b8063a1832e6411620001fe578063a1832e641462000a22578063a43569b31462000a47578063aad3ec961462000a7b578063ac4317511462000aa057600080fd5b806386d54506146200098e5780638a4d3fa814620009c85780638cd22b2214620009e6578063982ef0a71462000a0b57600080fd5b80634838d165116200034357806364028fbd11620002cd57806375cc7d89116200029857806375cc7d8914620008fc57806376e7d6d614620009215780638129fc1c1462000939578063831d65d114620009515780638456cb59146200097657600080fd5b806364028fbd1462000837578063663706d3146200084e5780636ec01b27146200087f5780636f8e2fa414620008d757600080fd5b80634e6fd6c4116200030e5780634e6fd6c4146200079e5780635949187114620007b65780635e7cc1c914620007db57806363a036b5146200080057600080fd5b80634838d16514620006ea57806349f41a42146200072f5780634a49ac4c14620007545780634d99dd16146200077957600080fd5b80631fab701511620003c5578063384099881162000390578063384099881462000663578063417c73a7146200067b578063449ecfe614620006a057806345211bfd14620006c557600080fd5b80631fab701514620005aa5780632b727c8614620005cf5780632e8e8c7114620005f4578063367dad49146200062e57600080fd5b80630e9fbf5111620004065780630e9fbf5114620004f35780631182b875146200051857806317b4f353146200054c5780631fa8882b146200059157600080fd5b8063046f7da2146200045b578063059ddd2214620004735780630661806e14620004b5578063092193ab14620004dc57600080fd5b36620004565760345460ff166001146200045457600080fd5b005b600080fd5b3480156200046857600080fd5b506200045462000e07565b3480156200048057600080fd5b506200049862000492366004620096f0565b62000e99565b6040516001600160a01b0390911681526020015b60405180910390f35b348015620004c257600080fd5b50620004cd60365481565b604051908152602001620004ac565b62000454620004ed366004620096f0565b620012c1565b3480156200050057600080fd5b50620004546200051236600462009752565b62001912565b3480156200052557600080fd5b506200053d6200053736600462009797565b62001c3e565b604051620004ac91906200984a565b3480156200055957600080fd5b50620004986200056b3660046200991c565b80516020818301810180516045825292820191909301209152546001600160a01b031681565b3480156200059e57600080fd5b50620004cd6201518081565b348015620005b757600080fd5b5062000454620005c9366004620099b8565b62001cd6565b348015620005dc57600080fd5b5062000498620005ee366004620096f0565b6200202d565b3480156200060157600080fd5b506200049862000613366004620096f0565b604d602052600090815260409020546001600160a01b031681565b3480156200063b57600080fd5b50620006536200064d366004620099b8565b62002082565b604051620004ac92919062009a37565b3480156200067057600080fd5b50620004cd60375481565b3480156200068857600080fd5b50620004546200069a366004620096f0565b62002651565b348015620006ad57600080fd5b5062000454620006bf366004620096f0565b620026d3565b348015620006d257600080fd5b5062000454620006e4366004620096f0565b620028b8565b348015620006f757600080fd5b506200071e62000709366004620096f0565b60016020526000908152604090205460ff1681565b6040519015158152602001620004ac565b3480156200073c57600080fd5b50620004546200074e366004620096f0565b62002a91565b3480156200076157600080fd5b506200045462000773366004620096f0565b62002cad565b3480156200078657600080fd5b50620004546200079836600462009ad6565b62002d29565b348015620007ab57600080fd5b506200049861dead81565b348015620007c357600080fd5b5062000454620007d536600462009b14565b62003359565b348015620007e857600080fd5b5062000454620007fa36600462009b7e565b62004152565b3480156200080d57600080fd5b50620008256200081f36600462009ba5565b6200437c565b604051620004ac949392919062009bc8565b620004546200084836600462009c84565b62004a21565b3480156200085b57600080fd5b50620004cd6200086d366004620096f0565b60446020526000908152604090205481565b3480156200088c57600080fd5b50620008a46200089e366004620096f0565b62005070565b6040805182516001600160401b0390811682526020808501518216908301529282015190921690820152606001620004ac565b348015620008e457600080fd5b506200053d620008f6366004620096f0565b62005115565b3480156200090957600080fd5b50620004546200091b366004620096f0565b62005541565b3480156200092e57600080fd5b50620004cd603d5481565b3480156200094657600080fd5b506200045462005713565b3480156200095e57600080fd5b50620004546200097036600462009797565b620058de565b3480156200098357600080fd5b50620004546200593c565b3480156200099b57600080fd5b5062000498620009ad366004620096f0565b6043602052600090815260409020546001600160a01b031681565b348015620009d557600080fd5b50620004cd670de0b6b3a764000081565b348015620009f357600080fd5b50620004cd62000a0536600462009ad6565b620059d4565b6200045462000a1c36600462009d5a565b62005a8d565b34801562000a2f57600080fd5b506200045462000a41366004620099b8565b6200613b565b34801562000a5457600080fd5b5062000a6c62000a66366004620096f0565b62006441565b604051620004ac919062009d92565b34801562000a8857600080fd5b506200045462000a9a36600462009ad6565b6200672e565b34801562000aad57600080fd5b506200045462000abf36600462009e0f565b6200679b565b34801562000ad257600080fd5b5060005462010000900460ff166200071e565b34801562000af257600080fd5b506200045462000b0436600462009e81565b6200774d565b34801562000b1757600080fd5b50620004cd603c5481565b34801562000b2f57600080fd5b5062000b4762000b4136600462009ba5565b6200791a565b604051620004ac9392919062009eda565b34801562000b6557600080fd5b50620004cd62000b77366004620096f0565b62007af6565b34801562000b8a57600080fd5b50620004cd600581565b34801562000ba157600080fd5b506200045462000bb3366004620096f0565b62007b44565b34801562000bc657600080fd5b50620004cd60385481565b34801562000bde57600080fd5b50620004cd604e5481565b34801562000bf657600080fd5b5062000c0e62000c08366004620096f0565b62007e06565b60408051938452911515602084015290820152606001620004ac565b34801562000c3757600080fd5b50620004cd620186a081565b34801562000c5057600080fd5b506200045462000c6236600462009f37565b62008249565b34801562000c7557600080fd5b506200045462000c873660046200a020565b6200846f565b34801562000c9a57600080fd5b50620004cd603b5481565b34801562000cb257600080fd5b50620004cd60495481565b34801562000cca57600080fd5b506200049862000cdc366004620096f0565b62008565565b34801562000cef57600080fd5b50600054630100000090046001600160a01b031662000498565b34801562000d1657600080fd5b50620004cd60355481565b34801562000d2e57600080fd5b50620004cd603a5481565b34801562000d4657600080fd5b50620004cd62000d583660046200991c565b805160208183018101805160468252928201919093012091525481565b34801562000d8257600080fd5b50620004cd603e5481565b34801562000d9a57600080fd5b50620004cd62000dac36600462009ad6565b6200898f565b34801562000dbf57600080fd5b506200045462000dd136600462009e0f565b62008a00565b34801562000de457600080fd5b50620004cd60395481565b34801562000dfc57600080fd5b50620004cd604a5481565b600054630100000090046001600160a01b0316331462000e3a576040516306fbb1e360e01b815260040160405180910390fd5b60005462010000900460ff1662000e6457604051636cd6020160e01b815260040160405180910390fd5b6000805462ff0000191681556040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f99190a1565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054929384939091608084019162000f04906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462000f32906200a086565b801562000f835780601f1062000f575761010080835404028352916020019162000f83565b820191906000526020600020905b81548152906001019060200180831162000f6557829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462000fae906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462000fdc906200a086565b80156200102d5780601f1062001001576101008083540402835291602001916200102d565b820191906000526020600020905b8154815290600101906020018083116200100f57829003601f168201915b5050505050815260200160018201805462001048906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462001076906200a086565b8015620010c75780601f106200109b57610100808354040283529160200191620010c7565b820191906000526020600020905b815481529060010190602001808311620010a957829003601f168201915b50505050508152602001600282018054620010e2906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462001110906200a086565b8015620011615780601f10620011355761010080835404028352916020019162001161565b820191906000526020600020905b8154815290600101906020018083116200114357829003601f168201915b505050505081526020016003820180546200117c906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620011aa906200a086565b8015620011fb5780601f10620011cf57610100808354040283529160200191620011fb565b820191906000526020600020905b815481529060010190602001808311620011dd57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200129a575050509190925250509051949350505050565b3361100014620012ed57604051630f22c43960e41b815261100060048201526024015b60405180910390fd5b6001600160a01b0380821660009081526043602090815260408083205484168084526041835281842082516101808101845281548716815260018201548716948101949094526002810154909516918301919091526003840154606083015260048401805491949160808401919062001366906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462001394906200a086565b8015620013e55780601f10620013b957610100808354040283529160200191620013e5565b820191906000526020600020905b815481529060010190602001808311620013c757829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462001410906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200143e906200a086565b80156200148f5780601f1062001463576101008083540402835291602001916200148f565b820191906000526020600020905b8154815290600101906020018083116200147157829003601f168201915b50505050508152602001600182018054620014aa906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620014d8906200a086565b8015620015295780601f10620014fd5761010080835404028352916020019162001529565b820191906000526020600020905b8154815290600101906020018083116200150b57829003601f168201915b5050505050815260200160028201805462001544906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462001572906200a086565b8015620015c35780601f106200159757610100808354040283529160200191620015c3565b820191906000526020600020905b815481529060010190602001808311620015a557829003601f168201915b50505050508152602001600382018054620015de906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200160c906200a086565b80156200165d5780601f1062001631576101008083540402835291602001916200165d565b820191906000526020600020905b8154815290600101906020018083116200163f57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620016fc575050509190925250505060408101519091506001600160a01b031615806200173957508060e001515b15620017f657604051611002903490600081818185875af1925050503d806000811462001783576040519150601f19603f3d011682016040523d82523d6000602084013e62001788565b606091505b505050816001600160a01b03167ffc8bff675087dd2da069cc3fb517b9ed001e19750c0865241a5542dba1ba170d604051620017e99060208082526011908201527024a72b20a624a22fab20a624a220aa27a960791b604082015260600190565b60405180910390a2505050565b60408181015160c0830151519151632f303ebb60e11b81526001600160401b0390921660048301526001600160a01b031690635e607d769034906024016000604051808303818588803b1580156200184d57600080fd5b505af115801562001862573d6000803e3d6000fd5b5050505050816001600160a01b03167fe34918ff1c7084970068b53fd71ad6d8b04e9f15d3886cbf006443e6cdc52ea634604051620018a391815260200190565b60405180910390a26040808201519051633041949b60e01b815261200591633041949b91620018d8919086906004016200a0bc565b600060405180830381600087803b158015620018f357600080fd5b505af115801562001908573d6000803e3d6000fd5b5050505050505b50565b33611001146200193a57604051630f22c43960e41b81526110016004820152602401620012e4565b60005462010000900460ff16156200196557604051631785c68160e01b815260040160405180910390fd5b6000604583836040516200197b9291906200a0d6565b908152604051908190036020019020546001600160a01b03169050620019a3603f8262008c39565b620019c15760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038116600090815260416020526040812090620019e962015180426200a0fc565b604a546000828152604b60205260409020549192501162001a1d5760405163bd52fcdb60e01b815260040160405180910390fd5b6000818152604b6020526040812080546001929062001a3e9084906200a11f565b909155505060405160469062001a5890879087906200a0d6565b90815260200160405180910390205460001415801562001aa9575042620151806046878760405162001a8c9291906200a0d6565b90815260200160405180910390205462001aa791906200a11f565b105b1562001ac857604051631898eb6b60e01b815260040160405180910390fd5b60008062001ad885600262008c5c565b915091508162001afb57604051631b919bb160e11b815260040160405180910390fd5b6002840154603c5460405163045bc4d160e41b815260048101919091526000916001600160a01b0316906345bc4d10906024016020604051808303816000875af115801562001b4e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001b7491906200a135565b905062001b82858362008ce4565b856001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb28383600260405162001bc2939291906200a14f565b60405180910390a26002850154604051633041949b60e01b815261200591633041949b9162001c00916001600160a01b0316908a906004016200a0bc565b600060405180830381600087803b15801562001c1b57600080fd5b505af115801562001c30573d6000803e3d6000fd5b505050505050505050505050565b6060336120001462001c6857604051630f22c43960e41b81526120006004820152602401620012e4565b60005462010000900460ff161562001c9357604051631785c68160e01b815260040160405180910390fd5b6034805460ff1916600117905560405162461bcd60e51b815260206004820152600a60248201526919195c1c9958d85d195960b21b6044820152606401620012e4565b60005462010000900460ff161562001d0157604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562001d335760405163b1d02c3d60e01b815260040160405180910390fd5b62001d3d62008dd8565b62001d4a603f8262008c39565b62001d685760405163056e881160e01b815260040160405180910390fd5b62001d7262008e23565b600082900362001d9557604051636490ffd360e01b815260040160405180910390fd5b600062001da162008dd8565b6001600160a01b0381166000908152604f602052604090208054604e54929350909162001dcf86836200a11f565b111562001def5760405163091af98560e21b815260040160405180910390fd5b60005b8581101562001ed557600087878381811062001e125762001e126200a18a565b905060200201350362001e3857604051636490ffd360e01b815260040160405180910390fd5b600062001e478260016200a11f565b90505b8681101562001ebf5787878281811062001e685762001e686200a18a565b9050602002013588888481811062001e845762001e846200a18a565b905060200201350362001eaa57604051632205e3c760e11b815260040160405180910390fd5b8062001eb6816200a1a0565b91505062001e4a565b508062001ecc816200a1a0565b91505062001df2565b5060005b8581101562001f745760005b8281101562001f5e5783818154811062001f035762001f036200a18a565b906000526020600020015488888481811062001f235762001f236200a18a565b905060200201350362001f4957604051632205e3c760e11b815260040160405180910390fd5b8062001f55816200a1a0565b91505062001ee5565b508062001f6b816200a1a0565b91505062001ed9565b5060005b8581101562001908578287878381811062001f975762001f976200a18a565b835460018101855560009485526020948590209190940292909201359190920155506001600160a01b0384167f7c4ff4c9a343a2daef608f3b5a91016e994a15fc0ef8611109e4f45823249f2988888481811062001ff95762001ff96200a18a565b905060200201356040516200201091815260200190565b60405180910390a28062002024816200a1a0565b91505062001f78565b6000816200203d603f8262008c39565b6200205b5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038084166000908152604160205260409020600d01541691505b50919050565b60608082806001600160401b03811115620020a157620020a16200985f565b604051908082528060200260200182016040528015620020cb578160200160208202803683370190505b509250806001600160401b03811115620020e957620020e96200985f565b6040519080825280602002602001820160405280156200211e57816020015b6060815260200190600190039081620021085790505b50915060005b81811015620026475760008686838181106200214457620021446200a18a565b90506020020160208101906200215b9190620096f0565b6001600160a01b03808216600090815260416020908152604080832081516101808101835281548616815260018201548616938101939093526002810154909416908201526003830154606082015260048301805494955091939092916080840191620021c8906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620021f6906200a086565b8015620022475780601f106200221b5761010080835404028352916020019162002247565b820191906000526020600020905b8154815290600101906020018083116200222957829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462002272906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620022a0906200a086565b8015620022f15780601f10620022c557610100808354040283529160200191620022f1565b820191906000526020600020905b815481529060010190602001808311620022d357829003601f168201915b505050505081526020016001820180546200230c906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200233a906200a086565b80156200238b5780601f106200235f576101008083540402835291602001916200238b565b820191906000526020600020905b8154815290600101906020018083116200236d57829003601f168201915b50505050508152602001600282018054620023a6906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620023d4906200a086565b8015620024255780601f10620023f95761010080835404028352916020019162002425565b820191906000526020600020905b8154815290600101906020018083116200240757829003601f168201915b5050505050815260200160038201805462002440906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200246e906200a086565b8015620024bf5780601f106200249357610100808354040283529160200191620024bf565b820191906000526020600020905b815481529060010190602001808311620024a157829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200255e57505050505081525050905080600001518684815181106200259757620025976200a18a565b6001600160a01b039283166020918202929092018101919091529083166000908152604f8252604090819020805482518185028101850190935280835291929091908301828280156200260a57602002820191906000526020600020905b815481526020019060010190808311620025f5575b50505050508584815181106200262457620026246200a18a565b6020026020010181905250505080806200263e906200a1a0565b91505062002124565b50505b9250929050565b600054630100000090046001600160a01b0316331462002684576040516306fbb1e360e01b815260040160405180910390fd5b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f7fd26be6fc92aff63f1f4409b2b2ddeb272a888031d7f55ec830485ec61941869190a250565b60005462010000900460ff1615620026fe57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620027305760405163b1d02c3d60e01b815260040160405180910390fd5b806200273e603f8262008c39565b6200275c5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0382166000908152604160205260409020600a81015460ff166200279a57604051634b6b857d60e01b815260040160405180910390fd5b6036546002820154604051630913db4760e01b81526001600160a01b03868116600483015290911690630913db4790602401602060405180830381865afa158015620027ea573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200281091906200a135565b101562002830576040516317b204bf60e11b815260040160405180910390fd5b4281600b01541115620028565760405163170cb76760e21b815260040160405180910390fd5b600a8101805460ff1916905560498054600191906000906200287a9084906200a1bc565b90915550506040516001600160a01b038416907f9390b453426557da5ebdc31f19a37753ca04addf656d32f35232211bb2af3f1990600090a2505050565b60005462010000900460ff1615620028e357604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620029155760405163b1d02c3d60e01b815260040160405180910390fd5b6200291f62008e36565b6200292c603f8262008c39565b6200294a5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0382166200297257604051636520611b60e11b815260040160405180910390fd5b6001600160a01b038281166000908152604360205260409020541615620029ac57604051631e6f587560e11b815260040160405180910390fd5b6000620029b862008e36565b6001600160a01b0381166000908152604160205260409020600c810154919250904290620029eb9062015180906200a11f565b111562002a0b57604051631f92cdbd60e11b815260040160405180910390fd5b80546001600160a01b039081166000908152604460209081526040808320429081905585548986166001600160a01b031991821681178855600c88019290925581855260439093528184208054958816959093168517909255519092917f6e4e747ca35203f16401c69805c7dd52fff67ef60b0ebc5c7fe16890530f223591a350505050565b3362002a9f603f8262008c39565b62002abd5760405163056e881160e01b815260040160405180910390fd5b60005462010000900460ff161562002ae857604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562002b1a5760405163b1d02c3d60e01b815260040160405180910390fd5b6001600160a01b038281166000908152604d6020526040902054161562002b545760405163bebdc75760e01b815260040160405180910390fd5b62002b61603f8362008c39565b1562002b805760405163bebdc75760e01b815260040160405180910390fd5b336000818152604160205260409020600d01546001600160a01b03908116908416810362002bc15760405163bebdc75760e01b815260040160405180910390fd5b6001600160a01b0381161562002bf8576001600160a01b0381166000908152604d6020526040902080546001600160a01b03191690555b6001600160a01b038281166000908152604160205260409020600d0180546001600160a01b03191691861691821790551562002c5d576001600160a01b038481166000908152604d6020526040902080546001600160a01b0319169184169190911790555b836001600160a01b0316816001600160a01b0316836001600160a01b03167fcbb728765de145e99c00e8ae32a325231e850359b7b8a6da3b84d672ab3f1d0a60405160405180910390a450505050565b600054630100000090046001600160a01b0316331462002ce0576040516306fbb1e360e01b815260040160405180910390fd5b6001600160a01b038116600081815260016020526040808220805460ff19169055517fe0db3499b7fdc3da4cddff5f45d694549c19835e7f719fb5606d3ad1a5de40119190a250565b60005462010000900460ff161562002d5457604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562002d865760405163b1d02c3d60e01b815260040160405180910390fd5b8162002d94603f8262008c39565b62002db25760405163056e881160e01b815260040160405180910390fd5b8160000362002dd457604051639811e0c760e01b815260040160405180910390fd5b6001600160a01b038084166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054339491608084019162002e3c906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462002e6a906200a086565b801562002ebb5780601f1062002e8f5761010080835404028352916020019162002ebb565b820191906000526020600020905b81548152906001019060200180831162002e9d57829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462002ee6906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462002f14906200a086565b801562002f655780601f1062002f395761010080835404028352916020019162002f65565b820191906000526020600020905b81548152906001019060200180831162002f4757829003601f168201915b5050505050815260200160018201805462002f80906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462002fae906200a086565b801562002fff5780601f1062002fd35761010080835404028352916020019162002fff565b820191906000526020600020905b81548152906001019060200180831162002fe157829003601f168201915b505050505081526020016002820180546200301a906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003048906200a086565b8015620030995780601f106200306d5761010080835404028352916020019162003099565b820191906000526020600020905b8154815290600101906020018083116200307b57829003601f168201915b50505050508152602001600382018054620030b4906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620030e2906200a086565b8015620031335780601f10620031075761010080835404028352916020019162003133565b820191906000526020600020905b8154815290600101906020018083116200311557829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620031d2575050509190925250505060408082015190516326ccee8b60e11b81526001600160a01b0385811660048301526024820188905292935060009290911690634d99dd16906044016020604051808303816000875af11580156200324c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200327291906200a135565b9050826001600160a01b0316866001600160a01b03167f3aace7340547de7b9156593a7652dc07ee900cea3fd8f82cb6c9d38b408298028784604051620032c3929190918252602082015260400190565b60405180910390a3856001600160a01b0316836001600160a01b031603620032f057620032f08662008e77565b6040808301519051633041949b60e01b815261200591633041949b916200331d919087906004016200a0bc565b600060405180830381600087803b1580156200333857600080fd5b505af11580156200334d573d6000803e3d6000fd5b50505050505050505050565b60005462010000900460ff16156200338457604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620033b65760405163b1d02c3d60e01b815260040160405180910390fd5b83620033c4603f8262008c39565b620033e25760405163056e881160e01b815260040160405180910390fd5b83620033f0603f8262008c39565b6200340e5760405163056e881160e01b815260040160405180910390fd5b6034805460ff1916600117905560008490036200343e57604051639811e0c760e01b815260040160405180910390fd5b846001600160a01b0316866001600160a01b031603620034715760405163f0e3e62960e01b815260040160405180910390fd5b6001600160a01b0380871660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608201526004830180543394916080840191620034d9906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003507906200a086565b8015620035585780601f106200352c5761010080835404028352916020019162003558565b820191906000526020600020905b8154815290600101906020018083116200353a57829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462003583906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620035b1906200a086565b8015620036025780601f10620035d65761010080835404028352916020019162003602565b820191906000526020600020905b815481529060010190602001808311620035e457829003601f168201915b505050505081526020016001820180546200361d906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200364b906200a086565b80156200369c5780601f1062003670576101008083540402835291602001916200369c565b820191906000526020600020905b8154815290600101906020018083116200367e57829003601f168201915b50505050508152602001600282018054620036b7906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620036e5906200a086565b8015620037365780601f106200370a5761010080835404028352916020019162003736565b820191906000526020600020905b8154815290600101906020018083116200371857829003601f168201915b5050505050815260200160038201805462003751906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200377f906200a086565b8015620037d05780601f10620037a457610100808354040283529160200191620037d0565b820191906000526020600020905b815481529060010190602001808311620037b257829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200386f57505050919092525050506001600160a01b03808916600090815260416020908152604080832081516101808101835281548616815260018201548616938101939093526002810154909416908201526003830154606082015260048301805494955091939092916080840191620038fa906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003928906200a086565b8015620039795780601f106200394d5761010080835404028352916020019162003979565b820191906000526020600020905b8154815290600101906020018083116200395b57829003601f168201915b5050505050815260200160058201604051806080016040529081600082018054620039a4906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620039d2906200a086565b801562003a235780601f10620039f75761010080835404028352916020019162003a23565b820191906000526020600020905b81548152906001019060200180831162003a0557829003601f168201915b5050505050815260200160018201805462003a3e906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003a6c906200a086565b801562003abd5780601f1062003a915761010080835404028352916020019162003abd565b820191906000526020600020905b81548152906001019060200180831162003a9f57829003601f168201915b5050505050815260200160028201805462003ad8906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003b06906200a086565b801562003b575780601f1062003b2b5761010080835404028352916020019162003b57565b820191906000526020600020905b81548152906001019060200180831162003b3957829003601f168201915b5050505050815260200160038201805462003b72906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003ba0906200a086565b801562003bf15780601f1062003bc55761010080835404028352916020019162003bf1565b820191906000526020600020905b81548152906001019060200180831162003bd357829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b81548152602001906001019080831162003c905750505050508152505090508060e00151801562003cd35750876001600160a01b0316836001600160a01b031614155b1562003cf257604051636468920360e01b815260040160405180910390fd5b60408083015190516352e82ce560e11b81526001600160a01b038581166004830152602482018a9052600092169063a5d059ca906044016020604051808303816000875af115801562003d49573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003d6f91906200a135565b905060375481101562003d955760405163dc6f0bdd60e01b815260040160405180910390fd5b896001600160a01b0316846001600160a01b031614801562003e2a57506036546040808501519051630913db4760e01b81526001600160a01b038d8116600483015290911690630913db4790602401602060405180830381865afa15801562003e02573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003e2891906200a135565b105b1562003e49576040516317b204bf60e11b815260040160405180910390fd5b6000620186a0603a548362003e5f91906200a1d2565b62003e6b91906200a0fc565b9050600083604001516001600160a01b03168260405160006040518083038185875af1925050503d806000811462003ec0576040519150601f19603f3d011682016040523d82523d6000602084013e62003ec5565b606091505b505090508062003ee8576040516312171d8360e31b815260040160405180910390fd5b62003ef482846200a1bc565b60408086015190516317066a5760e21b81526001600160a01b03898116600483015292955060009290911690635c19a95c90869060240160206040518083038185885af115801562003f4a573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019062003f7191906200a135565b9050866001600160a01b03168c6001600160a01b03168e6001600160a01b03167ffdac6e81913996d95abcc289e90f2d8bd235487ce6fe6f821e7d21002a1915b48e858960405162003fd6939291909283526020830191909152604082015260600190565b60405180910390a46040805160028082526060820183526000926020830190803683370190505090508660400151816000815181106200401a576200401a6200a18a565b60200260200101906001600160a01b031690816001600160a01b0316815250508560400151816001815181106200405557620040556200a18a565b6001600160a01b0390921660209283029190910190910152604051634484077560e01b815261200590634484077590620040969084908c906004016200a1ec565b600060405180830381600087803b158015620040b157600080fd5b505af1158015620040c6573d6000803e3d6000fd5b505050508a1562004138576120056001600160a01b031663e5ed5b1e898f6040518363ffffffff1660e01b8152600401620041039291906200a0bc565b600060405180830381600087803b1580156200411e57600080fd5b505af115801562004133573d6000803e3d6000fd5b505050505b50506034805460ff19169055505050505050505050505050565b60005462010000900460ff16156200417d57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620041af5760405163b1d02c3d60e01b815260040160405180910390fd5b620041b962008e36565b620041c6603f8262008c39565b620041e45760405163056e881160e01b815260040160405180910390fd5b6000620041f062008e36565b6001600160a01b0381166000908152604160205260409020600c810154919250904290620042239062015180906200a11f565b11156200424357604051631f92cdbd60e11b815260040160405180910390fd5b60098101546001600160401b03600160401b909104811690851611156200427d5760405163dc81db8560e01b815260040160405180910390fd5b60098101546000906001600160401b039081169086161015620042bb576009820154620042b59086906001600160401b03166200a218565b620042d5565b6009820154620042d5906001600160401b0316866200a218565b60098301546001600160401b039182169250600160801b900416811115620043105760405163dc81db8560e01b815260040160405180910390fd5b60098201805467ffffffffffffffff19166001600160401b03871690811790915542600c8401556040519081526001600160a01b038416907f78cdd96edf59e09cfd4d26ef6ef6c92d166effe6a40970c54821206d541932cb9060200160405180910390a25050505050565b606080606060006200438f603f62008f95565b90508086101562004a18578415620043a85784620043aa565b805b9450600085620043bb88846200a1bc565b11620043d357620043cd87836200a1bc565b620043d5565b855b9050806001600160401b03811115620043f257620043f26200985f565b6040519080825280602002602001820160405280156200441c578160200160208202803683370190505b509450806001600160401b038111156200443a576200443a6200985f565b60405190808252806020026020018201604052801562004464578160200160208202803683370190505b509350806001600160401b038111156200448257620044826200985f565b604051908082528060200260200182016040528015620044b757816020015b6060815260200190600190039081620044a15790505b50925060005b8181101562004a15576000620044e1620044d8838b6200a11f565b603f9062008fa0565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054949550919390929160808401916200454e906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200457c906200a086565b8015620045cd5780601f10620045a157610100808354040283529160200191620045cd565b820191906000526020600020905b815481529060010190602001808311620045af57829003601f168201915b5050505050815260200160058201604051806080016040529081600082018054620045f8906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462004626906200a086565b8015620046775780601f106200464b5761010080835404028352916020019162004677565b820191906000526020600020905b8154815290600101906020018083116200465957829003601f168201915b5050505050815260200160018201805462004692906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620046c0906200a086565b8015620047115780601f10620046e55761010080835404028352916020019162004711565b820191906000526020600020905b815481529060010190602001808311620046f357829003601f168201915b505050505081526020016002820180546200472c906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200475a906200a086565b8015620047ab5780601f106200477f57610100808354040283529160200191620047ab565b820191906000526020600020905b8154815290600101906020018083116200478d57829003601f168201915b50505050508152602001600382018054620047c6906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620047f4906200a086565b8015620048455780601f10620048195761010080835404028352916020019162004845565b820191906000526020600020905b8154815290600101906020018083116200482757829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620048e457505050505081525050905080600001518884815181106200491d576200491d6200a18a565b60200260200101906001600160a01b031690816001600160a01b0316815250508060e00151620049b65780604001516001600160a01b03166315d1f8986040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200498a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620049b091906200a135565b620049b9565b60005b878481518110620049ce57620049ce6200a18a565b6020026020010181815250508060800151868481518110620049f457620049f46200a18a565b602002602001018190525050508062004a0d906200a1a0565b9050620044bd565b50505b92959194509250565b60005462010000900460ff161562004a4c57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562004a7e5760405163b1d02c3d60e01b815260040160405180910390fd5b3362004a8c603f8262008c39565b1562004aab57604051635f28f62b60e01b815260040160405180910390fd5b6001600160a01b038181166000908152604d6020526040902054161562004ae557604051631a0a9b9f60e21b815260040160405180910390fd5b6001600160a01b03888116600090815260436020526040902054161562004b1f57604051631e6f587560e11b815260040160405180910390fd5b60006001600160a01b03166045888860405162004b3e9291906200a0d6565b908152604051908190036020019020546001600160a01b03161462004b76576040516311fdb94760e01b815260040160405180910390fd5b600062004b8483806200a242565b60405160200162004b979291906200a0d6565b60408051601f1981840301815291815281516020928301206000818152604290935291205490915060ff161562004be15760405163c0bf414360e01b815260040160405180910390fd5b600062004bf7670de0b6b3a7640000346200a1bc565b905060365481101562004c1d576040516317b204bf60e11b815260040160405180910390fd5b6001600160a01b038a1662004c4557604051636520611b60e11b815260040160405180910390fd5b61138862004c5a604087016020880162009b7e565b6001600160401b0316118062004ca0575062004c7d604086016020870162009b7e565b6001600160401b031662004c95602087018762009b7e565b6001600160401b0316115b8062004cdf575062004cb9604086016020870162009b7e565b6001600160401b031662004cd4606087016040880162009b7e565b6001600160401b0316115b1562004cfe5760405163dc81db8560e01b815260040160405180910390fd5b62004d4962004d0e85806200a242565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062008fae92505050565b62004d6757604051635dba5ad760e01b815260040160405180910390fd5b62004d76838a8a8a8a62009150565b62004d9457604051631647e3cb60e11b815260040160405180910390fd5b600062004de28462004da787806200a242565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506200928092505050565b905062004df1603f8562009381565b506000838152604260209081526040808320805460ff191660019081179091556001600160a01b0380891680865260419094529190932080548f83166001600160a01b03199182161782559381018054851690931790925560028201805491851691909316179091554260038201556004810162004e718b8d836200a2ea565b50856005820162004e8382826200a3b2565b508790506009820162004e9782826200a4eb565b505042600c8201556001600160a01b038c81166000908152604360205260409081902080546001600160a01b0319169288169290921790915551859060459062004ee5908e908e906200a0d6565b908152602001604051809103902060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550816001600160a01b0316856001600160a01b03168d6001600160a01b03167faecd9fb95e79c75a3a1de93362c6be5fe6ab65770d8614be583884161cd8228d8e8e60405162004f689291906200a5bb565b60405180910390a460408051848152602081018590526001600160a01b0387169182917f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e04910160405180910390a360408051670de0b6b3a7640000808252602082015261dead916001600160a01b038816917f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e04910160405180910390a3604051633041949b60e01b815261200590633041949b906200502e90859089906004016200a0bc565b600060405180830381600087803b1580156200504957600080fd5b505af11580156200505e573d6000803e3d6000fd5b50505050505050505050505050505050565b6040805160608101825260008082526020820181905291810191909152816200509b603f8262008c39565b620050b95760405163056e881160e01b815260040160405180910390fd5b50506001600160a01b031660009081526041602090815260409182902082516060810184526009909101546001600160401b038082168352600160401b8204811693830193909352600160801b90049091169181019190915290565b6001600160a01b0380821660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608281019190915260048401805491949160808401919062005182906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620051b0906200a086565b8015620052015780601f10620051d55761010080835404028352916020019162005201565b820191906000526020600020905b815481529060010190602001808311620051e357829003601f168201915b50505050508152602001600582016040518060800160405290816000820180546200522c906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200525a906200a086565b8015620052ab5780601f106200527f57610100808354040283529160200191620052ab565b820191906000526020600020905b8154815290600101906020018083116200528d57829003601f168201915b50505050508152602001600182018054620052c6906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620052f4906200a086565b8015620053455780601f10620053195761010080835404028352916020019162005345565b820191906000526020600020905b8154815290600101906020018083116200532757829003601f168201915b5050505050815260200160028201805462005360906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200538e906200a086565b8015620053df5780601f10620053b357610100808354040283529160200191620053df565b820191906000526020600020905b815481529060010190602001808311620053c157829003601f168201915b50505050508152602001600382018054620053fa906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005428906200a086565b8015620054795780601f106200544d5761010080835404028352916020019162005479565b820191906000526020600020905b8154815290600101906020018083116200545b57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620055185750505091909252505050608001519392505050565b33611001146200556957604051630f22c43960e41b81526110016004820152602401620012e4565b6001600160a01b038082166000908152604360205260409020541662005591603f8262008c39565b620055af5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038181166000908152604160205260408082206002810154603b54925163045bc4d160e41b81526004810193909352909316906345bc4d10906024016020604051808303816000875af115801562005612573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200563891906200a135565b90506000603d54426200564c91906200a11f565b90506200565a838262008ce4565b836001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb2828460016040516200569a939291906200a14f565b60405180910390a26002830154604051633041949b60e01b815261200591633041949b91620056d8916001600160a01b03169088906004016200a0bc565b600060405180830381600087803b158015620056f357600080fd5b505af115801562005708573d6000803e3d6000fd5b505050505050505050565b600054610100900460ff1615808015620057345750600054600160ff909116105b80620057505750303b15801562005750575060005460ff166001145b620057b55760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401620012e4565b6000805460ff191660011790558015620057d9576000805461ff0019166101001790555b334114620057fa5760405163022d8c9560e31b815260040160405180910390fd5b3a156200581a576040516383f1b1d360e01b815260040160405180910390fd5b611388603555686c6b935b8bbd400000603655670de0b6b3a7640000603755600960385562093a806039556002603a819055678ac7230489e80000603b55680ad78ebc5ac6200000603c556202a300603d5562069780603e55604a55620058957330151da466ec8ab345bef3d6983023e050fb067362009398565b80156200190f576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b33612000146200590657604051630f22c43960e41b81526120006004820152602401620012e4565b60405162461bcd60e51b815260206004820152600a60248201526919195c1c9958d85d195960b21b6044820152606401620012e4565b600054630100000090046001600160a01b031633146200596f576040516306fbb1e360e01b815260040160405180910390fd5b60005462010000900460ff16156200599a57604051631785c68160e01b815260040160405180910390fd5b6000805462ff00001916620100001781556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e7529190a1565b6000620059e3603f8462008c39565b62005a015760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0383811660009081526041602052604090819020600201549051636bbf224960e01b815260048101859052911690636bbf2249906024015b602060405180830381865afa15801562005a5e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062005a8491906200a135565b90505b92915050565b60005462010000900460ff161562005ab857604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562005aea5760405163b1d02c3d60e01b815260040160405180910390fd5b8162005af8603f8262008c39565b62005b165760405163056e881160e01b815260040160405180910390fd5b603754349081101562005b3c5760405163dc6f0bdd60e01b815260040160405180910390fd5b6001600160a01b038085166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054339491608084019162005ba4906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005bd2906200a086565b801562005c235780601f1062005bf75761010080835404028352916020019162005c23565b820191906000526020600020905b81548152906001019060200180831162005c0557829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462005c4e906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005c7c906200a086565b801562005ccd5780601f1062005ca15761010080835404028352916020019162005ccd565b820191906000526020600020905b81548152906001019060200180831162005caf57829003601f168201915b5050505050815260200160018201805462005ce8906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005d16906200a086565b801562005d675780601f1062005d3b5761010080835404028352916020019162005d67565b820191906000526020600020905b81548152906001019060200180831162005d4957829003601f168201915b5050505050815260200160028201805462005d82906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005db0906200a086565b801562005e015780601f1062005dd55761010080835404028352916020019162005e01565b820191906000526020600020905b81548152906001019060200180831162005de357829003601f168201915b5050505050815260200160038201805462005e1c906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005e4a906200a086565b801562005e9b5780601f1062005e6f5761010080835404028352916020019162005e9b565b820191906000526020600020905b81548152906001019060200180831162005e7d57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b81548152602001906001019080831162005f3a5750505050508152505090508060e00151801562005f7d5750856001600160a01b0316826001600160a01b031614155b1562005f9c57604051636468920360e01b815260040160405180910390fd5b60408082015190516317066a5760e21b81526001600160a01b0384811660048301526000921690635c19a95c90869060240160206040518083038185885af115801562005fed573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906200601491906200a135565b9050826001600160a01b0316876001600160a01b03167f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e04838760405162006065929190918252602082015260400190565b60405180910390a36040808301519051633041949b60e01b815261200591633041949b916200609a919087906004016200a0bc565b600060405180830381600087803b158015620060b557600080fd5b505af1158015620060ca573d6000803e3d6000fd5b50505050851562001908576040516372f6ad8f60e11b81526120059063e5ed5b1e90620060fe9086908b906004016200a0bc565b600060405180830381600087803b1580156200611957600080fd5b505af11580156200612e573d6000803e3d6000fd5b5050505050505050505050565b60005462010000900460ff16156200616657604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620061985760405163b1d02c3d60e01b815260040160405180910390fd5b620061a262008dd8565b620061af603f8262008c39565b620061cd5760405163056e881160e01b815260040160405180910390fd5b6000620061d962008dd8565b6001600160a01b0381166000908152604f6020526040812080549293509190859003620062b05760005b818110156200628457836001600160a01b03167f08e60c1b84aab23d99a7262015e647d5ffd6c6e08f78205e1df6774c48e1427a8483815481106200624c576200624c6200a18a565b90600052602060002001546040516200626791815260200190565b60405180910390a2806200627b816200a1a0565b91505062006203565b506001600160a01b0383166000908152604f60205260408120620062a8916200968a565b505050505050565b60005b858110156200640e576000878783818110620062d357620062d36200a18a565b90506020020135905060005b83811015620063f65781858281548110620062fe57620062fe6200a18a565b906000526020600020015403620063e157846200631d6001866200a1bc565b815481106200633057620063306200a18a565b90600052602060002001548582815481106200635057620063506200a18a565b9060005260206000200181905550848054806200637157620063716200a5d1565b60019003818190600052602060002001600090559055838062006394906200a5e7565b945050856001600160a01b03167f08e60c1b84aab23d99a7262015e647d5ffd6c6e08f78205e1df6774c48e1427a83604051620063d391815260200190565b60405180910390a2620063f6565b80620063ed816200a1a0565b915050620062df565b5050808062006405906200a1a0565b915050620062b3565b508154600003620062a8576001600160a01b0383166000908152604f60205260408120620062a8916200968a565b505050565b6200646d6040518060800160405280606081526020016060815260200160608152602001606081525090565b816200647b603f8262008c39565b620064995760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03831660009081526041602052604090819020815160808101909252600501805482908290620064d0906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620064fe906200a086565b80156200654f5780601f1062006523576101008083540402835291602001916200654f565b820191906000526020600020905b8154815290600101906020018083116200653157829003601f168201915b505050505081526020016001820180546200656a906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462006598906200a086565b8015620065e95780601f10620065bd57610100808354040283529160200191620065e9565b820191906000526020600020905b815481529060010190602001808311620065cb57829003601f168201915b5050505050815260200160028201805462006604906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462006632906200a086565b8015620066835780601f10620066575761010080835404028352916020019162006683565b820191906000526020600020905b8154815290600101906020018083116200666557829003601f168201915b505050505081526020016003820180546200669e906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620066cc906200a086565b80156200671d5780601f10620066f1576101008083540402835291602001916200671d565b820191906000526020600020905b815481529060010190602001808311620066ff57829003601f168201915b505050505081525050915050919050565b60005462010000900460ff16156200675957604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff16156200678b5760405163b1d02c3d60e01b815260040160405180910390fd5b62006797828262009431565b5050565b3361100714620067c357604051630f22c43960e41b81526110076004820152602401620012e4565b620068306040518060400160405280601081526020016f1d1c985b9cd9995c91d85cd31a5b5a5d60821b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620068eb5760208114620068625783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620068a59185858083850183828082843760009201919091525092939250506200959a9050565b90506108fc811080620068b9575061271081115b15620068e25784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60355562007708565b6200695c6040518060400160405280601481526020017336b4b729b2b6332232b632b3b0ba34b7b721272160611b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006a2657602081146200698e5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620069d19185858083850183828082843760009201919091525092939250506200959a9050565b9050683635c9adc5dea00000811080620069f4575069152d02c7e14af680000081115b1562006a1d5784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60365562007708565b62006a99604051806040016040528060168152602001756d696e44656c65676174696f6e424e424368616e676560501b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006b60576020811462006acb5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006b0e9185858083850183828082843760009201919091525092939250506200959a9050565b905067016345785d8a000081108062006b2e5750678ac7230489e8000081115b1562006b575784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60375562007708565b62006bd1604051806040016040528060148152602001736d6178456c656374656456616c696461746f727360601b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006c89576020811462006c035783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006c469185858083850183828082843760009201919091525092939250506200959a9050565b905080158062006c5757506101f481115b1562006c805784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60385562007708565b62006cf26040518060400160405280600c81526020016b1d5b989bdb9914195c9a5bd960a21b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006daf576020811462006d245783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006d679185858083850183828082843760009201919091525092939250506200959a9050565b90506203f48081108062006d7d575062278d0081115b1562006da65784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60395562007708565b62006e1d60405180604001604052806011815260200170726564656c65676174654665655261746560781b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006eca576020811462006e4f5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006e929185858083850183828082843760009201919091525092939250506200959a9050565b9050606481111562006ec15784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603a5562007708565b62006f3a60405180604001604052806013815260200172191bdddb9d1a5b5954db185cda105b5bdd5b9d606a1b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006ffc576020811462006f6c5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006faf9185858083850183828082843760009201919091525092939250506200959a9050565b9050670de0b6b3a764000081108062006fca5750603c548110155b1562006ff35784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603b5562007708565b6200706a6040518060400160405280601181526020017019995b1bdb9e54db185cda105b5bdd5b9d607a1b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b156200712c57602081146200709c5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620070df9185858083850183828082843760009201919091525092939250506200959a9050565b9050678ac7230489e80000811080620070fa5750603b548111155b15620071235784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603c5562007708565b620071996040518060400160405280601081526020016f646f776e74696d654a61696c54696d6560801b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620072565760208114620071cb5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f84018190048102820181019092528281526000916200720e9185858083850183828082843760009201919091525092939250506200959a9050565b905062015180811080620072245750603e548110155b156200724d5784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603d5562007708565b620072c16040518060400160405280600e81526020016d66656c6f6e794a61696c54696d6560901b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b156200737e5760208114620072f35783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620073369185858083850183828082843760009201919091525092939250506200959a9050565b90506203f4808110806200734c5750603d548111155b15620073755784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603e5562007708565b620073f86040518060400160405280601c81526020017f6d617846656c6f6e794265747765656e42726561746865426c6f636b0000000081525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620074a457602081146200742a5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f84018190048102820181019092528281526000916200746d9185858083850183828082843760009201919091525092939250506200959a9050565b9050806000036200749b5784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b604a5562007708565b620075126040518060400160405280601181526020017039ba30b5b2a43ab1283937ba32b1ba37b960791b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620075d25760148114620075445783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b60006200758c601484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200959a9050565b90506001600160a01b038116620075c05784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b620075cb816200959f565b5062007708565b620076396040518060400160405280600a8152602001696d61784e6f646549447360b01b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620076e557602081146200766b5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620076ae9185858083850183828082843760009201919091525092939250506200959a9050565b905080600003620076dc5784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b604e5562007708565b838383836040516325ee20d560e21b8152600401620012e494939291906200a601565b7ff1ce9b2cbf50eeb05769a29e2543fd350cab46894a7dd9978a12d534bb20e633848484846040516200773f94939291906200a601565b60405180910390a150505050565b60005462010000900460ff16156200777857604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620077aa5760405163b1d02c3d60e01b815260040160405180910390fd5b816000816001600160401b03811115620077c857620077c86200985f565b604051908082528060200260200182016040528015620077f2578160200160208202803683370190505b5090506000805b83811015620078f0576200783b8787838181106200781b576200781b6200a18a565b9050602002016020810190620078329190620096f0565b603f9062008c39565b620078595760405163056e881160e01b815260040160405180910390fd5b604160008888848181106200787257620078726200a18a565b9050602002016020810190620078899190620096f0565b6001600160a01b0390811682526020820192909252604001600020600201548451911692508290849083908110620078c557620078c56200a18a565b6001600160a01b0390921660209283029190910190910152620078e8816200a1a0565b9050620077f9565b50604051634484077560e01b8152612005906344840775906200331d90859088906004016200a1ec565b60608060006200792b603f62008f95565b90508085101562007aef57831562007944578362007946565b805b93506000846200795787846200a1bc565b116200796f576200796986836200a1bc565b62007971565b845b9050806001600160401b038111156200798e576200798e6200985f565b604051908082528060200260200182016040528015620079b8578160200160208202803683370190505b509350806001600160401b03811115620079d657620079d66200985f565b60405190808252806020026020018201604052801562007a00578160200160208202803683370190505b50925060005b8181101562007aec5762007a1f620044d882896200a11f565b85828151811062007a345762007a346200a18a565b60200260200101906001600160a01b031690816001600160a01b0316815250506041600086838151811062007a6d5762007a6d6200a18a565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060020160009054906101000a90046001600160a01b031684828151811062007ac15762007ac16200a18a565b6001600160a01b039092166020928302919091019091015262007ae4816200a1a0565b905062007a06565b50505b9250925092565b60008162007b06603f8262008c39565b62007b245760405163056e881160e01b815260040160405180910390fd5b50506001600160a01b03166000908152604160205260409020600c015490565b336110011462007b6c57604051630f22c43960e41b81526110016004820152602401620012e4565b60005462010000900460ff161562007b9757604051631785c68160e01b815260040160405180910390fd5b6001600160a01b038082166000908152604360205260409020541662007bbf603f8262008c39565b62007bdd5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03811660009081526041602052604081209062007c0562015180426200a0fc565b604a546000828152604b60205260409020549192501162007c395760405163bd52fcdb60e01b815260040160405180910390fd5b6000818152604b6020526040812080546001929062007c5a9084906200a11f565b90915550506001600160a01b0384166000908152604460205260409020541580159062007caf57506001600160a01b038416600090815260446020526040902054429062007cad9062015180906200a11f565b105b1562007cce576040516330abb81d60e21b815260040160405180910390fd5b60008062007cde85600062008c5c565b915091508162007d0157604051631b919bb160e11b815260040160405180910390fd5b6002840154603c5460405163045bc4d160e41b815260048101919091526000916001600160a01b0316906345bc4d10906024016020604051808303816000875af115801562007d54573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062007d7a91906200a135565b905062007d88858362008ce4565b856001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb28383600060405162007dc8939291906200a14f565b60405180910390a26002850154604051633041949b60e01b815261200591633041949b91620060fe916001600160a01b0316908a906004016200a0bc565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054929384938493849390929160808401919062007e77906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462007ea5906200a086565b801562007ef65780601f1062007eca5761010080835404028352916020019162007ef6565b820191906000526020600020905b81548152906001019060200180831162007ed857829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462007f21906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462007f4f906200a086565b801562007fa05780601f1062007f745761010080835404028352916020019162007fa0565b820191906000526020600020905b81548152906001019060200180831162007f8257829003601f168201915b5050505050815260200160018201805462007fbb906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462007fe9906200a086565b80156200803a5780601f106200800e576101008083540402835291602001916200803a565b820191906000526020600020905b8154815290600101906020018083116200801c57829003601f168201915b5050505050815260200160028201805462008055906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462008083906200a086565b8015620080d45780601f10620080a857610100808354040283529160200191620080d4565b820191906000526020600020905b815481529060010190602001808311620080b657829003601f168201915b50505050508152602001600382018054620080ef906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200811d906200a086565b80156200816e5780601f1062008142576101008083540402835291602001916200816e565b820191906000526020600020905b8154815290600101906020018083116200815057829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200820d5750505091909252505050606081015160e0820151610100909201519097919650945092505050565b60005462010000900460ff16156200827457604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620082a65760405163b1d02c3d60e01b815260040160405180910390fd5b620082b062008e36565b620082bd603f8262008c39565b620082db5760405163056e881160e01b815260040160405180910390fd5b6000620082e762008e36565b6001600160a01b0381166000908152604160205260409020600c8101549192509042906200831a9062015180906200a11f565b11156200833a57604051631f92cdbd60e11b815260040160405180910390fd5b6005810180546200834b906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462008379906200a086565b8015620083ca5780601f106200839e57610100808354040283529160200191620083ca565b820191906000526020600020905b815481529060010190602001808311620083ac57829003601f168201915b5050508287525085916005840191508190620083e790826200a637565b5060208201516001820190620083fe90826200a637565b50604082015160028201906200841590826200a637565b50606082015160038201906200842c90826200a637565b505042600c830155506040516001600160a01b038316907f85d6366b336ade7f106987ec7a8eac1e8799e508aeab045a39d2f63e0dc969d990600090a250505050565b60005462010000900460ff16156200849a57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620084cc5760405163b1d02c3d60e01b815260040160405180910390fd5b828114620084ed576040516341abc80160e01b815260040160405180910390fd5b60005b838110156200855e576200854b8585838181106200851257620085126200a18a565b9050602002016020810190620085299190620096f0565b8484848181106200853e576200853e6200a18a565b9050602002013562009431565b62008556816200a1a0565b9050620084f0565b5050505050565b6001600160a01b0380821660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608201526004830180549293849390916080840191620085d0906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620085fe906200a086565b80156200864f5780601f1062008623576101008083540402835291602001916200864f565b820191906000526020600020905b8154815290600101906020018083116200863157829003601f168201915b50505050508152602001600582016040518060800160405290816000820180546200867a906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620086a8906200a086565b8015620086f95780601f10620086cd57610100808354040283529160200191620086f9565b820191906000526020600020905b815481529060010190602001808311620086db57829003601f168201915b5050505050815260200160018201805462008714906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462008742906200a086565b8015620087935780601f10620087675761010080835404028352916020019162008793565b820191906000526020600020905b8154815290600101906020018083116200877557829003601f168201915b50505050508152602001600282018054620087ae906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620087dc906200a086565b80156200882d5780601f1062008801576101008083540402835291602001916200882d565b820191906000526020600020905b8154815290600101906020018083116200880f57829003601f168201915b5050505050815260200160038201805462008848906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462008876906200a086565b8015620088c75780601f106200889b57610100808354040283529160200191620088c7565b820191906000526020600020905b815481529060010190602001808311620088a957829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620089665750505091909252505050604001519392505050565b60006200899e603f8462008c39565b620089bc5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038381166000908152604160205260409081902060020154905163aa1966cd60e01b81526004810185905291169063aa1966cd9060240162005a40565b60005462010000900460ff161562008a2b57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562008a5d5760405163b1d02c3d60e01b815260040160405180910390fd5b62008a6762008e36565b62008a74603f8262008c39565b62008a925760405163056e881160e01b815260040160405180910390fd5b600062008a9e62008e36565b905062008aaf818787878762009150565b62008acd57604051631647e3cb60e11b815260040160405180910390fd5b60006001600160a01b03166045878760405162008aec9291906200a0d6565b908152604051908190036020019020546001600160a01b03161462008b24576040516311fdb94760e01b815260040160405180910390fd5b6001600160a01b0381166000908152604160205260409020600c810154429062008b539062015180906200a11f565b111562008b7357604051631f92cdbd60e11b815260040160405180910390fd5b4260468260040160405162008b8991906200a6ff565b908152604051908190036020019020556004810162008baa8789836200a2ea565b5042600c820155604051829060459062008bc8908a908a906200a0d6565b90815260405190819003602001812080546001600160a01b039384166001600160a01b0319909116179055908316907f783156582145bd0ff7924fae6953ba054cf1233eb60739a200ddb10de068ff0d9062008c28908a908a906200a5bb565b60405180910390a250505050505050565b6001600160a01b0381166000908152600183016020526040812054151562005a84565b6000806000848460405160200162008c769291906200a77d565b60408051601f1981840301815291815281516020928301206000818152604c9093529120549091504281111562008cb6576000809350935050506200264a565b603e5462008cc590426200a11f565b6000928352604c60205260409092208290555060019590945092505050565b6000600162008cf4603f62008f95565b62008d0091906200a1bc565b604954108015915062008d4c5760018301546040516001600160a01b03909116907f2afdc18061ac21cff7d9f11527ab9c8dec6fabd4edf6f894ed634bebd6a20d4590600090a2505050565b82600b015482111562008d6157600b83018290555b600a83015460ff166200643c57600a8301805460ff191660019081179091556049805460009062008d949084906200a11f565b909155505060018301546040516001600160a01b03909116907f4905ac32602da3fb8b4b7b00c285e5fc4c6c2308cc908b4a1e4e9625a29c90a390600090a2505050565b336000908152604360205260408120546001600160a01b03161562008e145750336000908152604360205260409020546001600160a01b031690565b62008e1e62008e36565b905090565b604e5460000362008e34576005604e555b565b336000908152604d60205260408120546001600160a01b03161562008e725750336000908152604d60205260409020546001600160a01b031690565b503390565b6001600160a01b0381166000908152604160205260409020600a81015460ff161562008ea1575050565b6036546002820154604051630913db4760e01b81526001600160a01b03858116600483015290911690630913db4790602401602060405180830381865afa15801562008ef1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062008f1791906200a135565b1015620067975762008f3981603d544262008f3391906200a11f565b62008ce4565b80546040516335409f7f60e01b81526001600160a01b039091166004820152611000906335409f7f90602401600060405180830381600087803b15801562008f8057600080fd5b505af1158015620062a8573d6000803e3d6000fd5b600062005a87825490565b600062005a8483836200960b565b60008082905060038151108062008fc6575060098151115b1562008fd55750600092915050565b60418160008151811062008fed5762008fed6200a18a565b016020015160f81c10806200901f5750605a816000815181106200901557620090156200a18a565b016020015160f81c115b156200902e5750600092915050565b60015b8151811015620091465760308282815181106200905257620090526200a18a565b016020015160f81c108062009083575060398282815181106200907957620090796200a18a565b016020015160f81c115b8015620090d357506041828281518110620090a257620090a26200a18a565b016020015160f81c1080620090d35750605a828281518110620090c957620090c96200a18a565b016020015160f81c115b80156200912357506061828281518110620090f257620090f26200a18a565b016020015160f81c1080620091235750607a8282815181106200911957620091196200a18a565b016020015160f81c115b1562009133575060009392505050565b6200913e816200a1a0565b905062009031565b5060019392505050565b600060308414158062009164575060608214155b15620091735750600062009277565b6000868686466040516020016200918e94939291906200a7c7565b60408051808303601f1901815282825280516020918201208184528383019092529092506000919060208201818036833701905050905081602082015260008186868a8a604051602001620091e89594939291906200a7f4565b60408051808303601f190181526001808452838301909252925060009190602082018180368337019050509050815160016020830182602086016066600019fa6200923257600080fd5b506000816000815181106200924b576200924b6200a18a565b016020015160f81c9050600181146200926d5760009550505050505062009277565b6001955050505050505b95945050505050565b60008061200361dead6040516200929790620096aa565b6001600160a01b03928316815291166020820152606060408201819052600090820152608001604051809103906000f080158015620092da573d6000803e3d6000fd5b509050806001600160a01b031663f399e22e3486866040518463ffffffff1660e01b81526004016200930e9291906200a82c565b6000604051808303818588803b1580156200932857600080fd5b505af11580156200933d573d6000803e3d6000fd5b50506040516001600160a01b038086169450881692507fd481492e4e93bb36b4c12a5af93f03be3bf04b454dfbc35dd2663fa26f44d5b09150600090a39392505050565b600062005a84836001600160a01b03841662009638565b600054610100900460ff16620094055760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401620012e4565b600080546001600160a01b039092166301000000026301000000600160b81b0319909216919091179055565b816200943f603f8262008c39565b6200945d5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03838116600090815260416020526040808220600201549051635569f64b60e11b8152336004820152602481018690529192169063aad3ec96906044016020604051808303816000875af1158015620094c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620094e791906200a135565b9050336001600160a01b0316846001600160a01b03167ff7a40077ff7a04c7e61f6f26fb13774259ddf1b6bce9ecf26a8276cdd3992683836040516200952f91815260200190565b60405180910390a350505050565b6000816040516020016200955291906200a852565b60405160208183030381529060405280519060200120836040516020016200957b91906200a852565b6040516020818303038152906040528051906020012014905092915050565b015190565b600080546040516001600160a01b0380851693630100000090930416917f44fc1b38a4abaa91ebd1b628a5b259a698f86238c8217d68f516e87769c60c0b91a3600080546001600160a01b039092166301000000026301000000600160b81b0319909216919091179055565b60008260000182815481106200962557620096256200a18a565b9060005260206000200154905092915050565b6000818152600183016020526040812054620096815750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562005a87565b50600062005a87565b50805460008255906000526020600020908101906200190f9190620096b8565b610e96806200a87183390190565b5b80821115620096cf5760008155600101620096b9565b5090565b80356001600160a01b0381168114620096eb57600080fd5b919050565b6000602082840312156200970357600080fd5b62005a8482620096d3565b60008083601f8401126200972157600080fd5b5081356001600160401b038111156200973957600080fd5b6020830191508360208285010111156200264a57600080fd5b600080602083850312156200976657600080fd5b82356001600160401b038111156200977d57600080fd5b6200978b858286016200970e565b90969095509350505050565b600080600060408486031215620097ad57600080fd5b833560ff81168114620097bf57600080fd5b925060208401356001600160401b03811115620097db57600080fd5b620097e9868287016200970e565b9497909650939450505050565b60005b8381101562009813578181015183820152602001620097f9565b50506000910152565b6000815180845262009836816020860160208601620097f6565b601f01601f19169290920160200192915050565b60208152600062005a8460208301846200981c565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156200989a576200989a6200985f565b60405290565b60006001600160401b0380841115620098bd57620098bd6200985f565b604051601f8501601f19908116603f01168101908282118183101715620098e857620098e86200985f565b816040528093508581528686860111156200990257600080fd5b858560208301376000602087830101525050509392505050565b6000602082840312156200992f57600080fd5b81356001600160401b038111156200994657600080fd5b8201601f810184136200995857600080fd5b6200996984823560208401620098a0565b949350505050565b60008083601f8401126200998457600080fd5b5081356001600160401b038111156200999c57600080fd5b6020830191508360208260051b85010111156200264a57600080fd5b60008060208385031215620099cc57600080fd5b82356001600160401b03811115620099e357600080fd5b6200978b8582860162009971565b600081518084526020808501945080840160005b8381101562009a2c5781516001600160a01b03168752958201959082019060010162009a05565b509495945050505050565b60408152600062009a4c6040830185620099f1565b6020838203818501528185518084528284019150828160051b8501018388016000805b8481101562009ac657878403601f19018652825180518086529088019088860190845b8181101562009ab05783518352928a0192918a019160010162009a92565b5050968801969450509186019160010162009a6f565b50919a9950505050505050505050565b6000806040838503121562009aea57600080fd5b62009af583620096d3565b946020939093013593505050565b80358015158114620096eb57600080fd5b6000806000806080858703121562009b2b57600080fd5b62009b3685620096d3565b935062009b4660208601620096d3565b92506040850135915062009b5d6060860162009b03565b905092959194509250565b6001600160401b03811681146200190f57600080fd5b60006020828403121562009b9157600080fd5b813562009b9e8162009b68565b9392505050565b6000806040838503121562009bb957600080fd5b50508035926020909101359150565b60808152600062009bdd6080830187620099f1565b82810360208481019190915286518083528782019282019060005b8181101562009c165784518352938301939183019160010162009bf8565b5050848103604086015286518082528282019350600581901b8201830183890160005b8381101562009c6b57601f1985840301875262009c588383516200981c565b9686019692509085019060010162009c39565b5050809550505050505082606083015295945050505050565b600080600080600080600087890360e081121562009ca157600080fd5b62009cac89620096d3565b975060208901356001600160401b038082111562009cc957600080fd5b62009cd78c838d016200970e565b909950975060408b013591508082111562009cf157600080fd5b62009cff8c838d016200970e565b90975095508591506060605f198401121562009d1a57600080fd5b60608b01945060c08b013592508083111562009d3557600080fd5b505088016080818b03121562009d4a57600080fd5b8091505092959891949750929550565b6000806040838503121562009d6e57600080fd5b62009d7983620096d3565b915062009d896020840162009b03565b90509250929050565b60208152600082516080602084015262009db060a08401826200981c565b90506020840151601f198085840301604086015262009dd083836200981c565b9250604086015191508085840301606086015262009def83836200981c565b92506060860151915080858403016080860152506200927782826200981c565b6000806000806040858703121562009e2657600080fd5b84356001600160401b038082111562009e3e57600080fd5b62009e4c888389016200970e565b9096509450602087013591508082111562009e6657600080fd5b5062009e75878288016200970e565b95989497509550505050565b60008060006040848603121562009e9757600080fd5b83356001600160401b0381111562009eae57600080fd5b62009ebc8682870162009971565b909450925062009ed1905060208501620096d3565b90509250925092565b60608152600062009eef6060830186620099f1565b828103602084015262009f038186620099f1565b915050826040830152949350505050565b600082601f83011262009f2657600080fd5b62005a8483833560208501620098a0565b60006020828403121562009f4a57600080fd5b81356001600160401b038082111562009f6257600080fd5b908301906080828603121562009f7757600080fd5b62009f8162009875565b82358281111562009f9157600080fd5b62009f9f8782860162009f14565b82525060208301358281111562009fb557600080fd5b62009fc38782860162009f14565b60208301525060408301358281111562009fdc57600080fd5b62009fea8782860162009f14565b6040830152506060830135828111156200a00357600080fd5b6200a0118782860162009f14565b60608301525095945050505050565b600080600080604085870312156200a03757600080fd5b84356001600160401b03808211156200a04f57600080fd5b6200a05d8883890162009971565b909650945060208701359150808211156200a07757600080fd5b5062009e758782880162009971565b600181811c908216806200a09b57607f821691505b6020821081036200207c57634e487b7160e01b600052602260045260246000fd5b6001600160a01b0392831681529116602082015260400190565b8183823760009101908152919050565b634e487b7160e01b600052601160045260246000fd5b6000826200a11a57634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111562005a875762005a876200a0e6565b6000602082840312156200a14857600080fd5b5051919050565b8381526020810183905260608101600383106200a17c57634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200a1b5576200a1b56200a0e6565b5060010190565b8181038181111562005a875762005a876200a0e6565b808202811582820484141762005a875762005a876200a0e6565b6040815260006200a2016040830185620099f1565b905060018060a01b03831660208301529392505050565b6001600160401b038281168282160390808211156200a23b576200a23b6200a0e6565b5092915050565b6000808335601e198436030181126200a25a57600080fd5b8301803591506001600160401b038211156200a27557600080fd5b6020019150368190038213156200264a57600080fd5b601f8211156200643c57600081815260208120601f850160051c810160208610156200a2b45750805b601f850160051c820191505b81811015620062a8578281556001016200a2c0565b600019600383901b1c191660019190911b1790565b6001600160401b038311156200a304576200a3046200985f565b6200a31c836200a31583546200a086565b836200a28b565b6000601f8411600181146200a34f57600085156200a33a5750838201355b6200a34686826200a2d5565b8455506200855e565b600083815260209020601f19861690835b828110156200a38257868501358255602094850194600190920191016200a360565b50868210156200a3a05760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b6200a3be82836200a242565b6001600160401b038111156200a3d8576200a3d86200985f565b6200a3f0816200a3e985546200a086565b856200a28b565b6000601f8211600181146200a42357600083156200a40e5750838201355b6200a41a84826200a2d5565b8655506200a480565b600085815260209020601f19841690835b828110156200a45657868501358255602094850194600190920191016200a434565b50848210156200a4745760001960f88660031b161c19848701351681555b505060018360011b0185555b505050506200a49360208301836200a242565b6200a4a38183600186016200a2ea565b50506200a4b460408301836200a242565b6200a4c48183600286016200a2ea565b50506200a4d560608301836200a242565b6200a4e58183600386016200a2ea565b50505050565b81356200a4f88162009b68565b6001600160401b03811690508154816001600160401b0319821617835560208401356200a5258162009b68565b6fffffffffffffffff0000000000000000604091821b166fffffffffffffffffffffffffffffffff198316841781178555908501356200a5658162009b68565b6001600160c01b0319929092169092179190911760809190911b67ffffffffffffffff60801b1617905550565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000620099696020830184866200a592565b634e487b7160e01b600052603160045260246000fd5b6000816200a5f9576200a5f96200a0e6565b506000190190565b6040815260006200a6176040830186886200a592565b82810360208401526200a62c8185876200a592565b979650505050505050565b81516001600160401b038111156200a653576200a6536200985f565b6200a66b816200a66484546200a086565b846200a28b565b602080601f8311600181146200a69f57600084156200a68a5750858301515b6200a69685826200a2d5565b865550620062a8565b600085815260208120601f198616915b828110156200a6d0578886015182559484019460019091019084016200a6af565b50858210156200a6ef5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60008083546200a70f816200a086565b600182811680156200a72a57600181146200a740576200a771565b60ff19841687528215158302870194506200a771565b8760005260208060002060005b858110156200a7685781548a8201529084019082016200a74d565b50505082870194505b50929695505050505050565b6bffffffffffffffffffffffff198360601b1681526000600383106200a7b357634e487b7160e01b600052602160045260246000fd5b5060f89190911b6014820152601501919050565b6bffffffffffffffffffffffff198560601b16815282846014830137601492019182015260340192915050565b600086516200a808818460208b01620097f6565b82018587823760009086019081528385823760009301928352509095945050505050565b6001600160a01b038316815260406020820181905260009062009969908301846200981c565b600082516200a866818460208701620097f6565b919091019291505056fe608060405260405162000e9638038062000e96833981016040819052620000269162000497565b828162000036828260006200004d565b50620000449050826200008a565b505050620005ca565b6200005883620000e5565b600082511180620000665750805b1562000085576200008383836200012760201b620001691760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000b562000156565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000e2816200018f565b50565b620000f08162000244565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200014f838360405180606001604052806027815260200162000e6f60279139620002f8565b9392505050565b60006200018060008051602062000e4f83398151915260001b6200037760201b620001951760201c565b546001600160a01b0316919050565b6001600160a01b038116620001fa5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200022360008051602062000e4f83398151915260001b6200037760201b620001951760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6200025a816200037a60201b620001981760201c565b620002be5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001f1565b80620002237f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200037760201b620001951760201c565b6060600080856001600160a01b03168560405162000317919062000577565b600060405180830381855af49150503d806000811462000354576040519150601f19603f3d011682016040523d82523d6000602084013e62000359565b606091505b5090925090506200036d8683838762000389565b9695505050505050565b90565b6001600160a01b03163b151590565b60608315620003fd578251600003620003f5576001600160a01b0385163b620003f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001f1565b508162000409565b62000409838362000411565b949350505050565b815115620004225781518083602001fd5b8060405162461bcd60e51b8152600401620001f1919062000595565b80516001600160a01b03811681146200045657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200048e57818101518382015260200162000474565b50506000910152565b600080600060608486031215620004ad57600080fd5b620004b8846200043e565b9250620004c8602085016200043e565b60408501519092506001600160401b0380821115620004e657600080fd5b818601915086601f830112620004fb57600080fd5b8151818111156200051057620005106200045b565b604051601f8201601f19908116603f011681019083821181831017156200053b576200053b6200045b565b816040528281528960208487010111156200055557600080fd5b6200056883602083016020880162000471565b80955050505050509250925092565b600082516200058b81846020870162000471565b9190910192915050565b6020815260008251806020840152620005b681604085016020870162000471565b601f01601f19169190910160400192915050565b61087580620005da6000396000f3fe60806040523661001357610011610017565b005b6100115b61001f6101a7565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a576100536101da565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a57610053610231565b63070d7c6960e41b6001600160e01b031982160161009a57610053610277565b621eb96f60e61b6001600160e01b03198216016100b9576100536102a8565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102e8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102fc565b565b606061018e83836040518060600160405280602781526020016108426027913961030c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101e4610384565b60006101f33660048184610695565b81019061020091906106db565b905061021d8160405180602001604052806000815250600061038f565b505060408051602081019091526000815290565b60606000806102433660048184610695565b810190610250919061070c565b915091506102608282600161038f565b604051806020016040528060008152509250505090565b6060610281610384565b60006102903660048184610695565b81019061029d91906106db565b905061021d816103bb565b60606102b2610384565b60006102bc6101a7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102f2610384565b60006102bc610412565b610167610307610412565b610421565b6060600080856001600160a01b03168560405161032991906107f2565b600060405180830381855af49150503d8060008114610364576040519150601f19603f3d011682016040523d82523d6000602084013e610369565b606091505b509150915061037a86838387610445565b9695505050505050565b341561016757600080fd5b610398836104c6565b6000825111806103a55750805b156103b6576103b48383610169565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e46101a7565b604080516001600160a01b03928316815291841660208301520160405180910390a161040f81610506565b50565b600061041c6105af565b905090565b3660008037600080366000845af43d6000803e808015610440573d6000f35b3d6000fd5b606083156104b45782516000036104ad576001600160a01b0385163b6104ad5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b50816104be565b6104be83836105d7565b949350505050565b6104cf81610601565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661056b5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101cb565b8151156105e75781518083602001fd5b8060405162461bcd60e51b815260040161014e919061080e565b6001600160a01b0381163b61066e5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61058e565b600080858511156106a557600080fd5b838611156106b257600080fd5b5050820193919092039150565b80356001600160a01b03811681146106d657600080fd5b919050565b6000602082840312156106ed57600080fd5b61018e826106bf565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561071f57600080fd5b610728836106bf565b9150602083013567ffffffffffffffff8082111561074557600080fd5b818501915085601f83011261075957600080fd5b81358181111561076b5761076b6106f6565b604051601f8201601f19908116603f01168101908382118183101715610793576107936106f6565b816040528281528860208487010111156107ac57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107e95781810151838201526020016107d1565b50506000910152565b600082516108048184602087016107ce565b9190910192915050565b602081526000825180602084015261082d8160408501602087016107ce565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a164736f6c6343000811000ab53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a164736f6c6343000811000a \ No newline at end of file diff --git a/core/systemcontracts/fermi/mainnet/StakeHubContract b/core/systemcontracts/fermi/mainnet/StakeHubContract new file mode 100644 index 0000000000..0cbfffd80d --- /dev/null +++ b/core/systemcontracts/fermi/mainnet/StakeHubContract @@ -0,0 +1 @@ +6080604052600436106200043b5760003560e01c806386d545061162000233578063ca47908f116200012f578063dd42a1dd11620000b9578063f1f74d841162000084578063f1f74d841462000d75578063f80a34021462000d8d578063fb50b31f1462000db2578063fc0c5ff11462000dd7578063ff69ab611462000def57600080fd5b8063dd42a1dd1462000ce2578063e8f67c3b1462000d09578063e992aaf51462000d21578063efdbf0e11462000d3957600080fd5b8063d7c2dfc811620000fa578063d7c2dfc81462000c68578063d8ca511f1462000c8d578063daacdb661462000ca5578063dbda7fb31462000cbd57600080fd5b8063ca47908f1462000bd1578063cbb04d9d1462000be9578063d115a2061462000c2a578063d6ca429d1462000c4357600080fd5b8063b187bd2611620001bd578063bfff04751162000188578063bfff04751462000b58578063c166f58a1462000b7d578063c38fbec81462000b94578063c473318f1462000bb9578063c8509d81146200095157600080fd5b8063b187bd261462000ac5578063baa7199e1462000ae5578063bdceadf31462000b0a578063bff02e201462000b2257600080fd5b8063a1832e6411620001fe578063a1832e641462000a22578063a43569b31462000a47578063aad3ec961462000a7b578063ac4317511462000aa057600080fd5b806386d54506146200098e5780638a4d3fa814620009c85780638cd22b2214620009e6578063982ef0a71462000a0b57600080fd5b80634838d165116200034357806364028fbd11620002cd57806375cc7d89116200029857806375cc7d8914620008fc57806376e7d6d614620009215780638129fc1c1462000939578063831d65d114620009515780638456cb59146200097657600080fd5b806364028fbd1462000837578063663706d3146200084e5780636ec01b27146200087f5780636f8e2fa414620008d757600080fd5b80634e6fd6c4116200030e5780634e6fd6c4146200079e5780635949187114620007b65780635e7cc1c914620007db57806363a036b5146200080057600080fd5b80634838d16514620006ea57806349f41a42146200072f5780634a49ac4c14620007545780634d99dd16146200077957600080fd5b80631fab701511620003c5578063384099881162000390578063384099881462000663578063417c73a7146200067b578063449ecfe614620006a057806345211bfd14620006c557600080fd5b80631fab701514620005aa5780632b727c8614620005cf5780632e8e8c7114620005f4578063367dad49146200062e57600080fd5b80630e9fbf5111620004065780630e9fbf5114620004f35780631182b875146200051857806317b4f353146200054c5780631fa8882b146200059157600080fd5b8063046f7da2146200045b578063059ddd2214620004735780630661806e14620004b5578063092193ab14620004dc57600080fd5b36620004565760345460ff166001146200045457600080fd5b005b600080fd5b3480156200046857600080fd5b506200045462000e07565b3480156200048057600080fd5b506200049862000492366004620096f0565b62000e99565b6040516001600160a01b0390911681526020015b60405180910390f35b348015620004c257600080fd5b50620004cd60365481565b604051908152602001620004ac565b62000454620004ed366004620096f0565b620012c1565b3480156200050057600080fd5b50620004546200051236600462009752565b62001912565b3480156200052557600080fd5b506200053d6200053736600462009797565b62001c3e565b604051620004ac91906200984a565b3480156200055957600080fd5b50620004986200056b3660046200991c565b80516020818301810180516045825292820191909301209152546001600160a01b031681565b3480156200059e57600080fd5b50620004cd6201518081565b348015620005b757600080fd5b5062000454620005c9366004620099b8565b62001cd6565b348015620005dc57600080fd5b5062000498620005ee366004620096f0565b6200202d565b3480156200060157600080fd5b506200049862000613366004620096f0565b604d602052600090815260409020546001600160a01b031681565b3480156200063b57600080fd5b50620006536200064d366004620099b8565b62002082565b604051620004ac92919062009a37565b3480156200067057600080fd5b50620004cd60375481565b3480156200068857600080fd5b50620004546200069a366004620096f0565b62002651565b348015620006ad57600080fd5b5062000454620006bf366004620096f0565b620026d3565b348015620006d257600080fd5b5062000454620006e4366004620096f0565b620028b8565b348015620006f757600080fd5b506200071e62000709366004620096f0565b60016020526000908152604090205460ff1681565b6040519015158152602001620004ac565b3480156200073c57600080fd5b50620004546200074e366004620096f0565b62002a91565b3480156200076157600080fd5b506200045462000773366004620096f0565b62002cad565b3480156200078657600080fd5b50620004546200079836600462009ad6565b62002d29565b348015620007ab57600080fd5b506200049861dead81565b348015620007c357600080fd5b5062000454620007d536600462009b14565b62003359565b348015620007e857600080fd5b5062000454620007fa36600462009b7e565b62004152565b3480156200080d57600080fd5b50620008256200081f36600462009ba5565b6200437c565b604051620004ac949392919062009bc8565b620004546200084836600462009c84565b62004a21565b3480156200085b57600080fd5b50620004cd6200086d366004620096f0565b60446020526000908152604090205481565b3480156200088c57600080fd5b50620008a46200089e366004620096f0565b62005070565b6040805182516001600160401b0390811682526020808501518216908301529282015190921690820152606001620004ac565b348015620008e457600080fd5b506200053d620008f6366004620096f0565b62005115565b3480156200090957600080fd5b50620004546200091b366004620096f0565b62005541565b3480156200092e57600080fd5b50620004cd603d5481565b3480156200094657600080fd5b506200045462005713565b3480156200095e57600080fd5b50620004546200097036600462009797565b620058de565b3480156200098357600080fd5b50620004546200593c565b3480156200099b57600080fd5b5062000498620009ad366004620096f0565b6043602052600090815260409020546001600160a01b031681565b348015620009d557600080fd5b50620004cd670de0b6b3a764000081565b348015620009f357600080fd5b50620004cd62000a0536600462009ad6565b620059d4565b6200045462000a1c36600462009d5a565b62005a8d565b34801562000a2f57600080fd5b506200045462000a41366004620099b8565b6200613b565b34801562000a5457600080fd5b5062000a6c62000a66366004620096f0565b62006441565b604051620004ac919062009d92565b34801562000a8857600080fd5b506200045462000a9a36600462009ad6565b6200672e565b34801562000aad57600080fd5b506200045462000abf36600462009e0f565b6200679b565b34801562000ad257600080fd5b5060005462010000900460ff166200071e565b34801562000af257600080fd5b506200045462000b0436600462009e81565b6200774d565b34801562000b1757600080fd5b50620004cd603c5481565b34801562000b2f57600080fd5b5062000b4762000b4136600462009ba5565b6200791a565b604051620004ac9392919062009eda565b34801562000b6557600080fd5b50620004cd62000b77366004620096f0565b62007af6565b34801562000b8a57600080fd5b50620004cd600581565b34801562000ba157600080fd5b506200045462000bb3366004620096f0565b62007b44565b34801562000bc657600080fd5b50620004cd60385481565b34801562000bde57600080fd5b50620004cd604e5481565b34801562000bf657600080fd5b5062000c0e62000c08366004620096f0565b62007e06565b60408051938452911515602084015290820152606001620004ac565b34801562000c3757600080fd5b50620004cd620186a081565b34801562000c5057600080fd5b506200045462000c6236600462009f37565b62008249565b34801562000c7557600080fd5b506200045462000c873660046200a020565b6200846f565b34801562000c9a57600080fd5b50620004cd603b5481565b34801562000cb257600080fd5b50620004cd60495481565b34801562000cca57600080fd5b506200049862000cdc366004620096f0565b62008565565b34801562000cef57600080fd5b50600054630100000090046001600160a01b031662000498565b34801562000d1657600080fd5b50620004cd60355481565b34801562000d2e57600080fd5b50620004cd603a5481565b34801562000d4657600080fd5b50620004cd62000d583660046200991c565b805160208183018101805160468252928201919093012091525481565b34801562000d8257600080fd5b50620004cd603e5481565b34801562000d9a57600080fd5b50620004cd62000dac36600462009ad6565b6200898f565b34801562000dbf57600080fd5b506200045462000dd136600462009e0f565b62008a00565b34801562000de457600080fd5b50620004cd60395481565b34801562000dfc57600080fd5b50620004cd604a5481565b600054630100000090046001600160a01b0316331462000e3a576040516306fbb1e360e01b815260040160405180910390fd5b60005462010000900460ff1662000e6457604051636cd6020160e01b815260040160405180910390fd5b6000805462ff0000191681556040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f99190a1565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054929384939091608084019162000f04906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462000f32906200a086565b801562000f835780601f1062000f575761010080835404028352916020019162000f83565b820191906000526020600020905b81548152906001019060200180831162000f6557829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462000fae906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462000fdc906200a086565b80156200102d5780601f1062001001576101008083540402835291602001916200102d565b820191906000526020600020905b8154815290600101906020018083116200100f57829003601f168201915b5050505050815260200160018201805462001048906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462001076906200a086565b8015620010c75780601f106200109b57610100808354040283529160200191620010c7565b820191906000526020600020905b815481529060010190602001808311620010a957829003601f168201915b50505050508152602001600282018054620010e2906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462001110906200a086565b8015620011615780601f10620011355761010080835404028352916020019162001161565b820191906000526020600020905b8154815290600101906020018083116200114357829003601f168201915b505050505081526020016003820180546200117c906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620011aa906200a086565b8015620011fb5780601f10620011cf57610100808354040283529160200191620011fb565b820191906000526020600020905b815481529060010190602001808311620011dd57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200129a575050509190925250509051949350505050565b3361100014620012ed57604051630f22c43960e41b815261100060048201526024015b60405180910390fd5b6001600160a01b0380821660009081526043602090815260408083205484168084526041835281842082516101808101845281548716815260018201548716948101949094526002810154909516918301919091526003840154606083015260048401805491949160808401919062001366906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462001394906200a086565b8015620013e55780601f10620013b957610100808354040283529160200191620013e5565b820191906000526020600020905b815481529060010190602001808311620013c757829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462001410906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200143e906200a086565b80156200148f5780601f1062001463576101008083540402835291602001916200148f565b820191906000526020600020905b8154815290600101906020018083116200147157829003601f168201915b50505050508152602001600182018054620014aa906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620014d8906200a086565b8015620015295780601f10620014fd5761010080835404028352916020019162001529565b820191906000526020600020905b8154815290600101906020018083116200150b57829003601f168201915b5050505050815260200160028201805462001544906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462001572906200a086565b8015620015c35780601f106200159757610100808354040283529160200191620015c3565b820191906000526020600020905b815481529060010190602001808311620015a557829003601f168201915b50505050508152602001600382018054620015de906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200160c906200a086565b80156200165d5780601f1062001631576101008083540402835291602001916200165d565b820191906000526020600020905b8154815290600101906020018083116200163f57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620016fc575050509190925250505060408101519091506001600160a01b031615806200173957508060e001515b15620017f657604051611002903490600081818185875af1925050503d806000811462001783576040519150601f19603f3d011682016040523d82523d6000602084013e62001788565b606091505b505050816001600160a01b03167ffc8bff675087dd2da069cc3fb517b9ed001e19750c0865241a5542dba1ba170d604051620017e99060208082526011908201527024a72b20a624a22fab20a624a220aa27a960791b604082015260600190565b60405180910390a2505050565b60408181015160c0830151519151632f303ebb60e11b81526001600160401b0390921660048301526001600160a01b031690635e607d769034906024016000604051808303818588803b1580156200184d57600080fd5b505af115801562001862573d6000803e3d6000fd5b5050505050816001600160a01b03167fe34918ff1c7084970068b53fd71ad6d8b04e9f15d3886cbf006443e6cdc52ea634604051620018a391815260200190565b60405180910390a26040808201519051633041949b60e01b815261200591633041949b91620018d8919086906004016200a0bc565b600060405180830381600087803b158015620018f357600080fd5b505af115801562001908573d6000803e3d6000fd5b5050505050505b50565b33611001146200193a57604051630f22c43960e41b81526110016004820152602401620012e4565b60005462010000900460ff16156200196557604051631785c68160e01b815260040160405180910390fd5b6000604583836040516200197b9291906200a0d6565b908152604051908190036020019020546001600160a01b03169050620019a3603f8262008c39565b620019c15760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038116600090815260416020526040812090620019e962015180426200a0fc565b604a546000828152604b60205260409020549192501162001a1d5760405163bd52fcdb60e01b815260040160405180910390fd5b6000818152604b6020526040812080546001929062001a3e9084906200a11f565b909155505060405160469062001a5890879087906200a0d6565b90815260200160405180910390205460001415801562001aa9575042620151806046878760405162001a8c9291906200a0d6565b90815260200160405180910390205462001aa791906200a11f565b105b1562001ac857604051631898eb6b60e01b815260040160405180910390fd5b60008062001ad885600262008c5c565b915091508162001afb57604051631b919bb160e11b815260040160405180910390fd5b6002840154603c5460405163045bc4d160e41b815260048101919091526000916001600160a01b0316906345bc4d10906024016020604051808303816000875af115801562001b4e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001b7491906200a135565b905062001b82858362008ce4565b856001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb28383600260405162001bc2939291906200a14f565b60405180910390a26002850154604051633041949b60e01b815261200591633041949b9162001c00916001600160a01b0316908a906004016200a0bc565b600060405180830381600087803b15801562001c1b57600080fd5b505af115801562001c30573d6000803e3d6000fd5b505050505050505050505050565b6060336120001462001c6857604051630f22c43960e41b81526120006004820152602401620012e4565b60005462010000900460ff161562001c9357604051631785c68160e01b815260040160405180910390fd5b6034805460ff1916600117905560405162461bcd60e51b815260206004820152600a60248201526919195c1c9958d85d195960b21b6044820152606401620012e4565b60005462010000900460ff161562001d0157604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562001d335760405163b1d02c3d60e01b815260040160405180910390fd5b62001d3d62008dd8565b62001d4a603f8262008c39565b62001d685760405163056e881160e01b815260040160405180910390fd5b62001d7262008e23565b600082900362001d9557604051636490ffd360e01b815260040160405180910390fd5b600062001da162008dd8565b6001600160a01b0381166000908152604f602052604090208054604e54929350909162001dcf86836200a11f565b111562001def5760405163091af98560e21b815260040160405180910390fd5b60005b8581101562001ed557600087878381811062001e125762001e126200a18a565b905060200201350362001e3857604051636490ffd360e01b815260040160405180910390fd5b600062001e478260016200a11f565b90505b8681101562001ebf5787878281811062001e685762001e686200a18a565b9050602002013588888481811062001e845762001e846200a18a565b905060200201350362001eaa57604051632205e3c760e11b815260040160405180910390fd5b8062001eb6816200a1a0565b91505062001e4a565b508062001ecc816200a1a0565b91505062001df2565b5060005b8581101562001f745760005b8281101562001f5e5783818154811062001f035762001f036200a18a565b906000526020600020015488888481811062001f235762001f236200a18a565b905060200201350362001f4957604051632205e3c760e11b815260040160405180910390fd5b8062001f55816200a1a0565b91505062001ee5565b508062001f6b816200a1a0565b91505062001ed9565b5060005b8581101562001908578287878381811062001f975762001f976200a18a565b835460018101855560009485526020948590209190940292909201359190920155506001600160a01b0384167f7c4ff4c9a343a2daef608f3b5a91016e994a15fc0ef8611109e4f45823249f2988888481811062001ff95762001ff96200a18a565b905060200201356040516200201091815260200190565b60405180910390a28062002024816200a1a0565b91505062001f78565b6000816200203d603f8262008c39565b6200205b5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038084166000908152604160205260409020600d01541691505b50919050565b60608082806001600160401b03811115620020a157620020a16200985f565b604051908082528060200260200182016040528015620020cb578160200160208202803683370190505b509250806001600160401b03811115620020e957620020e96200985f565b6040519080825280602002602001820160405280156200211e57816020015b6060815260200190600190039081620021085790505b50915060005b81811015620026475760008686838181106200214457620021446200a18a565b90506020020160208101906200215b9190620096f0565b6001600160a01b03808216600090815260416020908152604080832081516101808101835281548616815260018201548616938101939093526002810154909416908201526003830154606082015260048301805494955091939092916080840191620021c8906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620021f6906200a086565b8015620022475780601f106200221b5761010080835404028352916020019162002247565b820191906000526020600020905b8154815290600101906020018083116200222957829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462002272906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620022a0906200a086565b8015620022f15780601f10620022c557610100808354040283529160200191620022f1565b820191906000526020600020905b815481529060010190602001808311620022d357829003601f168201915b505050505081526020016001820180546200230c906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200233a906200a086565b80156200238b5780601f106200235f576101008083540402835291602001916200238b565b820191906000526020600020905b8154815290600101906020018083116200236d57829003601f168201915b50505050508152602001600282018054620023a6906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620023d4906200a086565b8015620024255780601f10620023f95761010080835404028352916020019162002425565b820191906000526020600020905b8154815290600101906020018083116200240757829003601f168201915b5050505050815260200160038201805462002440906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200246e906200a086565b8015620024bf5780601f106200249357610100808354040283529160200191620024bf565b820191906000526020600020905b815481529060010190602001808311620024a157829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200255e57505050505081525050905080600001518684815181106200259757620025976200a18a565b6001600160a01b039283166020918202929092018101919091529083166000908152604f8252604090819020805482518185028101850190935280835291929091908301828280156200260a57602002820191906000526020600020905b815481526020019060010190808311620025f5575b50505050508584815181106200262457620026246200a18a565b6020026020010181905250505080806200263e906200a1a0565b91505062002124565b50505b9250929050565b600054630100000090046001600160a01b0316331462002684576040516306fbb1e360e01b815260040160405180910390fd5b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f7fd26be6fc92aff63f1f4409b2b2ddeb272a888031d7f55ec830485ec61941869190a250565b60005462010000900460ff1615620026fe57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620027305760405163b1d02c3d60e01b815260040160405180910390fd5b806200273e603f8262008c39565b6200275c5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0382166000908152604160205260409020600a81015460ff166200279a57604051634b6b857d60e01b815260040160405180910390fd5b6036546002820154604051630913db4760e01b81526001600160a01b03868116600483015290911690630913db4790602401602060405180830381865afa158015620027ea573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200281091906200a135565b101562002830576040516317b204bf60e11b815260040160405180910390fd5b4281600b01541115620028565760405163170cb76760e21b815260040160405180910390fd5b600a8101805460ff1916905560498054600191906000906200287a9084906200a1bc565b90915550506040516001600160a01b038416907f9390b453426557da5ebdc31f19a37753ca04addf656d32f35232211bb2af3f1990600090a2505050565b60005462010000900460ff1615620028e357604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620029155760405163b1d02c3d60e01b815260040160405180910390fd5b6200291f62008e36565b6200292c603f8262008c39565b6200294a5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0382166200297257604051636520611b60e11b815260040160405180910390fd5b6001600160a01b038281166000908152604360205260409020541615620029ac57604051631e6f587560e11b815260040160405180910390fd5b6000620029b862008e36565b6001600160a01b0381166000908152604160205260409020600c810154919250904290620029eb9062015180906200a11f565b111562002a0b57604051631f92cdbd60e11b815260040160405180910390fd5b80546001600160a01b039081166000908152604460209081526040808320429081905585548986166001600160a01b031991821681178855600c88019290925581855260439093528184208054958816959093168517909255519092917f6e4e747ca35203f16401c69805c7dd52fff67ef60b0ebc5c7fe16890530f223591a350505050565b3362002a9f603f8262008c39565b62002abd5760405163056e881160e01b815260040160405180910390fd5b60005462010000900460ff161562002ae857604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562002b1a5760405163b1d02c3d60e01b815260040160405180910390fd5b6001600160a01b038281166000908152604d6020526040902054161562002b545760405163bebdc75760e01b815260040160405180910390fd5b62002b61603f8362008c39565b1562002b805760405163bebdc75760e01b815260040160405180910390fd5b336000818152604160205260409020600d01546001600160a01b03908116908416810362002bc15760405163bebdc75760e01b815260040160405180910390fd5b6001600160a01b0381161562002bf8576001600160a01b0381166000908152604d6020526040902080546001600160a01b03191690555b6001600160a01b038281166000908152604160205260409020600d0180546001600160a01b03191691861691821790551562002c5d576001600160a01b038481166000908152604d6020526040902080546001600160a01b0319169184169190911790555b836001600160a01b0316816001600160a01b0316836001600160a01b03167fcbb728765de145e99c00e8ae32a325231e850359b7b8a6da3b84d672ab3f1d0a60405160405180910390a450505050565b600054630100000090046001600160a01b0316331462002ce0576040516306fbb1e360e01b815260040160405180910390fd5b6001600160a01b038116600081815260016020526040808220805460ff19169055517fe0db3499b7fdc3da4cddff5f45d694549c19835e7f719fb5606d3ad1a5de40119190a250565b60005462010000900460ff161562002d5457604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562002d865760405163b1d02c3d60e01b815260040160405180910390fd5b8162002d94603f8262008c39565b62002db25760405163056e881160e01b815260040160405180910390fd5b8160000362002dd457604051639811e0c760e01b815260040160405180910390fd5b6001600160a01b038084166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054339491608084019162002e3c906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462002e6a906200a086565b801562002ebb5780601f1062002e8f5761010080835404028352916020019162002ebb565b820191906000526020600020905b81548152906001019060200180831162002e9d57829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462002ee6906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462002f14906200a086565b801562002f655780601f1062002f395761010080835404028352916020019162002f65565b820191906000526020600020905b81548152906001019060200180831162002f4757829003601f168201915b5050505050815260200160018201805462002f80906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462002fae906200a086565b801562002fff5780601f1062002fd35761010080835404028352916020019162002fff565b820191906000526020600020905b81548152906001019060200180831162002fe157829003601f168201915b505050505081526020016002820180546200301a906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003048906200a086565b8015620030995780601f106200306d5761010080835404028352916020019162003099565b820191906000526020600020905b8154815290600101906020018083116200307b57829003601f168201915b50505050508152602001600382018054620030b4906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620030e2906200a086565b8015620031335780601f10620031075761010080835404028352916020019162003133565b820191906000526020600020905b8154815290600101906020018083116200311557829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620031d2575050509190925250505060408082015190516326ccee8b60e11b81526001600160a01b0385811660048301526024820188905292935060009290911690634d99dd16906044016020604051808303816000875af11580156200324c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200327291906200a135565b9050826001600160a01b0316866001600160a01b03167f3aace7340547de7b9156593a7652dc07ee900cea3fd8f82cb6c9d38b408298028784604051620032c3929190918252602082015260400190565b60405180910390a3856001600160a01b0316836001600160a01b031603620032f057620032f08662008e77565b6040808301519051633041949b60e01b815261200591633041949b916200331d919087906004016200a0bc565b600060405180830381600087803b1580156200333857600080fd5b505af11580156200334d573d6000803e3d6000fd5b50505050505050505050565b60005462010000900460ff16156200338457604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620033b65760405163b1d02c3d60e01b815260040160405180910390fd5b83620033c4603f8262008c39565b620033e25760405163056e881160e01b815260040160405180910390fd5b83620033f0603f8262008c39565b6200340e5760405163056e881160e01b815260040160405180910390fd5b6034805460ff1916600117905560008490036200343e57604051639811e0c760e01b815260040160405180910390fd5b846001600160a01b0316866001600160a01b031603620034715760405163f0e3e62960e01b815260040160405180910390fd5b6001600160a01b0380871660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608201526004830180543394916080840191620034d9906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003507906200a086565b8015620035585780601f106200352c5761010080835404028352916020019162003558565b820191906000526020600020905b8154815290600101906020018083116200353a57829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462003583906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620035b1906200a086565b8015620036025780601f10620035d65761010080835404028352916020019162003602565b820191906000526020600020905b815481529060010190602001808311620035e457829003601f168201915b505050505081526020016001820180546200361d906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200364b906200a086565b80156200369c5780601f1062003670576101008083540402835291602001916200369c565b820191906000526020600020905b8154815290600101906020018083116200367e57829003601f168201915b50505050508152602001600282018054620036b7906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620036e5906200a086565b8015620037365780601f106200370a5761010080835404028352916020019162003736565b820191906000526020600020905b8154815290600101906020018083116200371857829003601f168201915b5050505050815260200160038201805462003751906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200377f906200a086565b8015620037d05780601f10620037a457610100808354040283529160200191620037d0565b820191906000526020600020905b815481529060010190602001808311620037b257829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200386f57505050919092525050506001600160a01b03808916600090815260416020908152604080832081516101808101835281548616815260018201548616938101939093526002810154909416908201526003830154606082015260048301805494955091939092916080840191620038fa906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003928906200a086565b8015620039795780601f106200394d5761010080835404028352916020019162003979565b820191906000526020600020905b8154815290600101906020018083116200395b57829003601f168201915b5050505050815260200160058201604051806080016040529081600082018054620039a4906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620039d2906200a086565b801562003a235780601f10620039f75761010080835404028352916020019162003a23565b820191906000526020600020905b81548152906001019060200180831162003a0557829003601f168201915b5050505050815260200160018201805462003a3e906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003a6c906200a086565b801562003abd5780601f1062003a915761010080835404028352916020019162003abd565b820191906000526020600020905b81548152906001019060200180831162003a9f57829003601f168201915b5050505050815260200160028201805462003ad8906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003b06906200a086565b801562003b575780601f1062003b2b5761010080835404028352916020019162003b57565b820191906000526020600020905b81548152906001019060200180831162003b3957829003601f168201915b5050505050815260200160038201805462003b72906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462003ba0906200a086565b801562003bf15780601f1062003bc55761010080835404028352916020019162003bf1565b820191906000526020600020905b81548152906001019060200180831162003bd357829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b81548152602001906001019080831162003c905750505050508152505090508060e00151801562003cd35750876001600160a01b0316836001600160a01b031614155b1562003cf257604051636468920360e01b815260040160405180910390fd5b60408083015190516352e82ce560e11b81526001600160a01b038581166004830152602482018a9052600092169063a5d059ca906044016020604051808303816000875af115801562003d49573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003d6f91906200a135565b905060375481101562003d955760405163dc6f0bdd60e01b815260040160405180910390fd5b896001600160a01b0316846001600160a01b031614801562003e2a57506036546040808501519051630913db4760e01b81526001600160a01b038d8116600483015290911690630913db4790602401602060405180830381865afa15801562003e02573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003e2891906200a135565b105b1562003e49576040516317b204bf60e11b815260040160405180910390fd5b6000620186a0603a548362003e5f91906200a1d2565b62003e6b91906200a0fc565b9050600083604001516001600160a01b03168260405160006040518083038185875af1925050503d806000811462003ec0576040519150601f19603f3d011682016040523d82523d6000602084013e62003ec5565b606091505b505090508062003ee8576040516312171d8360e31b815260040160405180910390fd5b62003ef482846200a1bc565b60408086015190516317066a5760e21b81526001600160a01b03898116600483015292955060009290911690635c19a95c90869060240160206040518083038185885af115801562003f4a573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019062003f7191906200a135565b9050866001600160a01b03168c6001600160a01b03168e6001600160a01b03167ffdac6e81913996d95abcc289e90f2d8bd235487ce6fe6f821e7d21002a1915b48e858960405162003fd6939291909283526020830191909152604082015260600190565b60405180910390a46040805160028082526060820183526000926020830190803683370190505090508660400151816000815181106200401a576200401a6200a18a565b60200260200101906001600160a01b031690816001600160a01b0316815250508560400151816001815181106200405557620040556200a18a565b6001600160a01b0390921660209283029190910190910152604051634484077560e01b815261200590634484077590620040969084908c906004016200a1ec565b600060405180830381600087803b158015620040b157600080fd5b505af1158015620040c6573d6000803e3d6000fd5b505050508a1562004138576120056001600160a01b031663e5ed5b1e898f6040518363ffffffff1660e01b8152600401620041039291906200a0bc565b600060405180830381600087803b1580156200411e57600080fd5b505af115801562004133573d6000803e3d6000fd5b505050505b50506034805460ff19169055505050505050505050505050565b60005462010000900460ff16156200417d57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620041af5760405163b1d02c3d60e01b815260040160405180910390fd5b620041b962008e36565b620041c6603f8262008c39565b620041e45760405163056e881160e01b815260040160405180910390fd5b6000620041f062008e36565b6001600160a01b0381166000908152604160205260409020600c810154919250904290620042239062015180906200a11f565b11156200424357604051631f92cdbd60e11b815260040160405180910390fd5b60098101546001600160401b03600160401b909104811690851611156200427d5760405163dc81db8560e01b815260040160405180910390fd5b60098101546000906001600160401b039081169086161015620042bb576009820154620042b59086906001600160401b03166200a218565b620042d5565b6009820154620042d5906001600160401b0316866200a218565b60098301546001600160401b039182169250600160801b900416811115620043105760405163dc81db8560e01b815260040160405180910390fd5b60098201805467ffffffffffffffff19166001600160401b03871690811790915542600c8401556040519081526001600160a01b038416907f78cdd96edf59e09cfd4d26ef6ef6c92d166effe6a40970c54821206d541932cb9060200160405180910390a25050505050565b606080606060006200438f603f62008f95565b90508086101562004a18578415620043a85784620043aa565b805b9450600085620043bb88846200a1bc565b11620043d357620043cd87836200a1bc565b620043d5565b855b9050806001600160401b03811115620043f257620043f26200985f565b6040519080825280602002602001820160405280156200441c578160200160208202803683370190505b509450806001600160401b038111156200443a576200443a6200985f565b60405190808252806020026020018201604052801562004464578160200160208202803683370190505b509350806001600160401b038111156200448257620044826200985f565b604051908082528060200260200182016040528015620044b757816020015b6060815260200190600190039081620044a15790505b50925060005b8181101562004a15576000620044e1620044d8838b6200a11f565b603f9062008fa0565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054949550919390929160808401916200454e906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200457c906200a086565b8015620045cd5780601f10620045a157610100808354040283529160200191620045cd565b820191906000526020600020905b815481529060010190602001808311620045af57829003601f168201915b5050505050815260200160058201604051806080016040529081600082018054620045f8906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462004626906200a086565b8015620046775780601f106200464b5761010080835404028352916020019162004677565b820191906000526020600020905b8154815290600101906020018083116200465957829003601f168201915b5050505050815260200160018201805462004692906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620046c0906200a086565b8015620047115780601f10620046e55761010080835404028352916020019162004711565b820191906000526020600020905b815481529060010190602001808311620046f357829003601f168201915b505050505081526020016002820180546200472c906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200475a906200a086565b8015620047ab5780601f106200477f57610100808354040283529160200191620047ab565b820191906000526020600020905b8154815290600101906020018083116200478d57829003601f168201915b50505050508152602001600382018054620047c6906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620047f4906200a086565b8015620048455780601f10620048195761010080835404028352916020019162004845565b820191906000526020600020905b8154815290600101906020018083116200482757829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620048e457505050505081525050905080600001518884815181106200491d576200491d6200a18a565b60200260200101906001600160a01b031690816001600160a01b0316815250508060e00151620049b65780604001516001600160a01b03166315d1f8986040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200498a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620049b091906200a135565b620049b9565b60005b878481518110620049ce57620049ce6200a18a565b6020026020010181815250508060800151868481518110620049f457620049f46200a18a565b602002602001018190525050508062004a0d906200a1a0565b9050620044bd565b50505b92959194509250565b60005462010000900460ff161562004a4c57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562004a7e5760405163b1d02c3d60e01b815260040160405180910390fd5b3362004a8c603f8262008c39565b1562004aab57604051635f28f62b60e01b815260040160405180910390fd5b6001600160a01b038181166000908152604d6020526040902054161562004ae557604051631a0a9b9f60e21b815260040160405180910390fd5b6001600160a01b03888116600090815260436020526040902054161562004b1f57604051631e6f587560e11b815260040160405180910390fd5b60006001600160a01b03166045888860405162004b3e9291906200a0d6565b908152604051908190036020019020546001600160a01b03161462004b76576040516311fdb94760e01b815260040160405180910390fd5b600062004b8483806200a242565b60405160200162004b979291906200a0d6565b60408051601f1981840301815291815281516020928301206000818152604290935291205490915060ff161562004be15760405163c0bf414360e01b815260040160405180910390fd5b600062004bf7670de0b6b3a7640000346200a1bc565b905060365481101562004c1d576040516317b204bf60e11b815260040160405180910390fd5b6001600160a01b038a1662004c4557604051636520611b60e11b815260040160405180910390fd5b61138862004c5a604087016020880162009b7e565b6001600160401b0316118062004ca0575062004c7d604086016020870162009b7e565b6001600160401b031662004c95602087018762009b7e565b6001600160401b0316115b8062004cdf575062004cb9604086016020870162009b7e565b6001600160401b031662004cd4606087016040880162009b7e565b6001600160401b0316115b1562004cfe5760405163dc81db8560e01b815260040160405180910390fd5b62004d4962004d0e85806200a242565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062008fae92505050565b62004d6757604051635dba5ad760e01b815260040160405180910390fd5b62004d76838a8a8a8a62009150565b62004d9457604051631647e3cb60e11b815260040160405180910390fd5b600062004de28462004da787806200a242565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506200928092505050565b905062004df1603f8562009381565b506000838152604260209081526040808320805460ff191660019081179091556001600160a01b0380891680865260419094529190932080548f83166001600160a01b03199182161782559381018054851690931790925560028201805491851691909316179091554260038201556004810162004e718b8d836200a2ea565b50856005820162004e8382826200a3b2565b508790506009820162004e9782826200a4eb565b505042600c8201556001600160a01b038c81166000908152604360205260409081902080546001600160a01b0319169288169290921790915551859060459062004ee5908e908e906200a0d6565b908152602001604051809103902060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550816001600160a01b0316856001600160a01b03168d6001600160a01b03167faecd9fb95e79c75a3a1de93362c6be5fe6ab65770d8614be583884161cd8228d8e8e60405162004f689291906200a5bb565b60405180910390a460408051848152602081018590526001600160a01b0387169182917f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e04910160405180910390a360408051670de0b6b3a7640000808252602082015261dead916001600160a01b038816917f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e04910160405180910390a3604051633041949b60e01b815261200590633041949b906200502e90859089906004016200a0bc565b600060405180830381600087803b1580156200504957600080fd5b505af11580156200505e573d6000803e3d6000fd5b50505050505050505050505050505050565b6040805160608101825260008082526020820181905291810191909152816200509b603f8262008c39565b620050b95760405163056e881160e01b815260040160405180910390fd5b50506001600160a01b031660009081526041602090815260409182902082516060810184526009909101546001600160401b038082168352600160401b8204811693830193909352600160801b90049091169181019190915290565b6001600160a01b0380821660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608281019190915260048401805491949160808401919062005182906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620051b0906200a086565b8015620052015780601f10620051d55761010080835404028352916020019162005201565b820191906000526020600020905b815481529060010190602001808311620051e357829003601f168201915b50505050508152602001600582016040518060800160405290816000820180546200522c906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200525a906200a086565b8015620052ab5780601f106200527f57610100808354040283529160200191620052ab565b820191906000526020600020905b8154815290600101906020018083116200528d57829003601f168201915b50505050508152602001600182018054620052c6906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620052f4906200a086565b8015620053455780601f10620053195761010080835404028352916020019162005345565b820191906000526020600020905b8154815290600101906020018083116200532757829003601f168201915b5050505050815260200160028201805462005360906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200538e906200a086565b8015620053df5780601f10620053b357610100808354040283529160200191620053df565b820191906000526020600020905b815481529060010190602001808311620053c157829003601f168201915b50505050508152602001600382018054620053fa906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005428906200a086565b8015620054795780601f106200544d5761010080835404028352916020019162005479565b820191906000526020600020905b8154815290600101906020018083116200545b57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620055185750505091909252505050608001519392505050565b33611001146200556957604051630f22c43960e41b81526110016004820152602401620012e4565b6001600160a01b038082166000908152604360205260409020541662005591603f8262008c39565b620055af5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038181166000908152604160205260408082206002810154603b54925163045bc4d160e41b81526004810193909352909316906345bc4d10906024016020604051808303816000875af115801562005612573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200563891906200a135565b90506000603d54426200564c91906200a11f565b90506200565a838262008ce4565b836001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb2828460016040516200569a939291906200a14f565b60405180910390a26002830154604051633041949b60e01b815261200591633041949b91620056d8916001600160a01b03169088906004016200a0bc565b600060405180830381600087803b158015620056f357600080fd5b505af115801562005708573d6000803e3d6000fd5b505050505050505050565b600054610100900460ff1615808015620057345750600054600160ff909116105b80620057505750303b15801562005750575060005460ff166001145b620057b55760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401620012e4565b6000805460ff191660011790558015620057d9576000805461ff0019166101001790555b334114620057fa5760405163022d8c9560e31b815260040160405180910390fd5b3a156200581a576040516383f1b1d360e01b815260040160405180910390fd5b611388603555686c6b935b8bbd400000603655670de0b6b3a7640000603755602d60385562093a806039556002603a819055678ac7230489e80000603b55680ad78ebc5ac6200000603c556202a300603d5562278d00603e55604a55620058957308e68ec70fa3b629784fdb28887e206ce8561e0862009398565b80156200190f576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b33612000146200590657604051630f22c43960e41b81526120006004820152602401620012e4565b60405162461bcd60e51b815260206004820152600a60248201526919195c1c9958d85d195960b21b6044820152606401620012e4565b600054630100000090046001600160a01b031633146200596f576040516306fbb1e360e01b815260040160405180910390fd5b60005462010000900460ff16156200599a57604051631785c68160e01b815260040160405180910390fd5b6000805462ff00001916620100001781556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e7529190a1565b6000620059e3603f8462008c39565b62005a015760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0383811660009081526041602052604090819020600201549051636bbf224960e01b815260048101859052911690636bbf2249906024015b602060405180830381865afa15801562005a5e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062005a8491906200a135565b90505b92915050565b60005462010000900460ff161562005ab857604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562005aea5760405163b1d02c3d60e01b815260040160405180910390fd5b8162005af8603f8262008c39565b62005b165760405163056e881160e01b815260040160405180910390fd5b603754349081101562005b3c5760405163dc6f0bdd60e01b815260040160405180910390fd5b6001600160a01b038085166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054339491608084019162005ba4906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005bd2906200a086565b801562005c235780601f1062005bf75761010080835404028352916020019162005c23565b820191906000526020600020905b81548152906001019060200180831162005c0557829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462005c4e906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005c7c906200a086565b801562005ccd5780601f1062005ca15761010080835404028352916020019162005ccd565b820191906000526020600020905b81548152906001019060200180831162005caf57829003601f168201915b5050505050815260200160018201805462005ce8906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005d16906200a086565b801562005d675780601f1062005d3b5761010080835404028352916020019162005d67565b820191906000526020600020905b81548152906001019060200180831162005d4957829003601f168201915b5050505050815260200160028201805462005d82906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005db0906200a086565b801562005e015780601f1062005dd55761010080835404028352916020019162005e01565b820191906000526020600020905b81548152906001019060200180831162005de357829003601f168201915b5050505050815260200160038201805462005e1c906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462005e4a906200a086565b801562005e9b5780601f1062005e6f5761010080835404028352916020019162005e9b565b820191906000526020600020905b81548152906001019060200180831162005e7d57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b81548152602001906001019080831162005f3a5750505050508152505090508060e00151801562005f7d5750856001600160a01b0316826001600160a01b031614155b1562005f9c57604051636468920360e01b815260040160405180910390fd5b60408082015190516317066a5760e21b81526001600160a01b0384811660048301526000921690635c19a95c90869060240160206040518083038185885af115801562005fed573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906200601491906200a135565b9050826001600160a01b0316876001600160a01b03167f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e04838760405162006065929190918252602082015260400190565b60405180910390a36040808301519051633041949b60e01b815261200591633041949b916200609a919087906004016200a0bc565b600060405180830381600087803b158015620060b557600080fd5b505af1158015620060ca573d6000803e3d6000fd5b50505050851562001908576040516372f6ad8f60e11b81526120059063e5ed5b1e90620060fe9086908b906004016200a0bc565b600060405180830381600087803b1580156200611957600080fd5b505af11580156200612e573d6000803e3d6000fd5b5050505050505050505050565b60005462010000900460ff16156200616657604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620061985760405163b1d02c3d60e01b815260040160405180910390fd5b620061a262008dd8565b620061af603f8262008c39565b620061cd5760405163056e881160e01b815260040160405180910390fd5b6000620061d962008dd8565b6001600160a01b0381166000908152604f6020526040812080549293509190859003620062b05760005b818110156200628457836001600160a01b03167f08e60c1b84aab23d99a7262015e647d5ffd6c6e08f78205e1df6774c48e1427a8483815481106200624c576200624c6200a18a565b90600052602060002001546040516200626791815260200190565b60405180910390a2806200627b816200a1a0565b91505062006203565b506001600160a01b0383166000908152604f60205260408120620062a8916200968a565b505050505050565b60005b858110156200640e576000878783818110620062d357620062d36200a18a565b90506020020135905060005b83811015620063f65781858281548110620062fe57620062fe6200a18a565b906000526020600020015403620063e157846200631d6001866200a1bc565b815481106200633057620063306200a18a565b90600052602060002001548582815481106200635057620063506200a18a565b9060005260206000200181905550848054806200637157620063716200a5d1565b60019003818190600052602060002001600090559055838062006394906200a5e7565b945050856001600160a01b03167f08e60c1b84aab23d99a7262015e647d5ffd6c6e08f78205e1df6774c48e1427a83604051620063d391815260200190565b60405180910390a2620063f6565b80620063ed816200a1a0565b915050620062df565b5050808062006405906200a1a0565b915050620062b3565b508154600003620062a8576001600160a01b0383166000908152604f60205260408120620062a8916200968a565b505050565b6200646d6040518060800160405280606081526020016060815260200160608152602001606081525090565b816200647b603f8262008c39565b620064995760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03831660009081526041602052604090819020815160808101909252600501805482908290620064d0906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620064fe906200a086565b80156200654f5780601f1062006523576101008083540402835291602001916200654f565b820191906000526020600020905b8154815290600101906020018083116200653157829003601f168201915b505050505081526020016001820180546200656a906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462006598906200a086565b8015620065e95780601f10620065bd57610100808354040283529160200191620065e9565b820191906000526020600020905b815481529060010190602001808311620065cb57829003601f168201915b5050505050815260200160028201805462006604906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462006632906200a086565b8015620066835780601f10620066575761010080835404028352916020019162006683565b820191906000526020600020905b8154815290600101906020018083116200666557829003601f168201915b505050505081526020016003820180546200669e906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620066cc906200a086565b80156200671d5780601f10620066f1576101008083540402835291602001916200671d565b820191906000526020600020905b815481529060010190602001808311620066ff57829003601f168201915b505050505081525050915050919050565b60005462010000900460ff16156200675957604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff16156200678b5760405163b1d02c3d60e01b815260040160405180910390fd5b62006797828262009431565b5050565b3361100714620067c357604051630f22c43960e41b81526110076004820152602401620012e4565b620068306040518060400160405280601081526020016f1d1c985b9cd9995c91d85cd31a5b5a5d60821b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620068eb5760208114620068625783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620068a59185858083850183828082843760009201919091525092939250506200959a9050565b90506108fc811080620068b9575061271081115b15620068e25784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60355562007708565b6200695c6040518060400160405280601481526020017336b4b729b2b6332232b632b3b0ba34b7b721272160611b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006a2657602081146200698e5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620069d19185858083850183828082843760009201919091525092939250506200959a9050565b9050683635c9adc5dea00000811080620069f4575069152d02c7e14af680000081115b1562006a1d5784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60365562007708565b62006a99604051806040016040528060168152602001756d696e44656c65676174696f6e424e424368616e676560501b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006b60576020811462006acb5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006b0e9185858083850183828082843760009201919091525092939250506200959a9050565b905067016345785d8a000081108062006b2e5750678ac7230489e8000081115b1562006b575784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60375562007708565b62006bd1604051806040016040528060148152602001736d6178456c656374656456616c696461746f727360601b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006c89576020811462006c035783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006c469185858083850183828082843760009201919091525092939250506200959a9050565b905080158062006c5757506101f481115b1562006c805784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60385562007708565b62006cf26040518060400160405280600c81526020016b1d5b989bdb9914195c9a5bd960a21b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006daf576020811462006d245783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006d679185858083850183828082843760009201919091525092939250506200959a9050565b90506203f48081108062006d7d575062278d0081115b1562006da65784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b60395562007708565b62006e1d60405180604001604052806011815260200170726564656c65676174654665655261746560781b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006eca576020811462006e4f5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006e929185858083850183828082843760009201919091525092939250506200959a9050565b9050606481111562006ec15784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603a5562007708565b62006f3a60405180604001604052806013815260200172191bdddb9d1a5b5954db185cda105b5bdd5b9d606a1b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b1562006ffc576020811462006f6c5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f840181900481028201810190925282815260009162006faf9185858083850183828082843760009201919091525092939250506200959a9050565b9050670de0b6b3a764000081108062006fca5750603c548110155b1562006ff35784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603b5562007708565b6200706a6040518060400160405280601181526020017019995b1bdb9e54db185cda105b5bdd5b9d607a1b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b156200712c57602081146200709c5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620070df9185858083850183828082843760009201919091525092939250506200959a9050565b9050678ac7230489e80000811080620070fa5750603b548111155b15620071235784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603c5562007708565b620071996040518060400160405280601081526020016f646f776e74696d654a61696c54696d6560801b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620072565760208114620071cb5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f84018190048102820181019092528281526000916200720e9185858083850183828082843760009201919091525092939250506200959a9050565b905062015180811080620072245750603e548110155b156200724d5784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603d5562007708565b620072c16040518060400160405280600e81526020016d66656c6f6e794a61696c54696d6560901b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b156200737e5760208114620072f35783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620073369185858083850183828082843760009201919091525092939250506200959a9050565b90506203f4808110806200734c5750603d548111155b15620073755784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b603e5562007708565b620073f86040518060400160405280601c81526020017f6d617846656c6f6e794265747765656e42726561746865426c6f636b0000000081525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620074a457602081146200742a5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f84018190048102820181019092528281526000916200746d9185858083850183828082843760009201919091525092939250506200959a9050565b9050806000036200749b5784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b604a5562007708565b620075126040518060400160405280601181526020017039ba30b5b2a43ab1283937ba32b1ba37b960791b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620075d25760148114620075445783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b60006200758c601484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200959a9050565b90506001600160a01b038116620075c05784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b620075cb816200959f565b5062007708565b620076396040518060400160405280600a8152602001696d61784e6f646549447360b01b81525085858080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200953d9050565b15620076e557602081146200766b5783838383604051630a5a604160e01b8152600401620012e494939291906200a601565b604080516020601f8401819004810282018101909252828152600091620076ae9185858083850183828082843760009201919091525092939250506200959a9050565b905080600003620076dc5784848484604051630a5a604160e01b8152600401620012e494939291906200a601565b604e5562007708565b838383836040516325ee20d560e21b8152600401620012e494939291906200a601565b7ff1ce9b2cbf50eeb05769a29e2543fd350cab46894a7dd9978a12d534bb20e633848484846040516200773f94939291906200a601565b60405180910390a150505050565b60005462010000900460ff16156200777857604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620077aa5760405163b1d02c3d60e01b815260040160405180910390fd5b816000816001600160401b03811115620077c857620077c86200985f565b604051908082528060200260200182016040528015620077f2578160200160208202803683370190505b5090506000805b83811015620078f0576200783b8787838181106200781b576200781b6200a18a565b9050602002016020810190620078329190620096f0565b603f9062008c39565b620078595760405163056e881160e01b815260040160405180910390fd5b604160008888848181106200787257620078726200a18a565b9050602002016020810190620078899190620096f0565b6001600160a01b0390811682526020820192909252604001600020600201548451911692508290849083908110620078c557620078c56200a18a565b6001600160a01b0390921660209283029190910190910152620078e8816200a1a0565b9050620077f9565b50604051634484077560e01b8152612005906344840775906200331d90859088906004016200a1ec565b60608060006200792b603f62008f95565b90508085101562007aef57831562007944578362007946565b805b93506000846200795787846200a1bc565b116200796f576200796986836200a1bc565b62007971565b845b9050806001600160401b038111156200798e576200798e6200985f565b604051908082528060200260200182016040528015620079b8578160200160208202803683370190505b509350806001600160401b03811115620079d657620079d66200985f565b60405190808252806020026020018201604052801562007a00578160200160208202803683370190505b50925060005b8181101562007aec5762007a1f620044d882896200a11f565b85828151811062007a345762007a346200a18a565b60200260200101906001600160a01b031690816001600160a01b0316815250506041600086838151811062007a6d5762007a6d6200a18a565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060020160009054906101000a90046001600160a01b031684828151811062007ac15762007ac16200a18a565b6001600160a01b039092166020928302919091019091015262007ae4816200a1a0565b905062007a06565b50505b9250925092565b60008162007b06603f8262008c39565b62007b245760405163056e881160e01b815260040160405180910390fd5b50506001600160a01b03166000908152604160205260409020600c015490565b336110011462007b6c57604051630f22c43960e41b81526110016004820152602401620012e4565b60005462010000900460ff161562007b9757604051631785c68160e01b815260040160405180910390fd5b6001600160a01b038082166000908152604360205260409020541662007bbf603f8262008c39565b62007bdd5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03811660009081526041602052604081209062007c0562015180426200a0fc565b604a546000828152604b60205260409020549192501162007c395760405163bd52fcdb60e01b815260040160405180910390fd5b6000818152604b6020526040812080546001929062007c5a9084906200a11f565b90915550506001600160a01b0384166000908152604460205260409020541580159062007caf57506001600160a01b038416600090815260446020526040902054429062007cad9062015180906200a11f565b105b1562007cce576040516330abb81d60e21b815260040160405180910390fd5b60008062007cde85600062008c5c565b915091508162007d0157604051631b919bb160e11b815260040160405180910390fd5b6002840154603c5460405163045bc4d160e41b815260048101919091526000916001600160a01b0316906345bc4d10906024016020604051808303816000875af115801562007d54573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062007d7a91906200a135565b905062007d88858362008ce4565b856001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb28383600060405162007dc8939291906200a14f565b60405180910390a26002850154604051633041949b60e01b815261200591633041949b91620060fe916001600160a01b0316908a906004016200a0bc565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054929384938493849390929160808401919062007e77906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462007ea5906200a086565b801562007ef65780601f1062007eca5761010080835404028352916020019162007ef6565b820191906000526020600020905b81548152906001019060200180831162007ed857829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462007f21906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462007f4f906200a086565b801562007fa05780601f1062007f745761010080835404028352916020019162007fa0565b820191906000526020600020905b81548152906001019060200180831162007f8257829003601f168201915b5050505050815260200160018201805462007fbb906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462007fe9906200a086565b80156200803a5780601f106200800e576101008083540402835291602001916200803a565b820191906000526020600020905b8154815290600101906020018083116200801c57829003601f168201915b5050505050815260200160028201805462008055906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462008083906200a086565b8015620080d45780601f10620080a857610100808354040283529160200191620080d4565b820191906000526020600020905b815481529060010190602001808311620080b657829003601f168201915b50505050508152602001600382018054620080ef906200a086565b80601f01602080910402602001604051908101604052809291908181526020018280546200811d906200a086565b80156200816e5780601f1062008142576101008083540402835291602001916200816e565b820191906000526020600020905b8154815290600101906020018083116200815057829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200820d5750505091909252505050606081015160e0820151610100909201519097919650945092505050565b60005462010000900460ff16156200827457604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620082a65760405163b1d02c3d60e01b815260040160405180910390fd5b620082b062008e36565b620082bd603f8262008c39565b620082db5760405163056e881160e01b815260040160405180910390fd5b6000620082e762008e36565b6001600160a01b0381166000908152604160205260409020600c8101549192509042906200831a9062015180906200a11f565b11156200833a57604051631f92cdbd60e11b815260040160405180910390fd5b6005810180546200834b906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462008379906200a086565b8015620083ca5780601f106200839e57610100808354040283529160200191620083ca565b820191906000526020600020905b815481529060010190602001808311620083ac57829003601f168201915b5050508287525085916005840191508190620083e790826200a637565b5060208201516001820190620083fe90826200a637565b50604082015160028201906200841590826200a637565b50606082015160038201906200842c90826200a637565b505042600c830155506040516001600160a01b038316907f85d6366b336ade7f106987ec7a8eac1e8799e508aeab045a39d2f63e0dc969d990600090a250505050565b60005462010000900460ff16156200849a57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620084cc5760405163b1d02c3d60e01b815260040160405180910390fd5b828114620084ed576040516341abc80160e01b815260040160405180910390fd5b60005b838110156200855e576200854b8585838181106200851257620085126200a18a565b9050602002016020810190620085299190620096f0565b8484848181106200853e576200853e6200a18a565b9050602002013562009431565b62008556816200a1a0565b9050620084f0565b5050505050565b6001600160a01b0380821660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608201526004830180549293849390916080840191620085d0906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620085fe906200a086565b80156200864f5780601f1062008623576101008083540402835291602001916200864f565b820191906000526020600020905b8154815290600101906020018083116200863157829003601f168201915b50505050508152602001600582016040518060800160405290816000820180546200867a906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620086a8906200a086565b8015620086f95780601f10620086cd57610100808354040283529160200191620086f9565b820191906000526020600020905b815481529060010190602001808311620086db57829003601f168201915b5050505050815260200160018201805462008714906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462008742906200a086565b8015620087935780601f10620087675761010080835404028352916020019162008793565b820191906000526020600020905b8154815290600101906020018083116200877557829003601f168201915b50505050508152602001600282018054620087ae906200a086565b80601f0160208091040260200160405190810160405280929190818152602001828054620087dc906200a086565b80156200882d5780601f1062008801576101008083540402835291602001916200882d565b820191906000526020600020905b8154815290600101906020018083116200880f57829003601f168201915b5050505050815260200160038201805462008848906200a086565b80601f016020809104026020016040519081016040528092919081815260200182805462008876906200a086565b8015620088c75780601f106200889b57610100808354040283529160200191620088c7565b820191906000526020600020905b815481529060010190602001808311620088a957829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620089665750505091909252505050604001519392505050565b60006200899e603f8462008c39565b620089bc5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038381166000908152604160205260409081902060020154905163aa1966cd60e01b81526004810185905291169063aa1966cd9060240162005a40565b60005462010000900460ff161562008a2b57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562008a5d5760405163b1d02c3d60e01b815260040160405180910390fd5b62008a6762008e36565b62008a74603f8262008c39565b62008a925760405163056e881160e01b815260040160405180910390fd5b600062008a9e62008e36565b905062008aaf818787878762009150565b62008acd57604051631647e3cb60e11b815260040160405180910390fd5b60006001600160a01b03166045878760405162008aec9291906200a0d6565b908152604051908190036020019020546001600160a01b03161462008b24576040516311fdb94760e01b815260040160405180910390fd5b6001600160a01b0381166000908152604160205260409020600c810154429062008b539062015180906200a11f565b111562008b7357604051631f92cdbd60e11b815260040160405180910390fd5b4260468260040160405162008b8991906200a6ff565b908152604051908190036020019020556004810162008baa8789836200a2ea565b5042600c820155604051829060459062008bc8908a908a906200a0d6565b90815260405190819003602001812080546001600160a01b039384166001600160a01b0319909116179055908316907f783156582145bd0ff7924fae6953ba054cf1233eb60739a200ddb10de068ff0d9062008c28908a908a906200a5bb565b60405180910390a250505050505050565b6001600160a01b0381166000908152600183016020526040812054151562005a84565b6000806000848460405160200162008c769291906200a77d565b60408051601f1981840301815291815281516020928301206000818152604c9093529120549091504281111562008cb6576000809350935050506200264a565b603e5462008cc590426200a11f565b6000928352604c60205260409092208290555060019590945092505050565b6000600162008cf4603f62008f95565b62008d0091906200a1bc565b604954108015915062008d4c5760018301546040516001600160a01b03909116907f2afdc18061ac21cff7d9f11527ab9c8dec6fabd4edf6f894ed634bebd6a20d4590600090a2505050565b82600b015482111562008d6157600b83018290555b600a83015460ff166200643c57600a8301805460ff191660019081179091556049805460009062008d949084906200a11f565b909155505060018301546040516001600160a01b03909116907f4905ac32602da3fb8b4b7b00c285e5fc4c6c2308cc908b4a1e4e9625a29c90a390600090a2505050565b336000908152604360205260408120546001600160a01b03161562008e145750336000908152604360205260409020546001600160a01b031690565b62008e1e62008e36565b905090565b604e5460000362008e34576005604e555b565b336000908152604d60205260408120546001600160a01b03161562008e725750336000908152604d60205260409020546001600160a01b031690565b503390565b6001600160a01b0381166000908152604160205260409020600a81015460ff161562008ea1575050565b6036546002820154604051630913db4760e01b81526001600160a01b03858116600483015290911690630913db4790602401602060405180830381865afa15801562008ef1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062008f1791906200a135565b1015620067975762008f3981603d544262008f3391906200a11f565b62008ce4565b80546040516335409f7f60e01b81526001600160a01b039091166004820152611000906335409f7f90602401600060405180830381600087803b15801562008f8057600080fd5b505af1158015620062a8573d6000803e3d6000fd5b600062005a87825490565b600062005a8483836200960b565b60008082905060038151108062008fc6575060098151115b1562008fd55750600092915050565b60418160008151811062008fed5762008fed6200a18a565b016020015160f81c10806200901f5750605a816000815181106200901557620090156200a18a565b016020015160f81c115b156200902e5750600092915050565b60015b8151811015620091465760308282815181106200905257620090526200a18a565b016020015160f81c108062009083575060398282815181106200907957620090796200a18a565b016020015160f81c115b8015620090d357506041828281518110620090a257620090a26200a18a565b016020015160f81c1080620090d35750605a828281518110620090c957620090c96200a18a565b016020015160f81c115b80156200912357506061828281518110620090f257620090f26200a18a565b016020015160f81c1080620091235750607a8282815181106200911957620091196200a18a565b016020015160f81c115b1562009133575060009392505050565b6200913e816200a1a0565b905062009031565b5060019392505050565b600060308414158062009164575060608214155b15620091735750600062009277565b6000868686466040516020016200918e94939291906200a7c7565b60408051808303601f1901815282825280516020918201208184528383019092529092506000919060208201818036833701905050905081602082015260008186868a8a604051602001620091e89594939291906200a7f4565b60408051808303601f190181526001808452838301909252925060009190602082018180368337019050509050815160016020830182602086016066600019fa6200923257600080fd5b506000816000815181106200924b576200924b6200a18a565b016020015160f81c9050600181146200926d5760009550505050505062009277565b6001955050505050505b95945050505050565b60008061200361dead6040516200929790620096aa565b6001600160a01b03928316815291166020820152606060408201819052600090820152608001604051809103906000f080158015620092da573d6000803e3d6000fd5b509050806001600160a01b031663f399e22e3486866040518463ffffffff1660e01b81526004016200930e9291906200a82c565b6000604051808303818588803b1580156200932857600080fd5b505af11580156200933d573d6000803e3d6000fd5b50506040516001600160a01b038086169450881692507fd481492e4e93bb36b4c12a5af93f03be3bf04b454dfbc35dd2663fa26f44d5b09150600090a39392505050565b600062005a84836001600160a01b03841662009638565b600054610100900460ff16620094055760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401620012e4565b600080546001600160a01b039092166301000000026301000000600160b81b0319909216919091179055565b816200943f603f8262008c39565b6200945d5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03838116600090815260416020526040808220600201549051635569f64b60e11b8152336004820152602481018690529192169063aad3ec96906044016020604051808303816000875af1158015620094c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620094e791906200a135565b9050336001600160a01b0316846001600160a01b03167ff7a40077ff7a04c7e61f6f26fb13774259ddf1b6bce9ecf26a8276cdd3992683836040516200952f91815260200190565b60405180910390a350505050565b6000816040516020016200955291906200a852565b60405160208183030381529060405280519060200120836040516020016200957b91906200a852565b6040516020818303038152906040528051906020012014905092915050565b015190565b600080546040516001600160a01b0380851693630100000090930416917f44fc1b38a4abaa91ebd1b628a5b259a698f86238c8217d68f516e87769c60c0b91a3600080546001600160a01b039092166301000000026301000000600160b81b0319909216919091179055565b60008260000182815481106200962557620096256200a18a565b9060005260206000200154905092915050565b6000818152600183016020526040812054620096815750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562005a87565b50600062005a87565b50805460008255906000526020600020908101906200190f9190620096b8565b610e96806200a87183390190565b5b80821115620096cf5760008155600101620096b9565b5090565b80356001600160a01b0381168114620096eb57600080fd5b919050565b6000602082840312156200970357600080fd5b62005a8482620096d3565b60008083601f8401126200972157600080fd5b5081356001600160401b038111156200973957600080fd5b6020830191508360208285010111156200264a57600080fd5b600080602083850312156200976657600080fd5b82356001600160401b038111156200977d57600080fd5b6200978b858286016200970e565b90969095509350505050565b600080600060408486031215620097ad57600080fd5b833560ff81168114620097bf57600080fd5b925060208401356001600160401b03811115620097db57600080fd5b620097e9868287016200970e565b9497909650939450505050565b60005b8381101562009813578181015183820152602001620097f9565b50506000910152565b6000815180845262009836816020860160208601620097f6565b601f01601f19169290920160200192915050565b60208152600062005a8460208301846200981c565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156200989a576200989a6200985f565b60405290565b60006001600160401b0380841115620098bd57620098bd6200985f565b604051601f8501601f19908116603f01168101908282118183101715620098e857620098e86200985f565b816040528093508581528686860111156200990257600080fd5b858560208301376000602087830101525050509392505050565b6000602082840312156200992f57600080fd5b81356001600160401b038111156200994657600080fd5b8201601f810184136200995857600080fd5b6200996984823560208401620098a0565b949350505050565b60008083601f8401126200998457600080fd5b5081356001600160401b038111156200999c57600080fd5b6020830191508360208260051b85010111156200264a57600080fd5b60008060208385031215620099cc57600080fd5b82356001600160401b03811115620099e357600080fd5b6200978b8582860162009971565b600081518084526020808501945080840160005b8381101562009a2c5781516001600160a01b03168752958201959082019060010162009a05565b509495945050505050565b60408152600062009a4c6040830185620099f1565b6020838203818501528185518084528284019150828160051b8501018388016000805b8481101562009ac657878403601f19018652825180518086529088019088860190845b8181101562009ab05783518352928a0192918a019160010162009a92565b5050968801969450509186019160010162009a6f565b50919a9950505050505050505050565b6000806040838503121562009aea57600080fd5b62009af583620096d3565b946020939093013593505050565b80358015158114620096eb57600080fd5b6000806000806080858703121562009b2b57600080fd5b62009b3685620096d3565b935062009b4660208601620096d3565b92506040850135915062009b5d6060860162009b03565b905092959194509250565b6001600160401b03811681146200190f57600080fd5b60006020828403121562009b9157600080fd5b813562009b9e8162009b68565b9392505050565b6000806040838503121562009bb957600080fd5b50508035926020909101359150565b60808152600062009bdd6080830187620099f1565b82810360208481019190915286518083528782019282019060005b8181101562009c165784518352938301939183019160010162009bf8565b5050848103604086015286518082528282019350600581901b8201830183890160005b8381101562009c6b57601f1985840301875262009c588383516200981c565b9686019692509085019060010162009c39565b5050809550505050505082606083015295945050505050565b600080600080600080600087890360e081121562009ca157600080fd5b62009cac89620096d3565b975060208901356001600160401b038082111562009cc957600080fd5b62009cd78c838d016200970e565b909950975060408b013591508082111562009cf157600080fd5b62009cff8c838d016200970e565b90975095508591506060605f198401121562009d1a57600080fd5b60608b01945060c08b013592508083111562009d3557600080fd5b505088016080818b03121562009d4a57600080fd5b8091505092959891949750929550565b6000806040838503121562009d6e57600080fd5b62009d7983620096d3565b915062009d896020840162009b03565b90509250929050565b60208152600082516080602084015262009db060a08401826200981c565b90506020840151601f198085840301604086015262009dd083836200981c565b9250604086015191508085840301606086015262009def83836200981c565b92506060860151915080858403016080860152506200927782826200981c565b6000806000806040858703121562009e2657600080fd5b84356001600160401b038082111562009e3e57600080fd5b62009e4c888389016200970e565b9096509450602087013591508082111562009e6657600080fd5b5062009e75878288016200970e565b95989497509550505050565b60008060006040848603121562009e9757600080fd5b83356001600160401b0381111562009eae57600080fd5b62009ebc8682870162009971565b909450925062009ed1905060208501620096d3565b90509250925092565b60608152600062009eef6060830186620099f1565b828103602084015262009f038186620099f1565b915050826040830152949350505050565b600082601f83011262009f2657600080fd5b62005a8483833560208501620098a0565b60006020828403121562009f4a57600080fd5b81356001600160401b038082111562009f6257600080fd5b908301906080828603121562009f7757600080fd5b62009f8162009875565b82358281111562009f9157600080fd5b62009f9f8782860162009f14565b82525060208301358281111562009fb557600080fd5b62009fc38782860162009f14565b60208301525060408301358281111562009fdc57600080fd5b62009fea8782860162009f14565b6040830152506060830135828111156200a00357600080fd5b6200a0118782860162009f14565b60608301525095945050505050565b600080600080604085870312156200a03757600080fd5b84356001600160401b03808211156200a04f57600080fd5b6200a05d8883890162009971565b909650945060208701359150808211156200a07757600080fd5b5062009e758782880162009971565b600181811c908216806200a09b57607f821691505b6020821081036200207c57634e487b7160e01b600052602260045260246000fd5b6001600160a01b0392831681529116602082015260400190565b8183823760009101908152919050565b634e487b7160e01b600052601160045260246000fd5b6000826200a11a57634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111562005a875762005a876200a0e6565b6000602082840312156200a14857600080fd5b5051919050565b8381526020810183905260608101600383106200a17c57634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200a1b5576200a1b56200a0e6565b5060010190565b8181038181111562005a875762005a876200a0e6565b808202811582820484141762005a875762005a876200a0e6565b6040815260006200a2016040830185620099f1565b905060018060a01b03831660208301529392505050565b6001600160401b038281168282160390808211156200a23b576200a23b6200a0e6565b5092915050565b6000808335601e198436030181126200a25a57600080fd5b8301803591506001600160401b038211156200a27557600080fd5b6020019150368190038213156200264a57600080fd5b601f8211156200643c57600081815260208120601f850160051c810160208610156200a2b45750805b601f850160051c820191505b81811015620062a8578281556001016200a2c0565b600019600383901b1c191660019190911b1790565b6001600160401b038311156200a304576200a3046200985f565b6200a31c836200a31583546200a086565b836200a28b565b6000601f8411600181146200a34f57600085156200a33a5750838201355b6200a34686826200a2d5565b8455506200855e565b600083815260209020601f19861690835b828110156200a38257868501358255602094850194600190920191016200a360565b50868210156200a3a05760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b6200a3be82836200a242565b6001600160401b038111156200a3d8576200a3d86200985f565b6200a3f0816200a3e985546200a086565b856200a28b565b6000601f8211600181146200a42357600083156200a40e5750838201355b6200a41a84826200a2d5565b8655506200a480565b600085815260209020601f19841690835b828110156200a45657868501358255602094850194600190920191016200a434565b50848210156200a4745760001960f88660031b161c19848701351681555b505060018360011b0185555b505050506200a49360208301836200a242565b6200a4a38183600186016200a2ea565b50506200a4b460408301836200a242565b6200a4c48183600286016200a2ea565b50506200a4d560608301836200a242565b6200a4e58183600386016200a2ea565b50505050565b81356200a4f88162009b68565b6001600160401b03811690508154816001600160401b0319821617835560208401356200a5258162009b68565b6fffffffffffffffff0000000000000000604091821b166fffffffffffffffffffffffffffffffff198316841781178555908501356200a5658162009b68565b6001600160c01b0319929092169092179190911760809190911b67ffffffffffffffff60801b1617905550565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000620099696020830184866200a592565b634e487b7160e01b600052603160045260246000fd5b6000816200a5f9576200a5f96200a0e6565b506000190190565b6040815260006200a6176040830186886200a592565b82810360208401526200a62c8185876200a592565b979650505050505050565b81516001600160401b038111156200a653576200a6536200985f565b6200a66b816200a66484546200a086565b846200a28b565b602080601f8311600181146200a69f57600084156200a68a5750858301515b6200a69685826200a2d5565b865550620062a8565b600085815260208120601f198616915b828110156200a6d0578886015182559484019460019091019084016200a6af565b50858210156200a6ef5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60008083546200a70f816200a086565b600182811680156200a72a57600181146200a740576200a771565b60ff19841687528215158302870194506200a771565b8760005260208060002060005b858110156200a7685781548a8201529084019082016200a74d565b50505082870194505b50929695505050505050565b6bffffffffffffffffffffffff198360601b1681526000600383106200a7b357634e487b7160e01b600052602160045260246000fd5b5060f89190911b6014820152601501919050565b6bffffffffffffffffffffffff198560601b16815282846014830137601492019182015260340192915050565b600086516200a808818460208b01620097f6565b82018587823760009086019081528385823760009301928352509095945050505050565b6001600160a01b038316815260406020820181905260009062009969908301846200981c565b600082516200a866818460208701620097f6565b919091019291505056fe608060405260405162000e9638038062000e96833981016040819052620000269162000497565b828162000036828260006200004d565b50620000449050826200008a565b505050620005ca565b6200005883620000e5565b600082511180620000665750805b1562000085576200008383836200012760201b620001691760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000b562000156565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000e2816200018f565b50565b620000f08162000244565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200014f838360405180606001604052806027815260200162000e6f60279139620002f8565b9392505050565b60006200018060008051602062000e4f83398151915260001b6200037760201b620001951760201c565b546001600160a01b0316919050565b6001600160a01b038116620001fa5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200022360008051602062000e4f83398151915260001b6200037760201b620001951760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6200025a816200037a60201b620001981760201c565b620002be5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001f1565b80620002237f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200037760201b620001951760201c565b6060600080856001600160a01b03168560405162000317919062000577565b600060405180830381855af49150503d806000811462000354576040519150601f19603f3d011682016040523d82523d6000602084013e62000359565b606091505b5090925090506200036d8683838762000389565b9695505050505050565b90565b6001600160a01b03163b151590565b60608315620003fd578251600003620003f5576001600160a01b0385163b620003f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001f1565b508162000409565b62000409838362000411565b949350505050565b815115620004225781518083602001fd5b8060405162461bcd60e51b8152600401620001f1919062000595565b80516001600160a01b03811681146200045657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200048e57818101518382015260200162000474565b50506000910152565b600080600060608486031215620004ad57600080fd5b620004b8846200043e565b9250620004c8602085016200043e565b60408501519092506001600160401b0380821115620004e657600080fd5b818601915086601f830112620004fb57600080fd5b8151818111156200051057620005106200045b565b604051601f8201601f19908116603f011681019083821181831017156200053b576200053b6200045b565b816040528281528960208487010111156200055557600080fd5b6200056883602083016020880162000471565b80955050505050509250925092565b600082516200058b81846020870162000471565b9190910192915050565b6020815260008251806020840152620005b681604085016020870162000471565b601f01601f19169190910160400192915050565b61087580620005da6000396000f3fe60806040523661001357610011610017565b005b6100115b61001f6101a7565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a576100536101da565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a57610053610231565b63070d7c6960e41b6001600160e01b031982160161009a57610053610277565b621eb96f60e61b6001600160e01b03198216016100b9576100536102a8565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102e8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102fc565b565b606061018e83836040518060600160405280602781526020016108426027913961030c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101e4610384565b60006101f33660048184610695565b81019061020091906106db565b905061021d8160405180602001604052806000815250600061038f565b505060408051602081019091526000815290565b60606000806102433660048184610695565b810190610250919061070c565b915091506102608282600161038f565b604051806020016040528060008152509250505090565b6060610281610384565b60006102903660048184610695565b81019061029d91906106db565b905061021d816103bb565b60606102b2610384565b60006102bc6101a7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102f2610384565b60006102bc610412565b610167610307610412565b610421565b6060600080856001600160a01b03168560405161032991906107f2565b600060405180830381855af49150503d8060008114610364576040519150601f19603f3d011682016040523d82523d6000602084013e610369565b606091505b509150915061037a86838387610445565b9695505050505050565b341561016757600080fd5b610398836104c6565b6000825111806103a55750805b156103b6576103b48383610169565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e46101a7565b604080516001600160a01b03928316815291841660208301520160405180910390a161040f81610506565b50565b600061041c6105af565b905090565b3660008037600080366000845af43d6000803e808015610440573d6000f35b3d6000fd5b606083156104b45782516000036104ad576001600160a01b0385163b6104ad5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b50816104be565b6104be83836105d7565b949350505050565b6104cf81610601565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661056b5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101cb565b8151156105e75781518083602001fd5b8060405162461bcd60e51b815260040161014e919061080e565b6001600160a01b0381163b61066e5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61058e565b600080858511156106a557600080fd5b838611156106b257600080fd5b5050820193919092039150565b80356001600160a01b03811681146106d657600080fd5b919050565b6000602082840312156106ed57600080fd5b61018e826106bf565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561071f57600080fd5b610728836106bf565b9150602083013567ffffffffffffffff8082111561074557600080fd5b818501915085601f83011261075957600080fd5b81358181111561076b5761076b6106f6565b604051601f8201601f19908116603f01168101908382118183101715610793576107936106f6565b816040528281528860208487010111156107ac57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107e95781810151838201526020016107d1565b50506000910152565b600082516108048184602087016107ce565b9190910192915050565b602081526000825180602084015261082d8160408501602087016107ce565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a164736f6c6343000811000ab53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a164736f6c6343000811000a \ No newline at end of file diff --git a/core/systemcontracts/fermi/rialto/StakeHubContract b/core/systemcontracts/fermi/rialto/StakeHubContract new file mode 100644 index 0000000000..96aa4faaf1 --- /dev/null +++ b/core/systemcontracts/fermi/rialto/StakeHubContract @@ -0,0 +1 @@ +6080604052600436106200043b5760003560e01c806386d545061162000233578063ca47908f116200012f578063dd42a1dd11620000b9578063f1f74d841162000084578063f1f74d841462000d74578063f80a34021462000d8c578063fb50b31f1462000db1578063fc0c5ff11462000dd6578063ff69ab611462000dee57600080fd5b8063dd42a1dd1462000ce1578063e8f67c3b1462000d08578063e992aaf51462000d20578063efdbf0e11462000d3857600080fd5b8063d7c2dfc811620000fa578063d7c2dfc81462000c67578063d8ca511f1462000c8c578063daacdb661462000ca4578063dbda7fb31462000cbc57600080fd5b8063ca47908f1462000bd0578063cbb04d9d1462000be8578063d115a2061462000c29578063d6ca429d1462000c4257600080fd5b8063b187bd2611620001bd578063bfff04751162000188578063bfff04751462000b57578063c166f58a1462000b7c578063c38fbec81462000b93578063c473318f1462000bb8578063c8509d81146200095057600080fd5b8063b187bd261462000ac4578063baa7199e1462000ae4578063bdceadf31462000b09578063bff02e201462000b2157600080fd5b8063a1832e6411620001fe578063a1832e641462000a21578063a43569b31462000a46578063aad3ec961462000a7a578063ac4317511462000a9f57600080fd5b806386d54506146200098d5780638a4d3fa814620009c75780638cd22b2214620009e5578063982ef0a71462000a0a57600080fd5b80634838d165116200034357806364028fbd11620002cd57806375cc7d89116200029857806375cc7d8914620008fb57806376e7d6d614620009205780638129fc1c1462000938578063831d65d114620009505780638456cb59146200097557600080fd5b806364028fbd1462000836578063663706d3146200084d5780636ec01b27146200087e5780636f8e2fa414620008d657600080fd5b80634e6fd6c4116200030e5780634e6fd6c4146200079d5780635949187114620007b55780635e7cc1c914620007da57806363a036b514620007ff57600080fd5b80634838d16514620006e957806349f41a42146200072e5780634a49ac4c14620007535780634d99dd16146200077857600080fd5b80631fab701511620003c5578063384099881162000390578063384099881462000662578063417c73a7146200067a578063449ecfe6146200069f57806345211bfd14620006c457600080fd5b80631fab701514620005a95780632b727c8614620005ce5780632e8e8c7114620005f3578063367dad49146200062d57600080fd5b80630e9fbf5111620004065780630e9fbf5114620004f35780631182b875146200051857806317b4f353146200054c5780631fa8882b146200059157600080fd5b8063046f7da2146200045b578063059ddd2214620004735780630661806e14620004b5578063092193ab14620004dc57600080fd5b36620004565760345460ff166001146200045457600080fd5b005b600080fd5b3480156200046857600080fd5b506200045462000e06565b3480156200048057600080fd5b506200049862000492366004620096e4565b62000e98565b6040516001600160a01b0390911681526020015b60405180910390f35b348015620004c257600080fd5b50620004cd60365481565b604051908152602001620004ac565b62000454620004ed366004620096e4565b620012c0565b3480156200050057600080fd5b50620004546200051236600462009746565b62001911565b3480156200052557600080fd5b506200053d620005373660046200978b565b62001c3b565b604051620004ac91906200983e565b3480156200055957600080fd5b50620004986200056b36600462009910565b80516020818301810180516045825292820191909301209152546001600160a01b031681565b3480156200059e57600080fd5b50620004cd61025881565b348015620005b657600080fd5b5062000454620005c8366004620099ac565b62001cd3565b348015620005db57600080fd5b5062000498620005ed366004620096e4565b6200202a565b3480156200060057600080fd5b506200049862000612366004620096e4565b604d602052600090815260409020546001600160a01b031681565b3480156200063a57600080fd5b50620006526200064c366004620099ac565b6200207f565b604051620004ac92919062009a2b565b3480156200066f57600080fd5b50620004cd60375481565b3480156200068757600080fd5b506200045462000699366004620096e4565b6200264e565b348015620006ac57600080fd5b5062000454620006be366004620096e4565b620026d0565b348015620006d157600080fd5b5062000454620006e3366004620096e4565b620028b5565b348015620006f657600080fd5b506200071d62000708366004620096e4565b60016020526000908152604090205460ff1681565b6040519015158152602001620004ac565b3480156200073b57600080fd5b50620004546200074d366004620096e4565b62002a8d565b3480156200076057600080fd5b506200045462000772366004620096e4565b62002ca9565b3480156200078557600080fd5b50620004546200079736600462009aca565b62002d25565b348015620007aa57600080fd5b506200049861dead81565b348015620007c257600080fd5b5062000454620007d436600462009b08565b62003355565b348015620007e757600080fd5b5062000454620007f936600462009b72565b6200414e565b3480156200080c57600080fd5b50620008246200081e36600462009b99565b62004377565b604051620004ac949392919062009bbc565b620004546200084736600462009c78565b62004a1c565b3480156200085a57600080fd5b50620004cd6200086c366004620096e4565b60446020526000908152604090205481565b3480156200088b57600080fd5b50620008a36200089d366004620096e4565b6200506b565b6040805182516001600160401b0390811682526020808501518216908301529282015190921690820152606001620004ac565b348015620008e357600080fd5b506200053d620008f5366004620096e4565b62005110565b3480156200090857600080fd5b50620004546200091a366004620096e4565b6200553c565b3480156200092d57600080fd5b50620004cd603d5481565b3480156200094557600080fd5b50620004546200570e565b3480156200095d57600080fd5b50620004546200096f3660046200978b565b620058d6565b3480156200098257600080fd5b506200045462005934565b3480156200099a57600080fd5b5062000498620009ac366004620096e4565b6043602052600090815260409020546001600160a01b031681565b348015620009d457600080fd5b50620004cd670de0b6b3a764000081565b348015620009f257600080fd5b50620004cd62000a0436600462009aca565b620059cc565b6200045462000a1b36600462009d4e565b62005a85565b34801562000a2e57600080fd5b506200045462000a40366004620099ac565b62006133565b34801562000a5357600080fd5b5062000a6b62000a65366004620096e4565b62006439565b604051620004ac919062009d86565b34801562000a8757600080fd5b506200045462000a9936600462009aca565b62006726565b34801562000aac57600080fd5b506200045462000abe36600462009e03565b62006793565b34801562000ad157600080fd5b5060005462010000900460ff166200071d565b34801562000af157600080fd5b506200045462000b0336600462009e75565b62007745565b34801562000b1657600080fd5b50620004cd603c5481565b34801562000b2e57600080fd5b5062000b4662000b4036600462009b99565b62007912565b604051620004ac9392919062009ece565b34801562000b6457600080fd5b50620004cd62000b76366004620096e4565b62007aee565b34801562000b8957600080fd5b50620004cd600581565b34801562000ba057600080fd5b506200045462000bb2366004620096e4565b62007b3c565b34801562000bc557600080fd5b50620004cd60385481565b34801562000bdd57600080fd5b50620004cd604e5481565b34801562000bf557600080fd5b5062000c0d62000c07366004620096e4565b62007dfc565b60408051938452911515602084015290820152606001620004ac565b34801562000c3657600080fd5b50620004cd620186a081565b34801562000c4f57600080fd5b506200045462000c6136600462009f2b565b6200823f565b34801562000c7457600080fd5b506200045462000c863660046200a014565b62008464565b34801562000c9957600080fd5b50620004cd603b5481565b34801562000cb157600080fd5b50620004cd60495481565b34801562000cc957600080fd5b506200049862000cdb366004620096e4565b6200855a565b34801562000cee57600080fd5b50600054630100000090046001600160a01b031662000498565b34801562000d1557600080fd5b50620004cd60355481565b34801562000d2d57600080fd5b50620004cd603a5481565b34801562000d4557600080fd5b50620004cd62000d5736600462009910565b805160208183018101805160468252928201919093012091525481565b34801562000d8157600080fd5b50620004cd603e5481565b34801562000d9957600080fd5b50620004cd62000dab36600462009aca565b62008984565b34801562000dbe57600080fd5b506200045462000dd036600462009e03565b620089f5565b34801562000de357600080fd5b50620004cd60395481565b34801562000dfb57600080fd5b50620004cd604a5481565b600054630100000090046001600160a01b0316331462000e39576040516306fbb1e360e01b815260040160405180910390fd5b60005462010000900460ff1662000e6357604051636cd6020160e01b815260040160405180910390fd5b6000805462ff0000191681556040517f62451d457bc659158be6e6247f56ec1df424a5c7597f71c20c2bc44e0965c8f99190a1565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054929384939091608084019162000f03906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462000f31906200a07a565b801562000f825780601f1062000f565761010080835404028352916020019162000f82565b820191906000526020600020905b81548152906001019060200180831162000f6457829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462000fad906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462000fdb906200a07a565b80156200102c5780601f1062001000576101008083540402835291602001916200102c565b820191906000526020600020905b8154815290600101906020018083116200100e57829003601f168201915b5050505050815260200160018201805462001047906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462001075906200a07a565b8015620010c65780601f106200109a57610100808354040283529160200191620010c6565b820191906000526020600020905b815481529060010190602001808311620010a857829003601f168201915b50505050508152602001600282018054620010e1906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200110f906200a07a565b8015620011605780601f10620011345761010080835404028352916020019162001160565b820191906000526020600020905b8154815290600101906020018083116200114257829003601f168201915b505050505081526020016003820180546200117b906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620011a9906200a07a565b8015620011fa5780601f10620011ce57610100808354040283529160200191620011fa565b820191906000526020600020905b815481529060010190602001808311620011dc57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b81548152602001906001019080831162001299575050509190925250509051949350505050565b3361100014620012ec57604051630f22c43960e41b815261100060048201526024015b60405180910390fd5b6001600160a01b0380821660009081526043602090815260408083205484168084526041835281842082516101808101845281548716815260018201548716948101949094526002810154909516918301919091526003840154606083015260048401805491949160808401919062001365906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462001393906200a07a565b8015620013e45780601f10620013b857610100808354040283529160200191620013e4565b820191906000526020600020905b815481529060010190602001808311620013c657829003601f168201915b50505050508152602001600582016040518060800160405290816000820180546200140f906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200143d906200a07a565b80156200148e5780601f1062001462576101008083540402835291602001916200148e565b820191906000526020600020905b8154815290600101906020018083116200147057829003601f168201915b50505050508152602001600182018054620014a9906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620014d7906200a07a565b8015620015285780601f10620014fc5761010080835404028352916020019162001528565b820191906000526020600020905b8154815290600101906020018083116200150a57829003601f168201915b5050505050815260200160028201805462001543906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462001571906200a07a565b8015620015c25780601f106200159657610100808354040283529160200191620015c2565b820191906000526020600020905b815481529060010190602001808311620015a457829003601f168201915b50505050508152602001600382018054620015dd906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200160b906200a07a565b80156200165c5780601f1062001630576101008083540402835291602001916200165c565b820191906000526020600020905b8154815290600101906020018083116200163e57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620016fb575050509190925250505060408101519091506001600160a01b031615806200173857508060e001515b15620017f557604051611002903490600081818185875af1925050503d806000811462001782576040519150601f19603f3d011682016040523d82523d6000602084013e62001787565b606091505b505050816001600160a01b03167ffc8bff675087dd2da069cc3fb517b9ed001e19750c0865241a5542dba1ba170d604051620017e89060208082526011908201527024a72b20a624a22fab20a624a220aa27a960791b604082015260600190565b60405180910390a2505050565b60408181015160c0830151519151632f303ebb60e11b81526001600160401b0390921660048301526001600160a01b031690635e607d769034906024016000604051808303818588803b1580156200184c57600080fd5b505af115801562001861573d6000803e3d6000fd5b5050505050816001600160a01b03167fe34918ff1c7084970068b53fd71ad6d8b04e9f15d3886cbf006443e6cdc52ea634604051620018a291815260200190565b60405180910390a26040808201519051633041949b60e01b815261200591633041949b91620018d7919086906004016200a0b0565b600060405180830381600087803b158015620018f257600080fd5b505af115801562001907573d6000803e3d6000fd5b5050505050505b50565b33611001146200193957604051630f22c43960e41b81526110016004820152602401620012e3565b60005462010000900460ff16156200196457604051631785c68160e01b815260040160405180910390fd5b6000604583836040516200197a9291906200a0ca565b908152604051908190036020019020546001600160a01b03169050620019a2603f8262008c2d565b620019c05760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038116600090815260416020526040812090620019e7610258426200a0f0565b604a546000828152604b60205260409020549192501162001a1b5760405163bd52fcdb60e01b815260040160405180910390fd5b6000818152604b6020526040812080546001929062001a3c9084906200a113565b909155505060405160469062001a5690879087906200a0ca565b90815260200160405180910390205460001415801562001aa65750426102586046878760405162001a899291906200a0ca565b90815260200160405180910390205462001aa491906200a113565b105b1562001ac557604051631898eb6b60e01b815260040160405180910390fd5b60008062001ad585600262008c50565b915091508162001af857604051631b919bb160e11b815260040160405180910390fd5b6002840154603c5460405163045bc4d160e41b815260048101919091526000916001600160a01b0316906345bc4d10906024016020604051808303816000875af115801562001b4b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001b7191906200a129565b905062001b7f858362008cd8565b856001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb28383600260405162001bbf939291906200a143565b60405180910390a26002850154604051633041949b60e01b815261200591633041949b9162001bfd916001600160a01b0316908a906004016200a0b0565b600060405180830381600087803b15801562001c1857600080fd5b505af115801562001c2d573d6000803e3d6000fd5b505050505050505050505050565b6060336120001462001c6557604051630f22c43960e41b81526120006004820152602401620012e3565b60005462010000900460ff161562001c9057604051631785c68160e01b815260040160405180910390fd5b6034805460ff1916600117905560405162461bcd60e51b815260206004820152600a60248201526919195c1c9958d85d195960b21b6044820152606401620012e3565b60005462010000900460ff161562001cfe57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562001d305760405163b1d02c3d60e01b815260040160405180910390fd5b62001d3a62008dcc565b62001d47603f8262008c2d565b62001d655760405163056e881160e01b815260040160405180910390fd5b62001d6f62008e17565b600082900362001d9257604051636490ffd360e01b815260040160405180910390fd5b600062001d9e62008dcc565b6001600160a01b0381166000908152604f602052604090208054604e54929350909162001dcc86836200a113565b111562001dec5760405163091af98560e21b815260040160405180910390fd5b60005b8581101562001ed257600087878381811062001e0f5762001e0f6200a17e565b905060200201350362001e3557604051636490ffd360e01b815260040160405180910390fd5b600062001e448260016200a113565b90505b8681101562001ebc5787878281811062001e655762001e656200a17e565b9050602002013588888481811062001e815762001e816200a17e565b905060200201350362001ea757604051632205e3c760e11b815260040160405180910390fd5b8062001eb3816200a194565b91505062001e47565b508062001ec9816200a194565b91505062001def565b5060005b8581101562001f715760005b8281101562001f5b5783818154811062001f005762001f006200a17e565b906000526020600020015488888481811062001f205762001f206200a17e565b905060200201350362001f4657604051632205e3c760e11b815260040160405180910390fd5b8062001f52816200a194565b91505062001ee2565b508062001f68816200a194565b91505062001ed6565b5060005b8581101562001907578287878381811062001f945762001f946200a17e565b835460018101855560009485526020948590209190940292909201359190920155506001600160a01b0384167f7c4ff4c9a343a2daef608f3b5a91016e994a15fc0ef8611109e4f45823249f2988888481811062001ff65762001ff66200a17e565b905060200201356040516200200d91815260200190565b60405180910390a28062002021816200a194565b91505062001f75565b6000816200203a603f8262008c2d565b620020585760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038084166000908152604160205260409020600d01541691505b50919050565b60608082806001600160401b038111156200209e576200209e62009853565b604051908082528060200260200182016040528015620020c8578160200160208202803683370190505b509250806001600160401b03811115620020e657620020e662009853565b6040519080825280602002602001820160405280156200211b57816020015b6060815260200190600190039081620021055790505b50915060005b81811015620026445760008686838181106200214157620021416200a17e565b9050602002016020810190620021589190620096e4565b6001600160a01b03808216600090815260416020908152604080832081516101808101835281548616815260018201548616938101939093526002810154909416908201526003830154606082015260048301805494955091939092916080840191620021c5906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620021f3906200a07a565b8015620022445780601f10620022185761010080835404028352916020019162002244565b820191906000526020600020905b8154815290600101906020018083116200222657829003601f168201915b50505050508152602001600582016040518060800160405290816000820180546200226f906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200229d906200a07a565b8015620022ee5780601f10620022c257610100808354040283529160200191620022ee565b820191906000526020600020905b815481529060010190602001808311620022d057829003601f168201915b5050505050815260200160018201805462002309906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462002337906200a07a565b8015620023885780601f106200235c5761010080835404028352916020019162002388565b820191906000526020600020905b8154815290600101906020018083116200236a57829003601f168201915b50505050508152602001600282018054620023a3906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620023d1906200a07a565b8015620024225780601f10620023f65761010080835404028352916020019162002422565b820191906000526020600020905b8154815290600101906020018083116200240457829003601f168201915b505050505081526020016003820180546200243d906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200246b906200a07a565b8015620024bc5780601f106200249057610100808354040283529160200191620024bc565b820191906000526020600020905b8154815290600101906020018083116200249e57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200255b57505050505081525050905080600001518684815181106200259457620025946200a17e565b6001600160a01b039283166020918202929092018101919091529083166000908152604f8252604090819020805482518185028101850190935280835291929091908301828280156200260757602002820191906000526020600020905b815481526020019060010190808311620025f2575b50505050508584815181106200262157620026216200a17e565b6020026020010181905250505080806200263b906200a194565b91505062002121565b50505b9250929050565b600054630100000090046001600160a01b0316331462002681576040516306fbb1e360e01b815260040160405180910390fd5b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f7fd26be6fc92aff63f1f4409b2b2ddeb272a888031d7f55ec830485ec61941869190a250565b60005462010000900460ff1615620026fb57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff16156200272d5760405163b1d02c3d60e01b815260040160405180910390fd5b806200273b603f8262008c2d565b620027595760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0382166000908152604160205260409020600a81015460ff166200279757604051634b6b857d60e01b815260040160405180910390fd5b6036546002820154604051630913db4760e01b81526001600160a01b03868116600483015290911690630913db4790602401602060405180830381865afa158015620027e7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200280d91906200a129565b10156200282d576040516317b204bf60e11b815260040160405180910390fd5b4281600b01541115620028535760405163170cb76760e21b815260040160405180910390fd5b600a8101805460ff191690556049805460019190600090620028779084906200a1b0565b90915550506040516001600160a01b038416907f9390b453426557da5ebdc31f19a37753ca04addf656d32f35232211bb2af3f1990600090a2505050565b60005462010000900460ff1615620028e057604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620029125760405163b1d02c3d60e01b815260040160405180910390fd5b6200291c62008e2a565b62002929603f8262008c2d565b620029475760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0382166200296f57604051636520611b60e11b815260040160405180910390fd5b6001600160a01b038281166000908152604360205260409020541615620029a957604051631e6f587560e11b815260040160405180910390fd5b6000620029b562008e2a565b6001600160a01b0381166000908152604160205260409020600c810154919250904290620029e790610258906200a113565b111562002a0757604051631f92cdbd60e11b815260040160405180910390fd5b80546001600160a01b039081166000908152604460209081526040808320429081905585548986166001600160a01b031991821681178855600c88019290925581855260439093528184208054958816959093168517909255519092917f6e4e747ca35203f16401c69805c7dd52fff67ef60b0ebc5c7fe16890530f223591a350505050565b3362002a9b603f8262008c2d565b62002ab95760405163056e881160e01b815260040160405180910390fd5b60005462010000900460ff161562002ae457604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562002b165760405163b1d02c3d60e01b815260040160405180910390fd5b6001600160a01b038281166000908152604d6020526040902054161562002b505760405163bebdc75760e01b815260040160405180910390fd5b62002b5d603f8362008c2d565b1562002b7c5760405163bebdc75760e01b815260040160405180910390fd5b336000818152604160205260409020600d01546001600160a01b03908116908416810362002bbd5760405163bebdc75760e01b815260040160405180910390fd5b6001600160a01b0381161562002bf4576001600160a01b0381166000908152604d6020526040902080546001600160a01b03191690555b6001600160a01b038281166000908152604160205260409020600d0180546001600160a01b03191691861691821790551562002c59576001600160a01b038481166000908152604d6020526040902080546001600160a01b0319169184169190911790555b836001600160a01b0316816001600160a01b0316836001600160a01b03167fcbb728765de145e99c00e8ae32a325231e850359b7b8a6da3b84d672ab3f1d0a60405160405180910390a450505050565b600054630100000090046001600160a01b0316331462002cdc576040516306fbb1e360e01b815260040160405180910390fd5b6001600160a01b038116600081815260016020526040808220805460ff19169055517fe0db3499b7fdc3da4cddff5f45d694549c19835e7f719fb5606d3ad1a5de40119190a250565b60005462010000900460ff161562002d5057604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562002d825760405163b1d02c3d60e01b815260040160405180910390fd5b8162002d90603f8262008c2d565b62002dae5760405163056e881160e01b815260040160405180910390fd5b8160000362002dd057604051639811e0c760e01b815260040160405180910390fd5b6001600160a01b038084166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054339491608084019162002e38906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462002e66906200a07a565b801562002eb75780601f1062002e8b5761010080835404028352916020019162002eb7565b820191906000526020600020905b81548152906001019060200180831162002e9957829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462002ee2906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462002f10906200a07a565b801562002f615780601f1062002f355761010080835404028352916020019162002f61565b820191906000526020600020905b81548152906001019060200180831162002f4357829003601f168201915b5050505050815260200160018201805462002f7c906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462002faa906200a07a565b801562002ffb5780601f1062002fcf5761010080835404028352916020019162002ffb565b820191906000526020600020905b81548152906001019060200180831162002fdd57829003601f168201915b5050505050815260200160028201805462003016906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462003044906200a07a565b8015620030955780601f10620030695761010080835404028352916020019162003095565b820191906000526020600020905b8154815290600101906020018083116200307757829003601f168201915b50505050508152602001600382018054620030b0906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620030de906200a07a565b80156200312f5780601f1062003103576101008083540402835291602001916200312f565b820191906000526020600020905b8154815290600101906020018083116200311157829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620031ce575050509190925250505060408082015190516326ccee8b60e11b81526001600160a01b0385811660048301526024820188905292935060009290911690634d99dd16906044016020604051808303816000875af115801562003248573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200326e91906200a129565b9050826001600160a01b0316866001600160a01b03167f3aace7340547de7b9156593a7652dc07ee900cea3fd8f82cb6c9d38b408298028784604051620032bf929190918252602082015260400190565b60405180910390a3856001600160a01b0316836001600160a01b031603620032ec57620032ec8662008e6b565b6040808301519051633041949b60e01b815261200591633041949b9162003319919087906004016200a0b0565b600060405180830381600087803b1580156200333457600080fd5b505af115801562003349573d6000803e3d6000fd5b50505050505050505050565b60005462010000900460ff16156200338057604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620033b25760405163b1d02c3d60e01b815260040160405180910390fd5b83620033c0603f8262008c2d565b620033de5760405163056e881160e01b815260040160405180910390fd5b83620033ec603f8262008c2d565b6200340a5760405163056e881160e01b815260040160405180910390fd5b6034805460ff1916600117905560008490036200343a57604051639811e0c760e01b815260040160405180910390fd5b846001600160a01b0316866001600160a01b0316036200346d5760405163f0e3e62960e01b815260040160405180910390fd5b6001600160a01b0380871660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608201526004830180543394916080840191620034d5906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462003503906200a07a565b8015620035545780601f10620035285761010080835404028352916020019162003554565b820191906000526020600020905b8154815290600101906020018083116200353657829003601f168201915b50505050508152602001600582016040518060800160405290816000820180546200357f906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620035ad906200a07a565b8015620035fe5780601f10620035d257610100808354040283529160200191620035fe565b820191906000526020600020905b815481529060010190602001808311620035e057829003601f168201915b5050505050815260200160018201805462003619906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462003647906200a07a565b8015620036985780601f106200366c5761010080835404028352916020019162003698565b820191906000526020600020905b8154815290600101906020018083116200367a57829003601f168201915b50505050508152602001600282018054620036b3906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620036e1906200a07a565b8015620037325780601f10620037065761010080835404028352916020019162003732565b820191906000526020600020905b8154815290600101906020018083116200371457829003601f168201915b505050505081526020016003820180546200374d906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200377b906200a07a565b8015620037cc5780601f10620037a057610100808354040283529160200191620037cc565b820191906000526020600020905b815481529060010190602001808311620037ae57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200386b57505050919092525050506001600160a01b03808916600090815260416020908152604080832081516101808101835281548616815260018201548616938101939093526002810154909416908201526003830154606082015260048301805494955091939092916080840191620038f6906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462003924906200a07a565b8015620039755780601f10620039495761010080835404028352916020019162003975565b820191906000526020600020905b8154815290600101906020018083116200395757829003601f168201915b5050505050815260200160058201604051806080016040529081600082018054620039a0906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620039ce906200a07a565b801562003a1f5780601f10620039f35761010080835404028352916020019162003a1f565b820191906000526020600020905b81548152906001019060200180831162003a0157829003601f168201915b5050505050815260200160018201805462003a3a906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462003a68906200a07a565b801562003ab95780601f1062003a8d5761010080835404028352916020019162003ab9565b820191906000526020600020905b81548152906001019060200180831162003a9b57829003601f168201915b5050505050815260200160028201805462003ad4906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462003b02906200a07a565b801562003b535780601f1062003b275761010080835404028352916020019162003b53565b820191906000526020600020905b81548152906001019060200180831162003b3557829003601f168201915b5050505050815260200160038201805462003b6e906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462003b9c906200a07a565b801562003bed5780601f1062003bc15761010080835404028352916020019162003bed565b820191906000526020600020905b81548152906001019060200180831162003bcf57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b81548152602001906001019080831162003c8c5750505050508152505090508060e00151801562003ccf5750876001600160a01b0316836001600160a01b031614155b1562003cee57604051636468920360e01b815260040160405180910390fd5b60408083015190516352e82ce560e11b81526001600160a01b038581166004830152602482018a9052600092169063a5d059ca906044016020604051808303816000875af115801562003d45573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003d6b91906200a129565b905060375481101562003d915760405163dc6f0bdd60e01b815260040160405180910390fd5b896001600160a01b0316846001600160a01b031614801562003e2657506036546040808501519051630913db4760e01b81526001600160a01b038d8116600483015290911690630913db4790602401602060405180830381865afa15801562003dfe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062003e2491906200a129565b105b1562003e45576040516317b204bf60e11b815260040160405180910390fd5b6000620186a0603a548362003e5b91906200a1c6565b62003e6791906200a0f0565b9050600083604001516001600160a01b03168260405160006040518083038185875af1925050503d806000811462003ebc576040519150601f19603f3d011682016040523d82523d6000602084013e62003ec1565b606091505b505090508062003ee4576040516312171d8360e31b815260040160405180910390fd5b62003ef082846200a1b0565b60408086015190516317066a5760e21b81526001600160a01b03898116600483015292955060009290911690635c19a95c90869060240160206040518083038185885af115801562003f46573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019062003f6d91906200a129565b9050866001600160a01b03168c6001600160a01b03168e6001600160a01b03167ffdac6e81913996d95abcc289e90f2d8bd235487ce6fe6f821e7d21002a1915b48e858960405162003fd2939291909283526020830191909152604082015260600190565b60405180910390a46040805160028082526060820183526000926020830190803683370190505090508660400151816000815181106200401657620040166200a17e565b60200260200101906001600160a01b031690816001600160a01b0316815250508560400151816001815181106200405157620040516200a17e565b6001600160a01b0390921660209283029190910190910152604051634484077560e01b815261200590634484077590620040929084908c906004016200a1e0565b600060405180830381600087803b158015620040ad57600080fd5b505af1158015620040c2573d6000803e3d6000fd5b505050508a1562004134576120056001600160a01b031663e5ed5b1e898f6040518363ffffffff1660e01b8152600401620040ff9291906200a0b0565b600060405180830381600087803b1580156200411a57600080fd5b505af11580156200412f573d6000803e3d6000fd5b505050505b50506034805460ff19169055505050505050505050505050565b60005462010000900460ff16156200417957604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620041ab5760405163b1d02c3d60e01b815260040160405180910390fd5b620041b562008e2a565b620041c2603f8262008c2d565b620041e05760405163056e881160e01b815260040160405180910390fd5b6000620041ec62008e2a565b6001600160a01b0381166000908152604160205260409020600c8101549192509042906200421e90610258906200a113565b11156200423e57604051631f92cdbd60e11b815260040160405180910390fd5b60098101546001600160401b03600160401b90910481169085161115620042785760405163dc81db8560e01b815260040160405180910390fd5b60098101546000906001600160401b039081169086161015620042b6576009820154620042b09086906001600160401b03166200a20c565b620042d0565b6009820154620042d0906001600160401b0316866200a20c565b60098301546001600160401b039182169250600160801b9004168111156200430b5760405163dc81db8560e01b815260040160405180910390fd5b60098201805467ffffffffffffffff19166001600160401b03871690811790915542600c8401556040519081526001600160a01b038416907f78cdd96edf59e09cfd4d26ef6ef6c92d166effe6a40970c54821206d541932cb9060200160405180910390a25050505050565b606080606060006200438a603f62008f89565b90508086101562004a13578415620043a35784620043a5565b805b9450600085620043b688846200a1b0565b11620043ce57620043c887836200a1b0565b620043d0565b855b9050806001600160401b03811115620043ed57620043ed62009853565b60405190808252806020026020018201604052801562004417578160200160208202803683370190505b509450806001600160401b0381111562004435576200443562009853565b6040519080825280602002602001820160405280156200445f578160200160208202803683370190505b509350806001600160401b038111156200447d576200447d62009853565b604051908082528060200260200182016040528015620044b257816020015b60608152602001906001900390816200449c5790505b50925060005b8181101562004a10576000620044dc620044d3838b6200a113565b603f9062008f94565b6001600160a01b0380821660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608201526004830180549495509193909291608084019162004549906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462004577906200a07a565b8015620045c85780601f106200459c57610100808354040283529160200191620045c8565b820191906000526020600020905b815481529060010190602001808311620045aa57829003601f168201915b5050505050815260200160058201604051806080016040529081600082018054620045f3906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462004621906200a07a565b8015620046725780601f10620046465761010080835404028352916020019162004672565b820191906000526020600020905b8154815290600101906020018083116200465457829003601f168201915b505050505081526020016001820180546200468d906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620046bb906200a07a565b80156200470c5780601f10620046e0576101008083540402835291602001916200470c565b820191906000526020600020905b815481529060010190602001808311620046ee57829003601f168201915b5050505050815260200160028201805462004727906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462004755906200a07a565b8015620047a65780601f106200477a57610100808354040283529160200191620047a6565b820191906000526020600020905b8154815290600101906020018083116200478857829003601f168201915b50505050508152602001600382018054620047c1906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620047ef906200a07a565b8015620048405780601f10620048145761010080835404028352916020019162004840565b820191906000526020600020905b8154815290600101906020018083116200482257829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620048df57505050505081525050905080600001518884815181106200491857620049186200a17e565b60200260200101906001600160a01b031690816001600160a01b0316815250508060e00151620049b15780604001516001600160a01b03166315d1f8986040518163ffffffff1660e01b8152600401602060405180830381865afa15801562004985573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620049ab91906200a129565b620049b4565b60005b878481518110620049c957620049c96200a17e565b6020026020010181815250508060800151868481518110620049ef57620049ef6200a17e565b602002602001018190525050508062004a08906200a194565b9050620044b8565b50505b92959194509250565b60005462010000900460ff161562004a4757604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562004a795760405163b1d02c3d60e01b815260040160405180910390fd5b3362004a87603f8262008c2d565b1562004aa657604051635f28f62b60e01b815260040160405180910390fd5b6001600160a01b038181166000908152604d6020526040902054161562004ae057604051631a0a9b9f60e21b815260040160405180910390fd5b6001600160a01b03888116600090815260436020526040902054161562004b1a57604051631e6f587560e11b815260040160405180910390fd5b60006001600160a01b03166045888860405162004b399291906200a0ca565b908152604051908190036020019020546001600160a01b03161462004b71576040516311fdb94760e01b815260040160405180910390fd5b600062004b7f83806200a236565b60405160200162004b929291906200a0ca565b60408051601f1981840301815291815281516020928301206000818152604290935291205490915060ff161562004bdc5760405163c0bf414360e01b815260040160405180910390fd5b600062004bf2670de0b6b3a7640000346200a1b0565b905060365481101562004c18576040516317b204bf60e11b815260040160405180910390fd5b6001600160a01b038a1662004c4057604051636520611b60e11b815260040160405180910390fd5b61138862004c55604087016020880162009b72565b6001600160401b0316118062004c9b575062004c78604086016020870162009b72565b6001600160401b031662004c90602087018762009b72565b6001600160401b0316115b8062004cda575062004cb4604086016020870162009b72565b6001600160401b031662004ccf606087016040880162009b72565b6001600160401b0316115b1562004cf95760405163dc81db8560e01b815260040160405180910390fd5b62004d4462004d0985806200a236565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525062008fa292505050565b62004d6257604051635dba5ad760e01b815260040160405180910390fd5b62004d71838a8a8a8a62009144565b62004d8f57604051631647e3cb60e11b815260040160405180910390fd5b600062004ddd8462004da287806200a236565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506200927492505050565b905062004dec603f8562009375565b506000838152604260209081526040808320805460ff191660019081179091556001600160a01b0380891680865260419094529190932080548f83166001600160a01b03199182161782559381018054851690931790925560028201805491851691909316179091554260038201556004810162004e6c8b8d836200a2de565b50856005820162004e7e82826200a3a6565b508790506009820162004e9282826200a4df565b505042600c8201556001600160a01b038c81166000908152604360205260409081902080546001600160a01b0319169288169290921790915551859060459062004ee0908e908e906200a0ca565b908152602001604051809103902060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550816001600160a01b0316856001600160a01b03168d6001600160a01b03167faecd9fb95e79c75a3a1de93362c6be5fe6ab65770d8614be583884161cd8228d8e8e60405162004f639291906200a5af565b60405180910390a460408051848152602081018590526001600160a01b0387169182917f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e04910160405180910390a360408051670de0b6b3a7640000808252602082015261dead916001600160a01b038816917f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e04910160405180910390a3604051633041949b60e01b815261200590633041949b906200502990859089906004016200a0b0565b600060405180830381600087803b1580156200504457600080fd5b505af115801562005059573d6000803e3d6000fd5b50505050505050505050505050505050565b60408051606081018252600080825260208201819052918101919091528162005096603f8262008c2d565b620050b45760405163056e881160e01b815260040160405180910390fd5b50506001600160a01b031660009081526041602090815260409182902082516060810184526009909101546001600160401b038082168352600160401b8204811693830193909352600160801b90049091169181019190915290565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060828101919091526004840180549194916080840191906200517d906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620051ab906200a07a565b8015620051fc5780601f10620051d057610100808354040283529160200191620051fc565b820191906000526020600020905b815481529060010190602001808311620051de57829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462005227906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462005255906200a07a565b8015620052a65780601f106200527a57610100808354040283529160200191620052a6565b820191906000526020600020905b8154815290600101906020018083116200528857829003601f168201915b50505050508152602001600182018054620052c1906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620052ef906200a07a565b8015620053405780601f10620053145761010080835404028352916020019162005340565b820191906000526020600020905b8154815290600101906020018083116200532257829003601f168201915b505050505081526020016002820180546200535b906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462005389906200a07a565b8015620053da5780601f10620053ae57610100808354040283529160200191620053da565b820191906000526020600020905b815481529060010190602001808311620053bc57829003601f168201915b50505050508152602001600382018054620053f5906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462005423906200a07a565b8015620054745780601f10620054485761010080835404028352916020019162005474565b820191906000526020600020905b8154815290600101906020018083116200545657829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620055135750505091909252505050608001519392505050565b33611001146200556457604051630f22c43960e41b81526110016004820152602401620012e3565b6001600160a01b03808216600090815260436020526040902054166200558c603f8262008c2d565b620055aa5760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038181166000908152604160205260408082206002810154603b54925163045bc4d160e41b81526004810193909352909316906345bc4d10906024016020604051808303816000875af11580156200560d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200563391906200a129565b90506000603d54426200564791906200a113565b905062005655838262008cd8565b836001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb28284600160405162005695939291906200a143565b60405180910390a26002830154604051633041949b60e01b815261200591633041949b91620056d3916001600160a01b03169088906004016200a0b0565b600060405180830381600087803b158015620056ee57600080fd5b505af115801562005703573d6000803e3d6000fd5b505050505050505050565b600054610100900460ff16158080156200572f5750600054600160ff909116105b806200574b5750303b1580156200574b575060005460ff166001145b620057b05760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401620012e3565b6000805460ff191660011790558015620057d4576000805461ff0019166101001790555b334114620057f55760405163022d8c9560e31b815260040160405180910390fd5b3a1562005815576040516383f1b1d360e01b815260040160405180910390fd5b611388603555686c6b935b8bbd400000603655670de0b6b3a7640000603755602d603855607860398190556002603a819055678ac7230489e80000603b55680ad78ebc5ac6200000603c55603d9190915560b4603e55604a556200588d7304d63abcd2b9b1baa327f2dda0f873f197ccd1866200938c565b80156200190e576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b3361200014620058fe57604051630f22c43960e41b81526120006004820152602401620012e3565b60405162461bcd60e51b815260206004820152600a60248201526919195c1c9958d85d195960b21b6044820152606401620012e3565b600054630100000090046001600160a01b0316331462005967576040516306fbb1e360e01b815260040160405180910390fd5b60005462010000900460ff16156200599257604051631785c68160e01b815260040160405180910390fd5b6000805462ff00001916620100001781556040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e7529190a1565b6000620059db603f8462008c2d565b620059f95760405163056e881160e01b815260040160405180910390fd5b6001600160a01b0383811660009081526041602052604090819020600201549051636bbf224960e01b815260048101859052911690636bbf2249906024015b602060405180830381865afa15801562005a56573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062005a7c91906200a129565b90505b92915050565b60005462010000900460ff161562005ab057604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562005ae25760405163b1d02c3d60e01b815260040160405180910390fd5b8162005af0603f8262008c2d565b62005b0e5760405163056e881160e01b815260040160405180910390fd5b603754349081101562005b345760405163dc6f0bdd60e01b815260040160405180910390fd5b6001600160a01b038085166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054339491608084019162005b9c906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462005bca906200a07a565b801562005c1b5780601f1062005bef5761010080835404028352916020019162005c1b565b820191906000526020600020905b81548152906001019060200180831162005bfd57829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462005c46906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462005c74906200a07a565b801562005cc55780601f1062005c995761010080835404028352916020019162005cc5565b820191906000526020600020905b81548152906001019060200180831162005ca757829003601f168201915b5050505050815260200160018201805462005ce0906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462005d0e906200a07a565b801562005d5f5780601f1062005d335761010080835404028352916020019162005d5f565b820191906000526020600020905b81548152906001019060200180831162005d4157829003601f168201915b5050505050815260200160028201805462005d7a906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462005da8906200a07a565b801562005df95780601f1062005dcd5761010080835404028352916020019162005df9565b820191906000526020600020905b81548152906001019060200180831162005ddb57829003601f168201915b5050505050815260200160038201805462005e14906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462005e42906200a07a565b801562005e935780601f1062005e675761010080835404028352916020019162005e93565b820191906000526020600020905b81548152906001019060200180831162005e7557829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b81548152602001906001019080831162005f325750505050508152505090508060e00151801562005f755750856001600160a01b0316826001600160a01b031614155b1562005f9457604051636468920360e01b815260040160405180910390fd5b60408082015190516317066a5760e21b81526001600160a01b0384811660048301526000921690635c19a95c90869060240160206040518083038185885af115801562005fe5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906200600c91906200a129565b9050826001600160a01b0316876001600160a01b03167f24d7bda8602b916d64417f0dbfe2e2e88ec9b1157bd9f596dfdb91ba26624e0483876040516200605d929190918252602082015260400190565b60405180910390a36040808301519051633041949b60e01b815261200591633041949b9162006092919087906004016200a0b0565b600060405180830381600087803b158015620060ad57600080fd5b505af1158015620060c2573d6000803e3d6000fd5b50505050851562001907576040516372f6ad8f60e11b81526120059063e5ed5b1e90620060f69086908b906004016200a0b0565b600060405180830381600087803b1580156200611157600080fd5b505af115801562006126573d6000803e3d6000fd5b5050505050505050505050565b60005462010000900460ff16156200615e57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620061905760405163b1d02c3d60e01b815260040160405180910390fd5b6200619a62008dcc565b620061a7603f8262008c2d565b620061c55760405163056e881160e01b815260040160405180910390fd5b6000620061d162008dcc565b6001600160a01b0381166000908152604f6020526040812080549293509190859003620062a85760005b818110156200627c57836001600160a01b03167f08e60c1b84aab23d99a7262015e647d5ffd6c6e08f78205e1df6774c48e1427a8483815481106200624457620062446200a17e565b90600052602060002001546040516200625f91815260200190565b60405180910390a28062006273816200a194565b915050620061fb565b506001600160a01b0383166000908152604f60205260408120620062a0916200967e565b505050505050565b60005b8581101562006406576000878783818110620062cb57620062cb6200a17e565b90506020020135905060005b83811015620063ee5781858281548110620062f657620062f66200a17e565b906000526020600020015403620063d95784620063156001866200a1b0565b815481106200632857620063286200a17e565b90600052602060002001548582815481106200634857620063486200a17e565b9060005260206000200181905550848054806200636957620063696200a5c5565b6001900381819060005260206000200160009055905583806200638c906200a5db565b945050856001600160a01b03167f08e60c1b84aab23d99a7262015e647d5ffd6c6e08f78205e1df6774c48e1427a83604051620063cb91815260200190565b60405180910390a2620063ee565b80620063e5816200a194565b915050620062d7565b50508080620063fd906200a194565b915050620062ab565b508154600003620062a0576001600160a01b0383166000908152604f60205260408120620062a0916200967e565b505050565b620064656040518060800160405280606081526020016060815260200160608152602001606081525090565b8162006473603f8262008c2d565b620064915760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03831660009081526041602052604090819020815160808101909252600501805482908290620064c8906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620064f6906200a07a565b8015620065475780601f106200651b5761010080835404028352916020019162006547565b820191906000526020600020905b8154815290600101906020018083116200652957829003601f168201915b5050505050815260200160018201805462006562906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462006590906200a07a565b8015620065e15780601f10620065b557610100808354040283529160200191620065e1565b820191906000526020600020905b815481529060010190602001808311620065c357829003601f168201915b50505050508152602001600282018054620065fc906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200662a906200a07a565b80156200667b5780601f106200664f576101008083540402835291602001916200667b565b820191906000526020600020905b8154815290600101906020018083116200665d57829003601f168201915b5050505050815260200160038201805462006696906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620066c4906200a07a565b8015620067155780601f10620066e95761010080835404028352916020019162006715565b820191906000526020600020905b815481529060010190602001808311620066f757829003601f168201915b505050505081525050915050919050565b60005462010000900460ff16156200675157604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620067835760405163b1d02c3d60e01b815260040160405180910390fd5b6200678f828262009425565b5050565b3361100714620067bb57604051630f22c43960e41b81526110076004820152602401620012e3565b620068286040518060400160405280601081526020016f1d1c985b9cd9995c91d85cd31a5b5a5d60821b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b15620068e357602081146200685a5783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f84018190048102820181019092528281526000916200689d9185858083850183828082843760009201919091525092939250506200958e9050565b90506108fc811080620068b1575061271081115b15620068da5784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b60355562007700565b620069546040518060400160405280601481526020017336b4b729b2b6332232b632b3b0ba34b7b721272160611b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b1562006a1e5760208114620069865783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f8401819004810282018101909252828152600091620069c99185858083850183828082843760009201919091525092939250506200958e9050565b9050683635c9adc5dea00000811080620069ec575069152d02c7e14af680000081115b1562006a155784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b60365562007700565b62006a91604051806040016040528060168152602001756d696e44656c65676174696f6e424e424368616e676560501b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b1562006b58576020811462006ac35783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f840181900481028201810190925282815260009162006b069185858083850183828082843760009201919091525092939250506200958e9050565b905067016345785d8a000081108062006b265750678ac7230489e8000081115b1562006b4f5784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b60375562007700565b62006bc9604051806040016040528060148152602001736d6178456c656374656456616c696461746f727360601b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b1562006c81576020811462006bfb5783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f840181900481028201810190925282815260009162006c3e9185858083850183828082843760009201919091525092939250506200958e9050565b905080158062006c4f57506101f481115b1562006c785784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b60385562007700565b62006cea6040518060400160405280600c81526020016b1d5b989bdb9914195c9a5bd960a21b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b1562006da7576020811462006d1c5783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f840181900481028201810190925282815260009162006d5f9185858083850183828082843760009201919091525092939250506200958e9050565b90506203f48081108062006d75575062278d0081115b1562006d9e5784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b60395562007700565b62006e1560405180604001604052806011815260200170726564656c65676174654665655261746560781b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b1562006ec2576020811462006e475783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f840181900481028201810190925282815260009162006e8a9185858083850183828082843760009201919091525092939250506200958e9050565b9050606481111562006eb95784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b603a5562007700565b62006f3260405180604001604052806013815260200172191bdddb9d1a5b5954db185cda105b5bdd5b9d606a1b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b1562006ff4576020811462006f645783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f840181900481028201810190925282815260009162006fa79185858083850183828082843760009201919091525092939250506200958e9050565b9050670de0b6b3a764000081108062006fc25750603c548110155b1562006feb5784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b603b5562007700565b620070626040518060400160405280601181526020017019995b1bdb9e54db185cda105b5bdd5b9d607a1b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b15620071245760208114620070945783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f8401819004810282018101909252828152600091620070d79185858083850183828082843760009201919091525092939250506200958e9050565b9050678ac7230489e80000811080620070f25750603b548111155b156200711b5784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b603c5562007700565b620071916040518060400160405280601081526020016f646f776e74696d654a61696c54696d6560801b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b156200724e5760208114620071c35783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f8401819004810282018101909252828152600091620072069185858083850183828082843760009201919091525092939250506200958e9050565b9050620151808110806200721c5750603e548110155b15620072455784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b603d5562007700565b620072b96040518060400160405280600e81526020016d66656c6f6e794a61696c54696d6560901b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b15620073765760208114620072eb5783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f84018190048102820181019092528281526000916200732e9185858083850183828082843760009201919091525092939250506200958e9050565b90506203f480811080620073445750603d548111155b156200736d5784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b603e5562007700565b620073f06040518060400160405280601c81526020017f6d617846656c6f6e794265747765656e42726561746865426c6f636b0000000081525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b156200749c5760208114620074225783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f8401819004810282018101909252828152600091620074659185858083850183828082843760009201919091525092939250506200958e9050565b905080600003620074935784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604a5562007700565b6200750a6040518060400160405280601181526020017039ba30b5b2a43ab1283937ba32b1ba37b960791b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b15620075ca57601481146200753c5783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b600062007584601484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092939250506200958e9050565b90506001600160a01b038116620075b85784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b620075c38162009593565b5062007700565b620076316040518060400160405280600a8152602001696d61784e6f646549447360b01b81525085858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293925050620095319050565b15620076dd5760208114620076635783838383604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604080516020601f8401819004810282018101909252828152600091620076a69185858083850183828082843760009201919091525092939250506200958e9050565b905080600003620076d45784848484604051630a5a604160e01b8152600401620012e394939291906200a5f5565b604e5562007700565b838383836040516325ee20d560e21b8152600401620012e394939291906200a5f5565b7ff1ce9b2cbf50eeb05769a29e2543fd350cab46894a7dd9978a12d534bb20e633848484846040516200773794939291906200a5f5565b60405180910390a150505050565b60005462010000900460ff16156200777057604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620077a25760405163b1d02c3d60e01b815260040160405180910390fd5b816000816001600160401b03811115620077c057620077c062009853565b604051908082528060200260200182016040528015620077ea578160200160208202803683370190505b5090506000805b83811015620078e857620078338787838181106200781357620078136200a17e565b90506020020160208101906200782a9190620096e4565b603f9062008c2d565b620078515760405163056e881160e01b815260040160405180910390fd5b604160008888848181106200786a576200786a6200a17e565b9050602002016020810190620078819190620096e4565b6001600160a01b0390811682526020820192909252604001600020600201548451911692508290849083908110620078bd57620078bd6200a17e565b6001600160a01b0390921660209283029190910190910152620078e0816200a194565b9050620077f1565b50604051634484077560e01b8152612005906344840775906200331990859088906004016200a1e0565b606080600062007923603f62008f89565b90508085101562007ae75783156200793c57836200793e565b805b93506000846200794f87846200a1b0565b1162007967576200796186836200a1b0565b62007969565b845b9050806001600160401b0381111562007986576200798662009853565b604051908082528060200260200182016040528015620079b0578160200160208202803683370190505b509350806001600160401b03811115620079ce57620079ce62009853565b604051908082528060200260200182016040528015620079f8578160200160208202803683370190505b50925060005b8181101562007ae45762007a17620044d382896200a113565b85828151811062007a2c5762007a2c6200a17e565b60200260200101906001600160a01b031690816001600160a01b0316815250506041600086838151811062007a655762007a656200a17e565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060020160009054906101000a90046001600160a01b031684828151811062007ab95762007ab96200a17e565b6001600160a01b039092166020928302919091019091015262007adc816200a194565b9050620079fe565b50505b9250925092565b60008162007afe603f8262008c2d565b62007b1c5760405163056e881160e01b815260040160405180910390fd5b50506001600160a01b03166000908152604160205260409020600c015490565b336110011462007b6457604051630f22c43960e41b81526110016004820152602401620012e3565b60005462010000900460ff161562007b8f57604051631785c68160e01b815260040160405180910390fd5b6001600160a01b038082166000908152604360205260409020541662007bb7603f8262008c2d565b62007bd55760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03811660009081526041602052604081209062007bfc610258426200a0f0565b604a546000828152604b60205260409020549192501162007c305760405163bd52fcdb60e01b815260040160405180910390fd5b6000818152604b6020526040812080546001929062007c519084906200a113565b90915550506001600160a01b0384166000908152604460205260409020541580159062007ca557506001600160a01b038416600090815260446020526040902054429062007ca390610258906200a113565b105b1562007cc4576040516330abb81d60e21b815260040160405180910390fd5b60008062007cd485600062008c50565b915091508162007cf757604051631b919bb160e11b815260040160405180910390fd5b6002840154603c5460405163045bc4d160e41b815260048101919091526000916001600160a01b0316906345bc4d10906024016020604051808303816000875af115801562007d4a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062007d7091906200a129565b905062007d7e858362008cd8565b856001600160a01b03167f6e9a2ee7aee95665e3a774a212eb11441b217e3e4656ab9563793094689aabb28383600060405162007dbe939291906200a143565b60405180910390a26002850154604051633041949b60e01b815261200591633041949b91620060f6916001600160a01b0316908a906004016200a0b0565b6001600160a01b038082166000908152604160209081526040808320815161018081018352815486168152600182015486169381019390935260028101549094169082015260038301546060820152600483018054929384938493849390929160808401919062007e6d906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462007e9b906200a07a565b801562007eec5780601f1062007ec05761010080835404028352916020019162007eec565b820191906000526020600020905b81548152906001019060200180831162007ece57829003601f168201915b505050505081526020016005820160405180608001604052908160008201805462007f17906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462007f45906200a07a565b801562007f965780601f1062007f6a5761010080835404028352916020019162007f96565b820191906000526020600020905b81548152906001019060200180831162007f7857829003601f168201915b5050505050815260200160018201805462007fb1906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462007fdf906200a07a565b8015620080305780601f10620080045761010080835404028352916020019162008030565b820191906000526020600020905b8154815290600101906020018083116200801257829003601f168201915b505050505081526020016002820180546200804b906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462008079906200a07a565b8015620080ca5780601f106200809e57610100808354040283529160200191620080ca565b820191906000526020600020905b815481529060010190602001808311620080ac57829003601f168201915b50505050508152602001600382018054620080e5906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462008113906200a07a565b8015620081645780601f10620081385761010080835404028352916020019162008164565b820191906000526020600020905b8154815290600101906020018083116200814657829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b815481526020019060010190808311620082035750505091909252505050606081015160e0820151610100909201519097919650945092505050565b60005462010000900460ff16156200826a57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff16156200829c5760405163b1d02c3d60e01b815260040160405180910390fd5b620082a662008e2a565b620082b3603f8262008c2d565b620082d15760405163056e881160e01b815260040160405180910390fd5b6000620082dd62008e2a565b6001600160a01b0381166000908152604160205260409020600c8101549192509042906200830f90610258906200a113565b11156200832f57604051631f92cdbd60e11b815260040160405180910390fd5b60058101805462008340906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200836e906200a07a565b8015620083bf5780601f106200839357610100808354040283529160200191620083bf565b820191906000526020600020905b815481529060010190602001808311620083a157829003601f168201915b5050508287525085916005840191508190620083dc90826200a62b565b5060208201516001820190620083f390826200a62b565b50604082015160028201906200840a90826200a62b565b50606082015160038201906200842190826200a62b565b505042600c830155506040516001600160a01b038316907f85d6366b336ade7f106987ec7a8eac1e8799e508aeab045a39d2f63e0dc969d990600090a250505050565b60005462010000900460ff16156200848f57604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff1615620084c15760405163b1d02c3d60e01b815260040160405180910390fd5b828114620084e2576040516341abc80160e01b815260040160405180910390fd5b60005b838110156200855357620085408585838181106200850757620085076200a17e565b90506020020160208101906200851e9190620096e4565b8484848181106200853357620085336200a17e565b9050602002013562009425565b6200854b816200a194565b9050620084e5565b5050505050565b6001600160a01b0380821660009081526041602090815260408083208151610180810183528154861681526001820154861693810193909352600281015490941690820152600383015460608201526004830180549293849390916080840191620085c5906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620085f3906200a07a565b8015620086445780601f10620086185761010080835404028352916020019162008644565b820191906000526020600020905b8154815290600101906020018083116200862657829003601f168201915b50505050508152602001600582016040518060800160405290816000820180546200866f906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200869d906200a07a565b8015620086ee5780601f10620086c257610100808354040283529160200191620086ee565b820191906000526020600020905b815481529060010190602001808311620086d057829003601f168201915b5050505050815260200160018201805462008709906200a07a565b80601f016020809104026020016040519081016040528092919081815260200182805462008737906200a07a565b8015620087885780601f106200875c5761010080835404028352916020019162008788565b820191906000526020600020905b8154815290600101906020018083116200876a57829003601f168201915b50505050508152602001600282018054620087a3906200a07a565b80601f0160208091040260200160405190810160405280929190818152602001828054620087d1906200a07a565b8015620088225780601f10620087f65761010080835404028352916020019162008822565b820191906000526020600020905b8154815290600101906020018083116200880457829003601f168201915b505050505081526020016003820180546200883d906200a07a565b80601f01602080910402602001604051908101604052809291908181526020018280546200886b906200a07a565b8015620088bc5780601f106200889057610100808354040283529160200191620088bc565b820191906000526020600020905b8154815290600101906020018083116200889e57829003601f168201915b505050919092525050508152604080516060808201835260098501546001600160401b038082168452600160401b82048116602080860191909152600160801b9092041683850152840191909152600a84015460ff16151582840152600b84015490830152600c8301546080830152600d8301546001600160a01b031660a0830152805161026081019182905260c09092019190600e84019060139082845b8154815260200190600101908083116200895b5750505091909252505050604001519392505050565b600062008993603f8462008c2d565b620089b15760405163056e881160e01b815260040160405180910390fd5b6001600160a01b038381166000908152604160205260409081902060020154905163aa1966cd60e01b81526004810185905291169063aa1966cd9060240162005a38565b60005462010000900460ff161562008a2057604051631785c68160e01b815260040160405180910390fd5b3360009081526001602052604090205460ff161562008a525760405163b1d02c3d60e01b815260040160405180910390fd5b62008a5c62008e2a565b62008a69603f8262008c2d565b62008a875760405163056e881160e01b815260040160405180910390fd5b600062008a9362008e2a565b905062008aa4818787878762009144565b62008ac257604051631647e3cb60e11b815260040160405180910390fd5b60006001600160a01b03166045878760405162008ae19291906200a0ca565b908152604051908190036020019020546001600160a01b03161462008b19576040516311fdb94760e01b815260040160405180910390fd5b6001600160a01b0381166000908152604160205260409020600c810154429062008b4790610258906200a113565b111562008b6757604051631f92cdbd60e11b815260040160405180910390fd5b4260468260040160405162008b7d91906200a6f3565b908152604051908190036020019020556004810162008b9e8789836200a2de565b5042600c820155604051829060459062008bbc908a908a906200a0ca565b90815260405190819003602001812080546001600160a01b039384166001600160a01b0319909116179055908316907f783156582145bd0ff7924fae6953ba054cf1233eb60739a200ddb10de068ff0d9062008c1c908a908a906200a5af565b60405180910390a250505050505050565b6001600160a01b0381166000908152600183016020526040812054151562005a7c565b6000806000848460405160200162008c6a9291906200a771565b60408051601f1981840301815291815281516020928301206000818152604c9093529120549091504281111562008caa5760008093509350505062002647565b603e5462008cb990426200a113565b6000928352604c60205260409092208290555060019590945092505050565b6000600162008ce8603f62008f89565b62008cf491906200a1b0565b604954108015915062008d405760018301546040516001600160a01b03909116907f2afdc18061ac21cff7d9f11527ab9c8dec6fabd4edf6f894ed634bebd6a20d4590600090a2505050565b82600b015482111562008d5557600b83018290555b600a83015460ff166200643457600a8301805460ff191660019081179091556049805460009062008d889084906200a113565b909155505060018301546040516001600160a01b03909116907f4905ac32602da3fb8b4b7b00c285e5fc4c6c2308cc908b4a1e4e9625a29c90a390600090a2505050565b336000908152604360205260408120546001600160a01b03161562008e085750336000908152604360205260409020546001600160a01b031690565b62008e1262008e2a565b905090565b604e5460000362008e28576005604e555b565b336000908152604d60205260408120546001600160a01b03161562008e665750336000908152604d60205260409020546001600160a01b031690565b503390565b6001600160a01b0381166000908152604160205260409020600a81015460ff161562008e95575050565b6036546002820154604051630913db4760e01b81526001600160a01b03858116600483015290911690630913db4790602401602060405180830381865afa15801562008ee5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062008f0b91906200a129565b10156200678f5762008f2d81603d544262008f2791906200a113565b62008cd8565b80546040516335409f7f60e01b81526001600160a01b039091166004820152611000906335409f7f90602401600060405180830381600087803b15801562008f7457600080fd5b505af1158015620062a0573d6000803e3d6000fd5b600062005a7f825490565b600062005a7c8383620095ff565b60008082905060038151108062008fba575060098151115b1562008fc95750600092915050565b60418160008151811062008fe15762008fe16200a17e565b016020015160f81c1080620090135750605a816000815181106200900957620090096200a17e565b016020015160f81c115b15620090225750600092915050565b60015b81518110156200913a5760308282815181106200904657620090466200a17e565b016020015160f81c108062009077575060398282815181106200906d576200906d6200a17e565b016020015160f81c115b8015620090c7575060418282815181106200909657620090966200a17e565b016020015160f81c1080620090c75750605a828281518110620090bd57620090bd6200a17e565b016020015160f81c115b80156200911757506061828281518110620090e657620090e66200a17e565b016020015160f81c1080620091175750607a8282815181106200910d576200910d6200a17e565b016020015160f81c115b1562009127575060009392505050565b62009132816200a194565b905062009025565b5060019392505050565b600060308414158062009158575060608214155b1562009167575060006200926b565b6000868686466040516020016200918294939291906200a7bb565b60408051808303601f1901815282825280516020918201208184528383019092529092506000919060208201818036833701905050905081602082015260008186868a8a604051602001620091dc9594939291906200a7e8565b60408051808303601f190181526001808452838301909252925060009190602082018180368337019050509050815160016020830182602086016066600019fa6200922657600080fd5b506000816000815181106200923f576200923f6200a17e565b016020015160f81c90506001811462009261576000955050505050506200926b565b6001955050505050505b95945050505050565b60008061200361dead6040516200928b906200969e565b6001600160a01b03928316815291166020820152606060408201819052600090820152608001604051809103906000f080158015620092ce573d6000803e3d6000fd5b509050806001600160a01b031663f399e22e3486866040518463ffffffff1660e01b8152600401620093029291906200a820565b6000604051808303818588803b1580156200931c57600080fd5b505af115801562009331573d6000803e3d6000fd5b50506040516001600160a01b038086169450881692507fd481492e4e93bb36b4c12a5af93f03be3bf04b454dfbc35dd2663fa26f44d5b09150600090a39392505050565b600062005a7c836001600160a01b0384166200962c565b600054610100900460ff16620093f95760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401620012e3565b600080546001600160a01b039092166301000000026301000000600160b81b0319909216919091179055565b8162009433603f8262008c2d565b620094515760405163056e881160e01b815260040160405180910390fd5b6001600160a01b03838116600090815260416020526040808220600201549051635569f64b60e11b8152336004820152602481018690529192169063aad3ec96906044016020604051808303816000875af1158015620094b5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620094db91906200a129565b9050336001600160a01b0316846001600160a01b03167ff7a40077ff7a04c7e61f6f26fb13774259ddf1b6bce9ecf26a8276cdd3992683836040516200952391815260200190565b60405180910390a350505050565b6000816040516020016200954691906200a846565b60405160208183030381529060405280519060200120836040516020016200956f91906200a846565b6040516020818303038152906040528051906020012014905092915050565b015190565b600080546040516001600160a01b0380851693630100000090930416917f44fc1b38a4abaa91ebd1b628a5b259a698f86238c8217d68f516e87769c60c0b91a3600080546001600160a01b039092166301000000026301000000600160b81b0319909216919091179055565b60008260000182815481106200961957620096196200a17e565b9060005260206000200154905092915050565b6000818152600183016020526040812054620096755750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915562005a7f565b50600062005a7f565b50805460008255906000526020600020908101906200190e9190620096ac565b610e96806200a86583390190565b5b80821115620096c35760008155600101620096ad565b5090565b80356001600160a01b0381168114620096df57600080fd5b919050565b600060208284031215620096f757600080fd5b62005a7c82620096c7565b60008083601f8401126200971557600080fd5b5081356001600160401b038111156200972d57600080fd5b6020830191508360208285010111156200264757600080fd5b600080602083850312156200975a57600080fd5b82356001600160401b038111156200977157600080fd5b6200977f8582860162009702565b90969095509350505050565b600080600060408486031215620097a157600080fd5b833560ff81168114620097b357600080fd5b925060208401356001600160401b03811115620097cf57600080fd5b620097dd8682870162009702565b9497909650939450505050565b60005b8381101562009807578181015183820152602001620097ed565b50506000910152565b600081518084526200982a816020860160208601620097ea565b601f01601f19169290920160200192915050565b60208152600062005a7c602083018462009810565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b03811182821017156200988e576200988e62009853565b60405290565b60006001600160401b0380841115620098b157620098b162009853565b604051601f8501601f19908116603f01168101908282118183101715620098dc57620098dc62009853565b81604052809350858152868686011115620098f657600080fd5b858560208301376000602087830101525050509392505050565b6000602082840312156200992357600080fd5b81356001600160401b038111156200993a57600080fd5b8201601f810184136200994c57600080fd5b6200995d8482356020840162009894565b949350505050565b60008083601f8401126200997857600080fd5b5081356001600160401b038111156200999057600080fd5b6020830191508360208260051b85010111156200264757600080fd5b60008060208385031215620099c057600080fd5b82356001600160401b03811115620099d757600080fd5b6200977f8582860162009965565b600081518084526020808501945080840160005b8381101562009a205781516001600160a01b031687529582019590820190600101620099f9565b509495945050505050565b60408152600062009a406040830185620099e5565b6020838203818501528185518084528284019150828160051b8501018388016000805b8481101562009aba57878403601f19018652825180518086529088019088860190845b8181101562009aa45783518352928a0192918a019160010162009a86565b5050968801969450509186019160010162009a63565b50919a9950505050505050505050565b6000806040838503121562009ade57600080fd5b62009ae983620096c7565b946020939093013593505050565b80358015158114620096df57600080fd5b6000806000806080858703121562009b1f57600080fd5b62009b2a85620096c7565b935062009b3a60208601620096c7565b92506040850135915062009b516060860162009af7565b905092959194509250565b6001600160401b03811681146200190e57600080fd5b60006020828403121562009b8557600080fd5b813562009b928162009b5c565b9392505050565b6000806040838503121562009bad57600080fd5b50508035926020909101359150565b60808152600062009bd16080830187620099e5565b82810360208481019190915286518083528782019282019060005b8181101562009c0a5784518352938301939183019160010162009bec565b5050848103604086015286518082528282019350600581901b8201830183890160005b8381101562009c5f57601f1985840301875262009c4c83835162009810565b9686019692509085019060010162009c2d565b5050809550505050505082606083015295945050505050565b600080600080600080600087890360e081121562009c9557600080fd5b62009ca089620096c7565b975060208901356001600160401b038082111562009cbd57600080fd5b62009ccb8c838d0162009702565b909950975060408b013591508082111562009ce557600080fd5b62009cf38c838d0162009702565b90975095508591506060605f198401121562009d0e57600080fd5b60608b01945060c08b013592508083111562009d2957600080fd5b505088016080818b03121562009d3e57600080fd5b8091505092959891949750929550565b6000806040838503121562009d6257600080fd5b62009d6d83620096c7565b915062009d7d6020840162009af7565b90509250929050565b60208152600082516080602084015262009da460a084018262009810565b90506020840151601f198085840301604086015262009dc4838362009810565b9250604086015191508085840301606086015262009de3838362009810565b92506060860151915080858403016080860152506200926b828262009810565b6000806000806040858703121562009e1a57600080fd5b84356001600160401b038082111562009e3257600080fd5b62009e408883890162009702565b9096509450602087013591508082111562009e5a57600080fd5b5062009e698782880162009702565b95989497509550505050565b60008060006040848603121562009e8b57600080fd5b83356001600160401b0381111562009ea257600080fd5b62009eb08682870162009965565b909450925062009ec5905060208501620096c7565b90509250925092565b60608152600062009ee36060830186620099e5565b828103602084015262009ef78186620099e5565b915050826040830152949350505050565b600082601f83011262009f1a57600080fd5b62005a7c8383356020850162009894565b60006020828403121562009f3e57600080fd5b81356001600160401b038082111562009f5657600080fd5b908301906080828603121562009f6b57600080fd5b62009f7562009869565b82358281111562009f8557600080fd5b62009f938782860162009f08565b82525060208301358281111562009fa957600080fd5b62009fb78782860162009f08565b60208301525060408301358281111562009fd057600080fd5b62009fde8782860162009f08565b60408301525060608301358281111562009ff757600080fd5b6200a0058782860162009f08565b60608301525095945050505050565b600080600080604085870312156200a02b57600080fd5b84356001600160401b03808211156200a04357600080fd5b6200a0518883890162009965565b909650945060208701359150808211156200a06b57600080fd5b5062009e698782880162009965565b600181811c908216806200a08f57607f821691505b6020821081036200207957634e487b7160e01b600052602260045260246000fd5b6001600160a01b0392831681529116602082015260400190565b8183823760009101908152919050565b634e487b7160e01b600052601160045260246000fd5b6000826200a10e57634e487b7160e01b600052601260045260246000fd5b500490565b8082018082111562005a7f5762005a7f6200a0da565b6000602082840312156200a13c57600080fd5b5051919050565b8381526020810183905260608101600383106200a17057634e487b7160e01b600052602160045260246000fd5b826040830152949350505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016200a1a9576200a1a96200a0da565b5060010190565b8181038181111562005a7f5762005a7f6200a0da565b808202811582820484141762005a7f5762005a7f6200a0da565b6040815260006200a1f56040830185620099e5565b905060018060a01b03831660208301529392505050565b6001600160401b038281168282160390808211156200a22f576200a22f6200a0da565b5092915050565b6000808335601e198436030181126200a24e57600080fd5b8301803591506001600160401b038211156200a26957600080fd5b6020019150368190038213156200264757600080fd5b601f8211156200643457600081815260208120601f850160051c810160208610156200a2a85750805b601f850160051c820191505b81811015620062a0578281556001016200a2b4565b600019600383901b1c191660019190911b1790565b6001600160401b038311156200a2f8576200a2f862009853565b6200a310836200a30983546200a07a565b836200a27f565b6000601f8411600181146200a34357600085156200a32e5750838201355b6200a33a86826200a2c9565b84555062008553565b600083815260209020601f19861690835b828110156200a37657868501358255602094850194600190920191016200a354565b50868210156200a3945760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b6200a3b282836200a236565b6001600160401b038111156200a3cc576200a3cc62009853565b6200a3e4816200a3dd85546200a07a565b856200a27f565b6000601f8211600181146200a41757600083156200a4025750838201355b6200a40e84826200a2c9565b8655506200a474565b600085815260209020601f19841690835b828110156200a44a57868501358255602094850194600190920191016200a428565b50848210156200a4685760001960f88660031b161c19848701351681555b505060018360011b0185555b505050506200a48760208301836200a236565b6200a4978183600186016200a2de565b50506200a4a860408301836200a236565b6200a4b88183600286016200a2de565b50506200a4c960608301836200a236565b6200a4d98183600386016200a2de565b50505050565b81356200a4ec8162009b5c565b6001600160401b03811690508154816001600160401b0319821617835560208401356200a5198162009b5c565b6fffffffffffffffff0000000000000000604091821b166fffffffffffffffffffffffffffffffff198316841781178555908501356200a5598162009b5c565b6001600160c01b0319929092169092179190911760809190911b67ffffffffffffffff60801b1617905550565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6020815260006200995d6020830184866200a586565b634e487b7160e01b600052603160045260246000fd5b6000816200a5ed576200a5ed6200a0da565b506000190190565b6040815260006200a60b6040830186886200a586565b82810360208401526200a6208185876200a586565b979650505050505050565b81516001600160401b038111156200a647576200a64762009853565b6200a65f816200a65884546200a07a565b846200a27f565b602080601f8311600181146200a69357600084156200a67e5750858301515b6200a68a85826200a2c9565b865550620062a0565b600085815260208120601f198616915b828110156200a6c4578886015182559484019460019091019084016200a6a3565b50858210156200a6e35787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60008083546200a703816200a07a565b600182811680156200a71e57600181146200a734576200a765565b60ff19841687528215158302870194506200a765565b8760005260208060002060005b858110156200a75c5781548a8201529084019082016200a741565b50505082870194505b50929695505050505050565b6bffffffffffffffffffffffff198360601b1681526000600383106200a7a757634e487b7160e01b600052602160045260246000fd5b5060f89190911b6014820152601501919050565b6bffffffffffffffffffffffff198560601b16815282846014830137601492019182015260340192915050565b600086516200a7fc818460208b01620097ea565b82018587823760009086019081528385823760009301928352509095945050505050565b6001600160a01b03831681526040602082018190526000906200995d9083018462009810565b600082516200a85a818460208701620097ea565b919091019291505056fe608060405260405162000e9638038062000e96833981016040819052620000269162000497565b828162000036828260006200004d565b50620000449050826200008a565b505050620005ca565b6200005883620000e5565b600082511180620000665750805b1562000085576200008383836200012760201b620001691760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000b562000156565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000e2816200018f565b50565b620000f08162000244565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200014f838360405180606001604052806027815260200162000e6f60279139620002f8565b9392505050565b60006200018060008051602062000e4f83398151915260001b6200037760201b620001951760201c565b546001600160a01b0316919050565b6001600160a01b038116620001fa5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200022360008051602062000e4f83398151915260001b6200037760201b620001951760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6200025a816200037a60201b620001981760201c565b620002be5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001f1565b80620002237f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200037760201b620001951760201c565b6060600080856001600160a01b03168560405162000317919062000577565b600060405180830381855af49150503d806000811462000354576040519150601f19603f3d011682016040523d82523d6000602084013e62000359565b606091505b5090925090506200036d8683838762000389565b9695505050505050565b90565b6001600160a01b03163b151590565b60608315620003fd578251600003620003f5576001600160a01b0385163b620003f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001f1565b508162000409565b62000409838362000411565b949350505050565b815115620004225781518083602001fd5b8060405162461bcd60e51b8152600401620001f1919062000595565b80516001600160a01b03811681146200045657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200048e57818101518382015260200162000474565b50506000910152565b600080600060608486031215620004ad57600080fd5b620004b8846200043e565b9250620004c8602085016200043e565b60408501519092506001600160401b0380821115620004e657600080fd5b818601915086601f830112620004fb57600080fd5b8151818111156200051057620005106200045b565b604051601f8201601f19908116603f011681019083821181831017156200053b576200053b6200045b565b816040528281528960208487010111156200055557600080fd5b6200056883602083016020880162000471565b80955050505050509250925092565b600082516200058b81846020870162000471565b9190910192915050565b6020815260008251806020840152620005b681604085016020870162000471565b601f01601f19169190910160400192915050565b61087580620005da6000396000f3fe60806040523661001357610011610017565b005b6100115b61001f6101a7565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a576100536101da565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a57610053610231565b63070d7c6960e41b6001600160e01b031982160161009a57610053610277565b621eb96f60e61b6001600160e01b03198216016100b9576100536102a8565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102e8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102fc565b565b606061018e83836040518060600160405280602781526020016108426027913961030c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101e4610384565b60006101f33660048184610695565b81019061020091906106db565b905061021d8160405180602001604052806000815250600061038f565b505060408051602081019091526000815290565b60606000806102433660048184610695565b810190610250919061070c565b915091506102608282600161038f565b604051806020016040528060008152509250505090565b6060610281610384565b60006102903660048184610695565b81019061029d91906106db565b905061021d816103bb565b60606102b2610384565b60006102bc6101a7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102f2610384565b60006102bc610412565b610167610307610412565b610421565b6060600080856001600160a01b03168560405161032991906107f2565b600060405180830381855af49150503d8060008114610364576040519150601f19603f3d011682016040523d82523d6000602084013e610369565b606091505b509150915061037a86838387610445565b9695505050505050565b341561016757600080fd5b610398836104c6565b6000825111806103a55750805b156103b6576103b48383610169565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e46101a7565b604080516001600160a01b03928316815291841660208301520160405180910390a161040f81610506565b50565b600061041c6105af565b905090565b3660008037600080366000845af43d6000803e808015610440573d6000f35b3d6000fd5b606083156104b45782516000036104ad576001600160a01b0385163b6104ad5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b50816104be565b6104be83836105d7565b949350505050565b6104cf81610601565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b03811661056b5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101cb565b8151156105e75781518083602001fd5b8060405162461bcd60e51b815260040161014e919061080e565b6001600160a01b0381163b61066e5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61058e565b600080858511156106a557600080fd5b838611156106b257600080fd5b5050820193919092039150565b80356001600160a01b03811681146106d657600080fd5b919050565b6000602082840312156106ed57600080fd5b61018e826106bf565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561071f57600080fd5b610728836106bf565b9150602083013567ffffffffffffffff8082111561074557600080fd5b818501915085601f83011261075957600080fd5b81358181111561076b5761076b6106f6565b604051601f8201601f19908116603f01168101908382118183101715610793576107936106f6565b816040528281528860208487010111156107ac57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107e95781810151838201526020016107d1565b50506000910152565b600082516108048184602087016107ce565b9190910192915050565b602081526000825180602084015261082d8160408501602087016107ce565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a164736f6c6343000811000ab53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a164736f6c6343000811000a \ No newline at end of file diff --git a/core/systemcontracts/fermi/types.go b/core/systemcontracts/fermi/types.go new file mode 100644 index 0000000000..16a2e25010 --- /dev/null +++ b/core/systemcontracts/fermi/types.go @@ -0,0 +1,21 @@ +package fermi + +import _ "embed" + +// contract codes for Mainnet upgrade +var ( + //go:embed mainnet/StakeHubContract + MainnetStakeHubContract string +) + +// contract codes for Chapel upgrade +var ( + //go:embed chapel/StakeHubContract + ChapelStakeHubContract string +) + +// contract codes for Rialto upgrade +var ( + //go:embed rialto/StakeHubContract + RialtoStakeHubContract string +) diff --git a/core/systemcontracts/upgrade.go b/core/systemcontracts/upgrade.go index bd586e148f..8920379cf3 100644 --- a/core/systemcontracts/upgrade.go +++ b/core/systemcontracts/upgrade.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/core/systemcontracts/bohr" "github.com/ethereum/go-ethereum/core/systemcontracts/bruno" "github.com/ethereum/go-ethereum/core/systemcontracts/euler" + "github.com/ethereum/go-ethereum/core/systemcontracts/fermi" "github.com/ethereum/go-ethereum/core/systemcontracts/feynman" feynmanFix "github.com/ethereum/go-ethereum/core/systemcontracts/feynman_fix" "github.com/ethereum/go-ethereum/core/systemcontracts/gibbs" @@ -92,6 +93,8 @@ var ( lorentzUpgrade = make(map[string]*Upgrade) maxwellUpgrade = make(map[string]*Upgrade) + + fermiUpgrade = make(map[string]*Upgrade) ) func init() { @@ -1019,6 +1022,39 @@ func init() { }, }, } + + fermiUpgrade[mainNet] = &Upgrade{ + UpgradeName: "fermi", + Configs: []*UpgradeConfig{ + { + ContractAddr: common.HexToAddress(StakeHubContract), + CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/34618f607f8356cf147dde6a69fae150bd53d5bf", + Code: fermi.MainnetStakeHubContract, + }, + }, + } + + fermiUpgrade[chapelNet] = &Upgrade{ + UpgradeName: "fermi", + Configs: []*UpgradeConfig{ + { + ContractAddr: common.HexToAddress(StakeHubContract), + CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/34618f607f8356cf147dde6a69fae150bd53d5bf", + Code: fermi.ChapelStakeHubContract, + }, + }, + } + + fermiUpgrade[rialtoNet] = &Upgrade{ + UpgradeName: "fermi", + Configs: []*UpgradeConfig{ + { + ContractAddr: common.HexToAddress(StakeHubContract), + CommitUrl: "https://github.com/bnb-chain/bsc-genesis-contract/commit/34618f607f8356cf147dde6a69fae150bd53d5bf", + Code: fermi.RialtoStakeHubContract, + }, + }, + } } func TryUpdateBuildInSystemContract(config *params.ChainConfig, blockNumber *big.Int, lastBlockTime uint64, blockTime uint64, statedb vm.StateDB, atBlockBegin bool) { @@ -1134,6 +1170,10 @@ func upgradeBuildInSystemContract(config *params.ChainConfig, blockNumber *big.I applySystemContractUpgrade(maxwellUpgrade[network], blockNumber, statedb, logger) } + if config.IsOnFermi(blockNumber, lastBlockTime, blockTime) { + applySystemContractUpgrade(fermiUpgrade[network], blockNumber, statedb, logger) + } + /* apply other upgrades */ diff --git a/core/txindexer.go b/core/txindexer.go index 76fc511de7..a52599fa8a 100644 --- a/core/txindexer.go +++ b/core/txindexer.go @@ -235,8 +235,8 @@ func (indexer *txIndexer) loop(chain *BlockChain) { // Listening to chain events and manipulate the transaction indexes. var ( - stop chan struct{} // Non-nil if background routine is active - done chan struct{} // Non-nil if background routine is active + stop chan struct{} // Non-nil if background routine is active. + done chan struct{} // Non-nil if background routine is active. headCh = make(chan ChainHeadEvent) sub = chain.SubscribeChainHeadEvent(headCh) ) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 8704fa4ed0..dc6b7eaa43 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -573,8 +573,8 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address] if filter.OnlyBlobTxs { return nil } - pool.mu.Lock() - defer pool.mu.Unlock() + pool.mu.RLock() + defer pool.mu.RUnlock() // Convert the new uint256.Int types to the old big.Int ones used by the legacy pool var ( diff --git a/core/types.go b/core/types.go index 2e27fcd5d5..e27395f5c3 100644 --- a/core/types.go +++ b/core/types.go @@ -49,6 +49,8 @@ type Prefetcher interface { Prefetch(transactions types.Transactions, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) // PrefetchMining used for pre-caching transaction signatures and state trie nodes. Only used for mining stage. PrefetchMining(txs TransactionsByPriceAndNonce, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg vm.Config, interruptCh <-chan struct{}, txCurr **types.Transaction) + // prefetch based on block access list + PrefetchBAL(block *types.Block, statedb *state.StateDB, interruptChan <-chan struct{}) } // Processor is an interface for processing blocks using a given initial state. diff --git a/core/types/block.go b/core/types/block.go index 62d486cf7f..4f4e47d9e1 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -235,6 +235,123 @@ type Body struct { Withdrawals []*Withdrawal `rlp:"optional"` } +// StorageAccessItem is a single storage key that is accessed in a block. +type StorageAccessItem struct { + TxIndex uint32 // index of the first transaction in the block that accessed the storage + Dirty bool // true if the storage was modified in the block, false if it was read only + Key common.Hash +} + +// AccountAccessListEncode & BlockAccessListEncode are for BAL serialization. +type AccountAccessListEncode struct { + TxIndex uint32 // index of the first transaction in the block that accessed the account + Address common.Address + StorageItems []StorageAccessItem +} + +type BlockAccessListEncode struct { + Version uint32 // Version of the access list format + Number uint64 // number of the block that the BAL is for + Hash common.Hash // hash of the block that the BAL is for + SignData []byte // sign data for BAL + Accounts []AccountAccessListEncode +} + +// TxAccessListPrefetch & BlockAccessListPrefetch are for BAL prefetch +type StorageAccessItemPrefetch struct { + Dirty bool + Key common.Hash +} + +type TxAccessListPrefetch struct { + Accounts map[common.Address][]StorageAccessItemPrefetch +} + +type BlockAccessListPrefetch struct { + AccessListItems map[uint32]TxAccessListPrefetch +} + +func (b *BlockAccessListPrefetch) Update(aclEncode *AccountAccessListEncode) { + if aclEncode == nil { + return + } + accAddr := aclEncode.Address + b.PrepareTxAccount(aclEncode.TxIndex, accAddr) + for _, storageItem := range aclEncode.StorageItems { + b.PrepareTxStorage(accAddr, storageItem) + } +} + +func (b *BlockAccessListPrefetch) PrepareTxStorage(accAddr common.Address, storageItem StorageAccessItem) { + b.PrepareTxAccount(storageItem.TxIndex, accAddr) + txAccessList := b.AccessListItems[storageItem.TxIndex] + txAccessList.Accounts[accAddr] = append(txAccessList.Accounts[accAddr], StorageAccessItemPrefetch{ + Dirty: storageItem.Dirty, + Key: storageItem.Key, + }) +} +func (b *BlockAccessListPrefetch) PrepareTxAccount(txIndex uint32, addr common.Address) { + // create the tx access list if not exists + if _, ok := b.AccessListItems[txIndex]; !ok { + b.AccessListItems[txIndex] = TxAccessListPrefetch{ + Accounts: make(map[common.Address][]StorageAccessItemPrefetch), + } + } + // create the account access list if not exists + if _, ok := b.AccessListItems[txIndex].Accounts[addr]; !ok { + b.AccessListItems[txIndex].Accounts[addr] = make([]StorageAccessItemPrefetch, 0) + } +} + +// BlockAccessListRecord & BlockAccessListRecord are used to record access list during tx execution. +type AccountAccessListRecord struct { + TxIndex uint32 // index of the first transaction in the block that accessed the account + StorageItems map[common.Hash]StorageAccessItem +} + +type BlockAccessListRecord struct { + Version uint32 // Version of the access list format + Accounts map[common.Address]AccountAccessListRecord +} + +func (b *BlockAccessListRecord) AddAccount(addr common.Address, txIndex uint32) { + if b == nil { + return + } + + if _, ok := b.Accounts[addr]; !ok { + b.Accounts[addr] = AccountAccessListRecord{ + TxIndex: txIndex, + StorageItems: make(map[common.Hash]StorageAccessItem), + } + } +} + +func (b *BlockAccessListRecord) AddStorage(addr common.Address, key common.Hash, txIndex uint32, dirty bool) { + if b == nil { + return + } + + if _, ok := b.Accounts[addr]; !ok { + b.Accounts[addr] = AccountAccessListRecord{ + TxIndex: txIndex, + StorageItems: make(map[common.Hash]StorageAccessItem), + } + } + + if _, ok := b.Accounts[addr].StorageItems[key]; !ok { + b.Accounts[addr].StorageItems[key] = StorageAccessItem{ + TxIndex: txIndex, + Dirty: dirty, + Key: key, + } + } else { + storageItem := b.Accounts[addr].StorageItems[key] + storageItem.Dirty = dirty + b.Accounts[addr].StorageItems[key] = storageItem + } +} + // Block represents an Ethereum block. // // Note the Block type tries to be 'immutable', and contains certain caches that rely @@ -274,6 +391,10 @@ type Block struct { // sidecars provides DA check sidecars BlobSidecars + + // bal provides block access list + bal *BlockAccessListEncode + balSize atomic.Uint64 } // "external" block encoding. used for eth protocol, etc. @@ -496,6 +617,19 @@ func (b *Block) Size() uint64 { return uint64(c) } +func (b *Block) BALSize() uint64 { + if b.bal == nil { + return 0 + } + if size := b.balSize.Load(); size > 0 { + return size + } + c := writeCounter(0) + rlp.Encode(&c, b.bal) + b.balSize.Store(uint64(c)) + return uint64(c) +} + func (b *Block) SetRoot(root common.Hash) { b.header.Root = root } // SanityCheck can be used to prevent that unbounded fields are @@ -508,6 +642,10 @@ func (b *Block) Sidecars() BlobSidecars { return b.sidecars } +func (b *Block) BAL() *BlockAccessListEncode { + return b.bal +} + func (b *Block) CleanSidecars() { b.sidecars = make(BlobSidecars, 0) } @@ -563,6 +701,7 @@ func (b *Block) WithSeal(header *Header) *Block { withdrawals: b.withdrawals, witness: b.witness, sidecars: b.sidecars, + bal: b.bal, } } @@ -576,6 +715,7 @@ func (b *Block) WithBody(body Body) *Block { withdrawals: slices.Clone(body.Withdrawals), witness: b.witness, sidecars: b.sidecars, + bal: b.bal, } for i := range body.Uncles { block.uncles[i] = CopyHeader(body.Uncles[i]) @@ -591,6 +731,7 @@ func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *Block { uncles: b.uncles, witness: b.witness, sidecars: b.sidecars, + bal: b.bal, } if withdrawals != nil { block.withdrawals = make([]*Withdrawal, len(withdrawals)) @@ -607,6 +748,7 @@ func (b *Block) WithSidecars(sidecars BlobSidecars) *Block { uncles: b.uncles, withdrawals: b.withdrawals, witness: b.witness, + bal: b.bal, } if sidecars != nil { block.sidecars = make(BlobSidecars, len(sidecars)) @@ -615,6 +757,23 @@ func (b *Block) WithSidecars(sidecars BlobSidecars) *Block { return block } +func (b *Block) WithBAL(bal *BlockAccessListEncode) *Block { + block := &Block{ + header: b.header, + transactions: b.transactions, + uncles: b.uncles, + withdrawals: b.withdrawals, + witness: b.witness, + sidecars: b.sidecars, + } + block.bal = bal + return block +} + +func (b *Block) UpdateBAL(bal *BlockAccessListEncode) { + b.bal = bal +} + func (b *Block) WithWitness(witness *ExecutionWitness) *Block { return &Block{ header: b.header, diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index 2a31e5ec2d..57124d0faa 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -60,11 +60,10 @@ type BlobTxSidecar struct { // NOTE(BSC): PeerDAS support (EIP-7594) is disabled. // Only sidecar Version = 0 (EIP-4844 legacy proofs) is supported for now. // See upstream PR: https://github.com/ethereum/go-ethereum/pull/31791 - Version byte `json:"version" rlp:"-"` // Sidecar version - - Blobs []kzg4844.Blob `json:"blobs"` // Blobs needed by the blob pool - Commitments []kzg4844.Commitment `json:"commitments"` // Commitments needed by the blob pool - Proofs []kzg4844.Proof `json:"proofs"` // Proofs needed by the blob pool + Version byte `json:"version" rlp:"-"` // Sidecar version + Blobs []kzg4844.Blob `json:"blobs"` // Blobs needed by the blob pool + Commitments []kzg4844.Commitment `json:"commitments"` // Commitments needed by the blob pool + Proofs []kzg4844.Proof `json:"proofs"` // Proofs needed by the blob pool } // BlobHashes computes the blob hashes of the given blobs. diff --git a/core/vm/analysis_legacy.go b/core/vm/analysis_legacy.go index 38af9084ac..30881f23f8 100644 --- a/core/vm/analysis_legacy.go +++ b/core/vm/analysis_legacy.go @@ -116,3 +116,133 @@ func codeBitmapInternal(code, bits bitvec) bitvec { } return bits } + +// codeBitmap collects data locations in code. +func codeBitmapWhitSI(code []byte) bitvec { + // The bitmap is 4 bytes longer than necessary, in case the code + // ends with a PUSH32, the algorithm will set bits on the + // bitvector outside the bounds of the actual code. + bits := make(bitvec, len(code)/8+1+4) + return codeBitmapInternalWhitSI(code, bits) +} + +// codeBitmapInternal is the internal implementation of codeBitmap. +// It exists for the purpose of being able to run benchmark tests +// without dynamic allocations affecting the results. +func codeBitmapInternalWhitSI(code, bits bitvec) bitvec { + for pc := uint64(0); pc < uint64(len(code)); { + op := OpCode(code[pc]) + pc++ + + // handle super instruction. + step, processed := codeBitmapForSI(code, pc, op, &bits) + if processed { + pc += step + continue + } + + if int8(op) < int8(PUSH1) { // If not PUSH (the int8(op) > int(PUSH32) is always false). + continue + } + numbits := op - PUSH1 + 1 + if numbits >= 8 { + for ; numbits >= 16; numbits -= 16 { + bits.set16(pc) + pc += 16 + } + for ; numbits >= 8; numbits -= 8 { + bits.set8(pc) + pc += 8 + } + } + switch numbits { + case 1: + bits.set1(pc) + pc += 1 + case 2: + bits.setN(set2BitsMask, pc) + pc += 2 + case 3: + bits.setN(set3BitsMask, pc) + pc += 3 + case 4: + bits.setN(set4BitsMask, pc) + pc += 4 + case 5: + bits.setN(set5BitsMask, pc) + pc += 5 + case 6: + bits.setN(set6BitsMask, pc) + pc += 6 + case 7: + bits.setN(set7BitsMask, pc) + pc += 7 + } + } + return bits +} + +func codeBitmapForSI(code []byte, pc uint64, op OpCode, bits *bitvec) (step uint64, processed bool) { + // pc points to the data pointer for push, or the next op for opcode + // bits marks the data bytes pointed by [pc] + switch op { + case Push2Jump, Push2JumpI: + bits.setN(set2BitsMask, pc) + step = 3 + processed = true + case Push1Push1: + bits.set1(pc) + bits.set1(pc + 2) + step = 3 + processed = true + case Push1Add, Push1Shl, Push1Dup1: + bits.set1(pc) + step = 2 + processed = true + case JumpIfZero: + bits.setN(set2BitsMask, pc+1) + step = 4 + processed = true + case IsZeroPush2: + bits.setN(set2BitsMask, pc+1) + step = 3 + processed = true + case Dup2MStorePush1Add: + bits.set1(pc + 2) + step = 4 + processed = true + case Dup1Push4EqPush2: + bits.setN(set4BitsMask, pc+1) + bits.setN(set2BitsMask, pc+7) + step = 9 + processed = true + case Push1CalldataloadPush1ShrDup1Push4GtPush2: + bits.set1(pc) + bits.set1(pc + 3) + bits.setN(set4BitsMask, pc+7) + bits.setN(set2BitsMask, pc+13) + step = 15 + processed = true + case Push1Push1Push1SHLSub: + bits.set1(pc) + bits.set1(pc + 2) + bits.set1(pc + 4) + step = 7 + processed = true + case Swap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT: + bits.set1(pc + 1) + step = 12 + processed = true + case Swap2Swap1Dup3SubSwap2Dup3GtPush2: + bits.setN(set2BitsMask, pc+7) + step = 9 + processed = true + case SubSLTIsZeroPush2: + bits.setN(set2BitsMask, pc+3) + step = 5 + processed = true + default: + return 0, false + } + return step, processed +} diff --git a/core/vm/contract.go b/core/vm/contract.go index f7995f6618..bf71549c35 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -19,6 +19,7 @@ package vm import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/lru" + "github.com/ethereum/go-ethereum/core/opcodeCompiler/compiler" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/metrics" "github.com/holiman/uint256" @@ -53,8 +54,10 @@ type Contract struct { IsDeployment bool IsSystemCall bool - Gas uint64 - value *uint256.Int + Gas uint64 + value *uint256.Int + optimized bool + codeBitmapFunc func(code []byte) bitvec } func (c *Contract) validJumpdest(dest *uint256.Int) bool { @@ -88,10 +91,17 @@ func (c *Contract) isCode(udest uint64) bool { if cached, ok := codeBitmapCache.Get(c.CodeHash); ok { contractCodeBitmapHitMeter.Mark(1) analysis = cached + } else if c.optimized { + analysis = compiler.LoadBitvec(c.CodeHash) + if analysis == nil { + analysis = c.codeBitmapFunc(c.Code) + compiler.StoreBitvec(c.CodeHash, analysis) + } + c.jumpdests[c.CodeHash] = analysis } else { // Do the analysis and save in parent context // We do not need to store it in c.analysis - analysis = codeBitmap(c.Code) + analysis = c.codeBitmapFunc(c.Code) c.jumpdests[c.CodeHash] = analysis contractCodeBitmapMissMeter.Mark(1) codeBitmapCache.Add(c.CodeHash, analysis) @@ -106,7 +116,7 @@ func (c *Contract) isCode(udest uint64) bool { // we don't have to recalculate it for every JUMP instruction in the execution // However, we don't save it within the parent context if c.analysis == nil { - c.analysis = codeBitmap(c.Code) + c.analysis = c.codeBitmapFunc(c.Code) } return c.analysis.codeSegment(udest) } @@ -161,8 +171,16 @@ func (c *Contract) Value() *uint256.Int { return c.value } -// SetCallCode sets the code of the contract, +// SetCallCode sets the code of the contract and address of the backing data +// object func (c *Contract) SetCallCode(hash common.Hash, code []byte) { c.Code = code c.CodeHash = hash } + +// SetOptimizedForTest returns a contract with optimized equals true for test purpose only +func (c *Contract) SetOptimizedForTest() *Contract { + c.optimized = true + + return c +} diff --git a/core/vm/contract_pool.go b/core/vm/contract_pool.go index a10cab6bb9..ab41938286 100644 --- a/core/vm/contract_pool.go +++ b/core/vm/contract_pool.go @@ -32,6 +32,7 @@ func GetContract(caller common.Address, address common.Address, value *uint256.I if jumpDests == nil { jumpDests = make(map[common.Hash]bitvec) } + contract.codeBitmapFunc = codeBitmap contract.jumpdests = jumpDests contract.analysis = nil diff --git a/core/vm/errors.go b/core/vm/errors.go index e33c9fcb85..dcda842e5d 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -38,6 +38,7 @@ var ( ErrGasUintOverflow = errors.New("gas uint64 overflow") ErrInvalidCode = errors.New("invalid code: must not begin with 0xef") ErrNonceUintOverflow = errors.New("nonce uint64 overflow") + ErrInvalidOptimizedCode = errors.New("cannot use optimized code when optimize config is false") // errStopToken is an internal token indicating interpreter loop termination, // never returned to outside callers. diff --git a/core/vm/evm.go b/core/vm/evm.go index 9a9a48a0f1..3d95861d6a 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -24,6 +24,7 @@ import ( "github.com/holiman/uint256" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/opcodeCompiler/compiler" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" @@ -124,8 +125,11 @@ type EVM struct { precompiles map[common.Address]PrecompiledContract // jumpDests is the aggregated result of JUMPDEST analysis made through - // the life cycle of EVM. jumpDests map[common.Hash]bitvec + + // the life cycle of EVM. + optInterpreter *EVMInterpreter + baseInterpreter *EVMInterpreter } // NewEVM constructs an EVM instance with the supplied block context, state @@ -142,11 +146,35 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon jumpDests: make(map[common.Hash]bitvec), } evm.precompiles = activePrecompiledContracts(evm.chainRules) - evm.interpreter = NewEVMInterpreter(evm) + evm.baseInterpreter = NewEVMInterpreter(evm) + evm.interpreter = evm.baseInterpreter + if evm.Config.EnableOpcodeOptimizations { + evm.optInterpreter = NewEVMInterpreter(evm) + evm.optInterpreter.CopyAndInstallSuperInstruction() + evm.interpreter = evm.optInterpreter + compiler.EnableOptimization() + } return evm } +func (evm *EVM) UseOptInterpreter() { + if !evm.Config.EnableOpcodeOptimizations { + return + } + if evm.interpreter == evm.optInterpreter { + return + } + evm.interpreter = evm.optInterpreter +} + +func (evm *EVM) UseBaseInterpreter() { + if evm.interpreter == evm.baseInterpreter { + return + } + evm.interpreter = evm.baseInterpreter +} + // SetPrecompiles sets the precompiled contracts for the EVM. // This method is only used through RPC calls. // It is not thread-safe. @@ -239,15 +267,34 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g if len(code) == 0 { ret, err = nil, nil // gas is unchanged } else { - // If the account has no code, we can abort here - // The depth-check is already done, and precompiles handled above - contract := GetContract(caller, addr, value, gas, evm.jumpDests) - defer ReturnContract(contract) - - contract.IsSystemCall = isSystemCall(caller) - contract.SetCallCode(evm.resolveCodeHash(addr), code) - ret, err = evm.interpreter.Run(contract, input, false) - gas = contract.Gas + if evm.Config.EnableOpcodeOptimizations { + // If the account has no code, we can abort here + // The depth-check is already done, and precompiles handled above + contract := GetContract(caller, addr, value, gas, evm.jumpDests) + defer ReturnContract(contract) + codeHash := evm.resolveCodeHash(addr) + contract.optimized, code = tryGetOptimizedCode(evm, codeHash, code) + if contract.optimized { + evm.UseOptInterpreter() + contract.codeBitmapFunc = codeBitmapWhitSI + } else { + evm.UseBaseInterpreter() + } + contract.IsSystemCall = isSystemCall(caller) + contract.SetCallCode(codeHash, code) + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas + } else { + // If the account has no code, we can abort here + // The depth-check is already done, and precompiles handled above + contract := GetContract(caller, addr, value, gas, evm.jumpDests) + defer ReturnContract(contract) + + contract.IsSystemCall = isSystemCall(caller) + contract.SetCallCode(evm.resolveCodeHash(addr), code) + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas + } } } // When an error was returned by the EVM or when setting the creation code @@ -301,14 +348,35 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { - // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. - contract := GetContract(caller, caller, value, gas, evm.jumpDests) - defer ReturnContract(contract) + if evm.Config.EnableOpcodeOptimizations { + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := GetContract(caller, caller, value, gas, evm.jumpDests) + defer ReturnContract(contract) + code := evm.resolveCode(addr) + codeHash := evm.resolveCodeHash(addr) + contract.optimized, code = tryGetOptimizedCode(evm, codeHash, code) + contract.SetCallCode(codeHash, code) + + if contract.optimized { + evm.UseOptInterpreter() + contract.codeBitmapFunc = codeBitmapWhitSI + } else { + evm.UseBaseInterpreter() + } - contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) - ret, err = evm.interpreter.Run(contract, input, false) - gas = contract.Gas + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas + } else { + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := GetContract(caller, caller, value, gas, evm.jumpDests) + defer ReturnContract(contract) + + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas + } } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) @@ -346,13 +414,31 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { - // Initialise a new contract and make initialise the delegate values - contract := GetContract(originCaller, caller, value, gas, evm.jumpDests) - defer ReturnContract(contract) + if evm.Config.EnableOpcodeOptimizations { + // Initialise a new contract and make initialise the delegate values + contract := GetContract(originCaller, caller, value, gas, evm.jumpDests) + defer ReturnContract(contract) + code := evm.resolveCode(addr) + codeHash := evm.resolveCodeHash(addr) + contract.optimized, code = tryGetOptimizedCode(evm, codeHash, code) + contract.SetCallCode(codeHash, code) + if contract.optimized { + evm.UseOptInterpreter() + contract.codeBitmapFunc = codeBitmapWhitSI + } else { + evm.UseBaseInterpreter() + } + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas + } else { + // Initialise a new contract and make initialise the delegate values + contract := GetContract(originCaller, caller, value, gas, evm.jumpDests) + defer ReturnContract(contract) - contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) - ret, err = evm.interpreter.Run(contract, input, false) - gas = contract.Gas + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) + ret, err = evm.interpreter.Run(contract, input, false) + gas = contract.Gas + } } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) @@ -398,17 +484,39 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { - // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. - contract := GetContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) - defer ReturnContract(contract) + if evm.Config.EnableOpcodeOptimizations { + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := GetContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) + defer ReturnContract(contract) + code := evm.resolveCode(addr) + codeHash := evm.resolveCodeHash(addr) + contract.optimized, code = tryGetOptimizedCode(evm, codeHash, code) + if contract.optimized { + evm.UseOptInterpreter() + contract.codeBitmapFunc = codeBitmapWhitSI + } else { + evm.UseBaseInterpreter() + } + contract.SetCallCode(codeHash, code) + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in Homestead this also counts for code storage gas errors. + ret, err = evm.interpreter.Run(contract, input, true) + gas = contract.Gas + } else { + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := GetContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) + defer ReturnContract(contract) - contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) - // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally - // when we're in Homestead this also counts for code storage gas errors. - ret, err = evm.interpreter.Run(contract, input, true) - gas = contract.Gas + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in Homestead this also counts for code storage gas errors. + ret, err = evm.interpreter.Run(contract, input, true) + gas = contract.Gas + } } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) @@ -423,6 +531,20 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b return ret, gas, err } +func tryGetOptimizedCode(evm *EVM, codeHash common.Hash, rawCode []byte) (bool, []byte) { + var code []byte + optimized := false + code = rawCode + optCode := compiler.LoadOptimizedCode(codeHash) + if len(optCode) != 0 { + code = optCode + optimized = true + } else { + compiler.GenOrLoadOptimizedCode(codeHash, rawCode) + } + return optimized, code +} + // create creates a new contract using code as deployment code. func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { if evm.Config.Tracer != nil { @@ -530,11 +652,25 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui // initNewContract runs a new contract's creation code, performs checks on the // resulting code that is to be deployed, and consumes necessary gas. func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]byte, error) { + // We don't optimize creation code as it run only once. + contract.optimized = false + if evm.Config.EnableOpcodeOptimizations { + compiler.DisableOptimization() + evm.UseBaseInterpreter() + } + ret, err := evm.interpreter.Run(contract, nil, false) if err != nil { return ret, err } + // After creation, retrieve to optimization + if evm.Config.EnableOpcodeOptimizations { + compiler.EnableOptimization() + evm.UseOptInterpreter() + contract.codeBitmapFunc = codeBitmapWhitSI + } + // Check whether the max code size has been exceeded, assign err if the case. if evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { return ret, ErrMaxCodeSizeExceeded diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 63bb6d2d51..decb40ef20 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -171,13 +171,15 @@ func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() + x, y := scope.Stack.pop2() + z := scope.Stack.peek() z.AddMod(&x, &y, z) return nil, nil } func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() + x, y := scope.Stack.pop2() + z := scope.Stack.peek() z.MulMod(&x, &y, z) return nil, nil } @@ -290,9 +292,8 @@ func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = scope.Stack.pop() - dataOffset = scope.Stack.pop() - length = scope.Stack.pop() + memOffset, dataOffset = scope.Stack.pop2() + length = scope.Stack.pop() ) dataOffset64, overflow := dataOffset.Uint64WithOverflow() if overflow { @@ -313,9 +314,8 @@ func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = scope.Stack.pop() - dataOffset = scope.Stack.pop() - length = scope.Stack.pop() + memOffset, dataOffset = scope.Stack.pop2() + length = scope.Stack.pop() ) offset64, overflow := dataOffset.Uint64WithOverflow() @@ -340,22 +340,28 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code)))) + code := scope.Contract.Code + if scope.Contract.optimized { + code = interpreter.evm.resolveCode(scope.Contract.address) + } + scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(code)))) return nil, nil } func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( - memOffset = scope.Stack.pop() - codeOffset = scope.Stack.pop() - length = scope.Stack.pop() + memOffset, codeOffset = scope.Stack.pop2() + length = scope.Stack.pop() ) uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { uint64CodeOffset = math.MaxUint64 } - - codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64()) + code := scope.Contract.Code + if scope.Contract.optimized { + code = interpreter.evm.resolveCode(scope.Contract.address) + } + codeCopy := getData(code, uint64CodeOffset, length.Uint64()) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) return nil, nil } @@ -499,13 +505,13 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by } func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - mStart, val := scope.Stack.pop(), scope.Stack.pop() + mStart, val := scope.Stack.pop2() scope.Memory.Set32(mStart.Uint64(), &val) return nil, nil } func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - off, val := scope.Stack.pop(), scope.Stack.pop() + off, val := scope.Stack.pop2() scope.Memory.store[off.Uint64()] = byte(val.Uint64()) return nil, nil } @@ -522,8 +528,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b if interpreter.readOnly { return nil, ErrWriteProtection } - loc := scope.Stack.pop() - val := scope.Stack.pop() + loc, val := scope.Stack.pop2() interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) return nil, nil } @@ -544,7 +549,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by if interpreter.evm.abort.Load() { return nil, errStopToken } - pos, cond := scope.Stack.pop(), scope.Stack.pop() + pos, cond := scope.Stack.pop2() if !cond.IsZero() { if !scope.Contract.validJumpdest(&pos) { return nil, ErrInvalidJump @@ -658,10 +663,10 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b return nil, ErrWriteProtection } var ( - value = scope.Stack.pop() - offset, size = scope.Stack.pop(), scope.Stack.pop() - input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) - gas = scope.Contract.Gas + value, offset = scope.Stack.pop2() + size = scope.Stack.pop() + input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) + gas = scope.Contract.Gas ) if interpreter.evm.chainRules.IsEIP150 { gas -= gas / 64 @@ -701,11 +706,10 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] return nil, ErrWriteProtection } var ( - endowment = scope.Stack.pop() - offset, size = scope.Stack.pop(), scope.Stack.pop() - salt = scope.Stack.pop() - input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) - gas = scope.Contract.Gas + endowment, offset = scope.Stack.pop2() + size, salt = scope.Stack.pop2() + input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) + gas = scope.Contract.Gas ) // Apply EIP150 @@ -860,14 +864,14 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - offset, size := scope.Stack.pop(), scope.Stack.pop() + offset, size := scope.Stack.pop2() ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) return ret, errStopToken } func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - offset, size := scope.Stack.pop(), scope.Stack.pop() + offset, size := scope.Stack.pop2() ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) interpreter.returnData = ret @@ -1010,3 +1014,620 @@ func makeDup(size int64) executionFunc { return nil, nil } } + +// fused instructions +// opAndSwap1PopSwap2Swap1 implements the fused instruction of And and `move to the stack bottom`. +func opAndSwap1PopSwap2Swap1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + a, b := scope.Stack.pop2() + c, d, e := scope.Stack.peek(), scope.Stack.Back(1), scope.Stack.Back(2) + r := a.And(&a, &b) + *c = *d + *d = *e + *e = *r + *pc += 4 + return nil, nil +} + +// opSwap2Swap1PopJump +func opSwap2Swap1PopJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + a, _ := scope.Stack.pop2() + c := scope.Stack.peek() + dest := *c + *c = a + if !scope.Contract.validJumpdest(&dest) { + return nil, ErrInvalidJump + } + *pc = dest.Uint64() - 1 // pc will be increased by the interpreter loop + return nil, nil +} + +// opSwap1PopSwap2Swap1 +func opSwap1PopSwap2Swap1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + a, _ := scope.Stack.pop2() + c, d := scope.Stack.pop(), scope.Stack.peek() + scope.Stack.push(d) + *d = a + scope.Stack.push(&c) + *pc += 3 // pc will be increased by the interpreter loop + return nil, nil +} + +// opPopSwap2Swap1Pop +func opPopSwap2Swap1Pop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + _, b := scope.Stack.pop2() + _, d := scope.Stack.pop(), scope.Stack.peek() + scope.Stack.push(d) + *d = b + *pc += 3 // pc will be increased by the interpreter loop + return nil, nil +} + +// opPush2Jump +func opPush2Jump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = len(scope.Contract.Code) + integer = new(uint256.Int) + pos = integer.Clear() + ) + + startMin := codeLen + if int(*pc+1) < startMin { + startMin = int(*pc + 1) + } + + endMin := codeLen + if startMin+2 < endMin { + endMin = startMin + 2 + } + + pos = integer.SetBytes(common.RightPadBytes( + scope.Contract.Code[startMin:endMin], 2)) + + if !scope.Contract.validJumpdest(pos) { + return nil, ErrInvalidJump + } + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop + return nil, nil +} + +// opPush2JumpI +func opPush2JumpI(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = len(scope.Contract.Code) + integer = new(uint256.Int) + pos = integer.Clear() + ) + + startMin := codeLen + if int(*pc+1) < startMin { + startMin = int(*pc + 1) + } + + endMin := codeLen + if startMin+2 < endMin { + endMin = startMin + 2 + } + + pos = integer.SetBytes(common.RightPadBytes( + scope.Contract.Code[startMin:endMin], 2)) + + cond := scope.Stack.pop() + if !cond.IsZero() { + if !scope.Contract.validJumpdest(pos) { + return nil, ErrInvalidJump + } + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop + } else { + *pc += 3 + } + return nil, nil +} + +// opPush1Push1 +func opPush1Push1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = uint64(len(scope.Contract.Code)) + a, b = new(uint256.Int), new(uint256.Int) + ) + *pc += 3 + if *pc < codeLen { + a = a.SetUint64(uint64(scope.Contract.Code[*pc-2])) + b = b.SetUint64(uint64(scope.Contract.Code[*pc])) + } + scope.Stack.push2(a, b) + return nil, nil +} + +// opPush1Add +func opPush1Add(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = uint64(len(scope.Contract.Code)) + a = new(uint256.Int) + ) + *pc += 1 + if *pc < codeLen { + a = a.SetUint64(uint64(scope.Contract.Code[*pc])) + } + b := scope.Stack.pop() + scope.Stack.push(b.Add(a, &b)) + *pc += 1 + return nil, nil +} + +// opPush1Shl +func opPush1Shl(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = uint64(len(scope.Contract.Code)) + shift = new(uint256.Int) + ) + *pc += 1 + if *pc < codeLen { + shift = shift.SetUint64(uint64(scope.Contract.Code[*pc])) + } else { + shift = shift.Clear() + } + value := scope.Stack.peek() + + if shift.LtUint64(256) { + value.Lsh(value, uint(shift.Uint64())) + } else { + value.Clear() + } + *pc += 1 + return nil, nil +} + +// opPush1Dup1 +func opPush1Dup1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = uint64(len(scope.Contract.Code)) + value = new(uint256.Int) + ) + *pc += 1 + if *pc < codeLen { + value = value.SetUint64(uint64(scope.Contract.Code[*pc])) + } + + scope.Stack.push2(value, value) + *pc += 1 + return nil, nil +} + +// opSwap1Pop +func opSwap1Pop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + a, b := scope.Stack.pop(), scope.Stack.peek() + *b = a + *pc += 1 + return nil, nil +} + +// opPopJump +func opPopJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + _, pos := scope.Stack.pop2() + if !scope.Contract.validJumpdest(&pos) { + return nil, ErrInvalidJump + } + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop + return nil, nil +} + +// opPop2 +func opPop2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + _, _ = scope.Stack.pop2() + *pc += 1 // pc will be increased by the interpreter loop + return nil, nil +} + +// opSwap2Swap1 +func opSwap2Swap1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + a, b, c := *scope.Stack.peek(), *scope.Stack.Back(1), *scope.Stack.Back(2) + // to b, c, a + *scope.Stack.peek() = b + *scope.Stack.Back(1) = c + *scope.Stack.Back(2) = a + *pc += 1 // pc will be increased by the interpreter loop + return nil, nil +} + +// opSwap2Pop +func opSwap2Pop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + // a,b,c -> b,a + a := scope.Stack.peek() + *scope.Stack.Back(2) = *a + scope.Stack.pop() + *pc += 1 // pc will be increased by the interpreter loop + return nil, nil +} + +// opDup2Lt +func opDup2LT(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x := scope.Stack.peek() + y := scope.Stack.Back(1) + if y.Lt(x) { + x.SetOne() + } else { + x.Clear() + } + *pc += 1 // pc will be increased by the interpreter loop + return nil, nil +} + +// opJumpIfZero +func opJumpIfZero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + value := scope.Stack.pop() + integer := new(uint256.Int) + pos := new(uint256.Int) + + if value.IsZero() { + codeLen := len(scope.Contract.Code) + *pc += 2 + startMin := codeLen + if int(*pc) < startMin { + startMin = int(*pc) + } + + endMin := codeLen + if startMin+2 < endMin { + endMin = startMin + 2 + } + + pos = integer.SetBytes(common.RightPadBytes( + scope.Contract.Code[startMin:endMin], 2)) + + if !scope.Contract.validJumpdest(pos) { + return nil, ErrInvalidJump + } + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop + } else { + *pc += 4 + } + return nil, nil +} + +// opNop has no behavior. +func opNop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if !scope.Contract.optimized { + return nil, ErrInvalidOptimizedCode + } + return nil, nil +} + +// opIsZeroPush2 is a super instruction +func opIsZeroPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x := scope.Stack.peek() + if x.IsZero() { + x.SetOne() + } else { + x.Clear() + } + + *pc += 1 + var ( + codeLen = uint64(len(scope.Contract.Code)) + integer = new(uint256.Int) + ) + if *pc+2 < codeLen { + scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3])) + } else if *pc+1 < codeLen { + scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)) + } else { + scope.Stack.push(integer.Clear()) + } + *pc += 2 + return nil, nil +} + +// DUP2 MSTORE PUSH1 ADD +func opDup2MStorePush1Add(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var mStart, val uint256.Int + + if scope.Stack.len() >= 2 { + mStart, val = scope.Stack.data[scope.Stack.len()-2], scope.Stack.pop() + } + scope.Memory.Set32(mStart.Uint64(), &val) + *pc += 3 + + var ( + codeLen = uint64(len(scope.Contract.Code)) + integer = new(uint256.Int) + ) + if *pc < codeLen { + integer.SetUint64(uint64(scope.Contract.Code[*pc])) + } + + x := scope.Stack.peek() + x.Add(x, integer) + + *pc += 1 + + return nil, nil +} + +// DUP1 PUSH4 EQ PUSH2 +func opDup1Push4EqPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.dup(1) + *pc += 1 + _, err := makePush(4, 4)(pc, interpreter, scope) + if err != nil { + return nil, err + } + + *pc += 1 + x, y := scope.Stack.pop(), scope.Stack.peek() + if x.Eq(y) { + y.SetOne() + } else { + y.Clear() + } + *pc += 1 + _, err = makePush(2, 2)(pc, interpreter, scope) + if err != nil { + return nil, err + } + + return nil, nil +} + +// PUSH1 CALLDATALOAD PUSH1 SHR DUP1 PUSH4 GT PUSH2 +func opPush1CalldataloadPush1ShrDup1Push4GtPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + codeLen := uint64(len(scope.Contract.Code)) + var x = new(uint256.Int) + *pc += 1 + if *pc < codeLen { + x = new(uint256.Int).SetUint64(uint64(scope.Contract.Code[*pc])) + } + + if offset, overflow := x.Uint64WithOverflow(); !overflow { + data := getData(scope.Contract.Input, offset, 32) + x.SetBytes(data) + } else { + x.Clear() + } + + *pc += 3 + var shift = new(uint256.Int) + if *pc < codeLen { + shift = new(uint256.Int).SetUint64(uint64(scope.Contract.Code[*pc])) + } + + if shift.LtUint64(256) { + x.Rsh(x, uint(shift.Uint64())) + } else { + x.Clear() + } + scope.Stack.push(x) + *pc += 2 // dup1 + scope.Stack.dup(1) + *pc += 1 // push4 + + var ( + start = min(codeLen, *pc+1) + end = min(codeLen, start+4) + ) + a := new(uint256.Int).SetBytes(scope.Contract.Code[start:end]) + + // Missing bytes: pushByteSize - len(pushData) + if missing := 4 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + *pc += 5 // GT + p := scope.Stack.peek() + if a.Gt(p) { + p.SetOne() + } else { + p.Clear() + } + *pc += 1 // push2 + _, err := makePush(2, 2)(pc, interpreter, scope) + if err != nil { + return nil, err + } + return nil, nil +} + +// PUSH1 PUSH1 PUSH1 SHL SUB +func opPush1Push1Push1SHLSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + codeLen := uint64(len(scope.Contract.Code)) + var integer1, integer2, integer3 = new(uint256.Int), new(uint256.Int), new(uint256.Int) + *pc += 1 + if *pc < codeLen { + integer1 = new(uint256.Int).SetUint64(uint64(scope.Contract.Code[*pc])) + } + + *pc += 2 + if *pc < codeLen { + integer2 = new(uint256.Int).SetUint64(uint64(scope.Contract.Code[*pc])) + } + + *pc += 2 + if *pc < codeLen { + integer3 = new(uint256.Int).SetUint64(uint64(scope.Contract.Code[*pc])) + } + + if integer3.LtUint64(256) { + integer2.Lsh(integer2, uint(integer3.Uint64())) + } else { + integer2.Clear() + } + + integer1.Sub(integer2, integer1) + scope.Stack.push(integer1) + *pc += 2 + + return nil, nil +} + +// AND DUP2 ADD SWAP1 DUP2 LT +func opAndDup2AddSwap1Dup2LT(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.Back(0) + y.And(&x, y) + z := scope.Stack.Back(1) + y.Add(z, y) + tmpy := *y + tmpz := *z + scope.Stack.swap1() + if tmpy.Lt(&tmpz) { + scope.Stack.Back(0).SetOne() + } else { + scope.Stack.Back(0).Clear() + } + + *pc += 5 + return nil, nil +} + +// SWAP1 PUSH1 DUP1 NOT SWAP2 ADD AND DUP2 ADD SWAP1 DUP2 LT +func opSwap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + codeLen := uint64(len(scope.Contract.Code)) + scope.Stack.swap1() + + *pc += 2 + var integer1 = new(uint256.Int) + if *pc < codeLen { + integer1.SetUint64(uint64(scope.Contract.Code[*pc])) + } + scope.Stack.push(integer1) + scope.Stack.dup(1) + x := scope.Stack.peek() + x.Not(x) + scope.Stack.swap2() + a, b := scope.Stack.pop(), scope.Stack.pop() + b.Add(&a, &b) + c := scope.Stack.peek() + c.And(&b, c) + e := scope.Stack.Back(1) + c.Add(e, c) + scope.Stack.swap1() + g, h := *c, scope.Stack.peek() + if g.Lt(h) { + h.SetOne() + } else { + h.Clear() + } + + *pc += 10 + return nil, nil +} + +// opDup3And +func opDup3And(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x := scope.Stack.data[scope.Stack.len()-3] + y := scope.Stack.peek() + y.And(&x, y) + *pc += 1 + return nil, nil +} + +// opSwap2Swap1Dup3SubSwap2Dup3GtPush2 +func opSwap2Swap1Dup3SubSwap2Dup3GtPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap2() + scope.Stack.swap1() + x := scope.Stack.data[scope.Stack.len()-3] + y := scope.Stack.peek() + y.Sub(&x, y) + scope.Stack.swap2() + x = scope.Stack.data[scope.Stack.len()-3] + y = scope.Stack.peek() + if x.Gt(y) { + y.SetOne() + } else { + y.Clear() + } + *pc += 7 + var ( + codeLen = uint64(len(scope.Contract.Code)) + integer = new(uint256.Int) + ) + if *pc+2 < codeLen { + scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3])) + } else if *pc+1 < codeLen { + scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)) + } else { + scope.Stack.push(integer.Clear()) + } + *pc += 2 + return nil, nil +} + +func opSwap1Dup2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap1() + scope.Stack.dup(2) + *pc += 1 + return nil, nil +} + +func opSHRSHRDup1MulDup1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + shift, value := scope.Stack.pop(), scope.Stack.pop() + if shift.LtUint64(256) { + value.Rsh(&value, uint(shift.Uint64())) + } else { + value.Clear() + } + + value2 := scope.Stack.peek() + if value.LtUint64(256) { + value2.Rsh(value2, uint(value.Uint64())) + } else { + value2.Clear() + } + + value3 := *value2 + value2.Mul(value2, &value3) + scope.Stack.dup(1) + *pc += 4 + return nil, nil +} + +func opSwap3PopPopPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap3() + scope.Stack.pop2() + scope.Stack.pop() + *pc += 3 + return nil, nil +} + +func opSubSLTIsZeroPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x, y := scope.Stack.pop(), scope.Stack.pop() + y.Sub(&x, &y) + z := scope.Stack.peek() + if y.Slt(z) { + z.SetOne() + } else { + z.Clear() + } + + if z.IsZero() { + z.SetOne() + } else { + z.Clear() + } + *pc += 3 + var ( + codeLen = uint64(len(scope.Contract.Code)) + integer = new(uint256.Int) + ) + if *pc+2 < codeLen { + scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3])) + } else if *pc+1 < codeLen { + scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)) + } else { + scope.Stack.push(integer.Clear()) + } + *pc += 2 + return nil, nil +} + +func opDup11MulDup3SubMulDup1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + x := scope.Stack.data[scope.Stack.len()-11] + y := scope.Stack.pop() + y.Mul(&x, &y) + + x = scope.Stack.data[scope.Stack.len()-2] + y.Sub(&x, &y) + + z := scope.Stack.peek() + z.Mul(&y, z) + scope.Stack.dup(1) + *pc += 5 + return nil, nil +} diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 0d3451a1bb..905dfda8e7 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" + "github.com/stretchr/testify/require" ) type TwoOperandTestcase struct { @@ -974,3 +975,770 @@ func TestPush(t *testing.T) { } } } + +func TestOpPush1Push1(t *testing.T) { + code := []byte{0x60, 0x0a, 0x60, 0x0b} + + pc1 := uint64(0) + stack1 := new(Stack) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true}, + Stack: stack1, + } + interpreter1 := &EVMInterpreter{} + + _, err := opPush1Push1(&pc1, interpreter1, scope1) + require.NoError(t, err) + + stack1Top := stack1.pop() + stack1Second := stack1.pop() + + pc2 := uint64(0) + stack2 := new(Stack) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code}, + Stack: stack2, + } + interpreter2 := &EVMInterpreter{} + + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + + stack2Top := stack2.pop() + stack2Second := stack2.pop() + + require.True(t, stack1Top.Eq(&stack2Top)) + require.True(t, stack1Second.Eq(&stack2Second)) + require.Equal(t, pc1, pc2) +} + +func TestOpIsZeroPush2(t *testing.T) { + code := []byte{0x15, 0x61, 0x0a, 0x0b} + + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true}, + Stack: stack1, + } + interpreter1 := &EVMInterpreter{} + + _, err := opIsZeroPush2(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code}, + Stack: stack2, + } + interpreter2 := &EVMInterpreter{} + + _, err = opIszero(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush2(&pc2, interpreter2, scope2) + require.NoError(t, err) + + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) +} + +func TestOpPop2(t *testing.T) { + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{optimized: true}, + Stack: stack1, + } + interpreter1 := &EVMInterpreter{} + + _, err := opPop2(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{}, + Stack: stack2, + } + interpreter2 := &EVMInterpreter{} + + _, err = opPop(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPop(&pc2, interpreter2, scope2) + require.NoError(t, err) + + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) +} + +func TestOpDup2MStorePush1Add(t *testing.T) { + var err error + code := []byte{0x81, 0x52, 0x60, 0x0a, 0x1} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opDup2MStorePush1Add(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = makeDup(2)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opMstore(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opAdd(&pc2, interpreter2, scope2) + require.NoError(t, err) + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpDup1Push4EqPush2(t *testing.T) { + var err error + code := []byte{0x80, 0x63, 0x04, 0x05, 0x06, 0x07, 0x14, 0x61, 0x08, 0x09} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opDup1Push4EqPush2(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = makeDup(1)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makePush(4, 4)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opEq(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush2(&pc2, interpreter2, scope2) + require.NoError(t, err) + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpPush1Push1Push1SHLSub(t *testing.T) { + var err error + code := []byte{0x60, 0x01, 0x60, 0x05, 0x60, 0x07, 0x1b, 0x3} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opPush1Push1Push1SHLSub(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSHL(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSub(&pc2, interpreter2, scope2) + require.NoError(t, err) + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpAndDup2AddSwap1Dup2LT(t *testing.T) { + var err error + code := []byte{0x16, 0x81, 0x1, 0x90, 0x81, 0x10} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opAndDup2AddSwap1Dup2LT(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = opAnd(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(2)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opAdd(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSwap1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(2)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opLt(&pc2, interpreter2, scope2) + require.NoError(t, err) + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpSwap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT(t *testing.T) { + var err error + code := []byte{0x90, 0x60, 0x1, 0x80, 0x19, 0x91, 0x1, 0x16, 0x81, 0x1, 0x90, 0x81, 0x10} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opSwap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = opSwap1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(1)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opNot(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSwap2(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opAdd(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opAnd(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(2)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opAdd(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSwap1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(2)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opLt(&pc2, interpreter2, scope2) + require.NoError(t, err) + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpPush1CalldataloadPush1ShrDup1Push4GtPush2(t *testing.T) { + var err error + code := []byte{0x60, 0x10, 0x35, 0x60, 0x11, 0x1c, 0x80, 0x63, 0x12, 0x13, 0x14, 0x15, 0x11, 0x61, 0x16, 0x17} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opPush1CalldataloadPush1ShrDup1Push4GtPush2(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opCallDataLoad(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSHR(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(1)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makePush(4, 4)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opGt(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makePush(2, 2)(&pc2, interpreter2, scope2) + require.NoError(t, err) + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpDup11MulDup3SubMulDup1(t *testing.T) { + var err error + code := []byte{0x8a, 0x2, 0x82, 0x3, 0x2, 0x80} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opDup11MulDup3SubMulDup1(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = makeDup(11)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opMul(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(3)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSub(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opMul(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(1)(&pc2, interpreter2, scope2) + require.NoError(t, err) + + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpSubSLTIsZeroPush2(t *testing.T) { + var err error + code := []byte{0x3, 0x12, 0x15, 0x61, 0x2, 0x80} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opSubSLTIsZeroPush2(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = opSub(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSlt(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opIszero(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush2(&pc2, interpreter2, scope2) + require.NoError(t, err) + + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpSHRSHRDup1MulDup1(t *testing.T) { + var err error + code := []byte{0x1c, 0x1c, 0x80, 0x2, 0x80} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opSHRSHRDup1MulDup1(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = opSHR(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSHR(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(1)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opMul(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(1)(&pc2, interpreter2, scope2) + require.NoError(t, err) + + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} + +func TestOpSwap2Swap1Dup3SubSwap2Dup3GtPush2(t *testing.T) { + var err error + code := []byte{0x91, 0x90, 0x82, 0x3, 0x91, 0x82, 0x11, 0x61, 0x1, 0x2} + pc1 := uint64(0) + stack1 := new(Stack) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(1)) + stack1.push(new(uint256.Int).SetUint64(2)) + stack1.push(new(uint256.Int).SetUint64(3)) + stack1.push(new(uint256.Int).SetUint64(3)) + scope1 := &ScopeContext{ + Contract: &Contract{Code: code, optimized: true, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack1, + Memory: NewMemory(), + } + scope1.Memory.Resize(34) + interpreter1 := &EVMInterpreter{} + + _, err = opSwap2Swap1Dup3SubSwap2Dup3GtPush2(&pc1, interpreter1, scope1) + require.NoError(t, err) + + pc2 := uint64(0) + stack2 := new(Stack) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(1)) + stack2.push(new(uint256.Int).SetUint64(2)) + stack2.push(new(uint256.Int).SetUint64(3)) + stack2.push(new(uint256.Int).SetUint64(3)) + scope2 := &ScopeContext{ + Contract: &Contract{Code: code, Input: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}}, + Stack: stack2, + Memory: NewMemory(), + } + scope2.Memory.Resize(34) + interpreter2 := &EVMInterpreter{} + + _, err = opSwap2(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSwap1(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(3)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSub(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opSwap2(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = makeDup(3)(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opGt(&pc2, interpreter2, scope2) + require.NoError(t, err) + pc2++ + _, err = opPush2(&pc2, interpreter2, scope2) + require.NoError(t, err) + + require.Equal(t, stack1.len(), stack2.len()) + for stack1.len() != 0 { + require.Equal(t, stack1.pop(), stack2.pop()) + } + require.Equal(t, pc1, pc2) + require.Equal(t, scope2.Memory.Data(), scope1.Memory.Data()) +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index b44411551c..1a434a363d 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -29,10 +29,11 @@ import ( // Config are the configuration options for the Interpreter type Config struct { - Tracer *tracing.Hooks - NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) - EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - ExtraEips []int // Additional EIPS that are to be enabled + Tracer *tracing.Hooks + NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) + EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages + ExtraEips []int // Additional EIPS that are to be enabled + EnableOpcodeOptimizations bool // Enable opcode optimization StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose) } @@ -154,6 +155,11 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter { return &EVMInterpreter{evm: evm, table: table, hasher: crypto.NewKeccakState()} } +func (in *EVMInterpreter) CopyAndInstallSuperInstruction() { + table := copyJumpTable(in.table) + in.table = createOptimizedOpcodeTable(table) +} + // Run loops and evaluates the contract's code with the given input data and returns // the return byte-slice and an error if one occurred. // @@ -258,6 +264,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } // for tracing: this gas consumption event is emitted below in the debug section. if contract.Gas < cost { + if seq, isSuper := DecomposeSuperInstruction(op); isSuper { + err = in.tryFallbackForSuperInstruction(&pc, seq, contract, stack, mem, callContext) + return nil, err + } return nil, ErrOutOfGas } else { contract.Gas -= cost @@ -285,6 +295,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // cost is explicitly set so that the capture state defer method can get the proper cost // cost is explicitly set so that the capture state defer method can get the proper cost var dynamicCost uint64 + memLastGasCost := mem.lastGasCost dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) cost += dynamicCost // for tracing if err != nil { @@ -292,6 +303,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } // for tracing: this gas consumption event is emitted below in the debug section. if contract.Gas < dynamicCost { + contract.Gas += operation.constantGas // restore deducted constant gas first + mem.lastGasCost = memLastGasCost + if seq, isSuper := DecomposeSuperInstruction(op); isSuper { + err = in.tryFallbackForSuperInstruction(&pc, seq, contract, stack, mem, callContext) + return nil, err + } return nil, ErrOutOfGas } else { contract.Gas -= dynamicCost diff --git a/core/vm/interpreter_si.go b/core/vm/interpreter_si.go new file mode 100644 index 0000000000..bc753e3a95 --- /dev/null +++ b/core/vm/interpreter_si.go @@ -0,0 +1,118 @@ +package vm + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/log" +) + +// superInstructionMap maps super-instruction opcodes to the slice of ordinary opcodes +// they were fused from. The mapping comes from the fusion patterns implemented in +// core/opcodeCompiler/compiler/opCodeProcessor.go (applyFusionPatterns). When that file +// is updated with new fusion rules, this map should be kept in sync. +var superInstructionMap = map[OpCode][]OpCode{ + AndSwap1PopSwap2Swap1: {AND, SWAP1, POP, SWAP2, SWAP1}, + Swap2Swap1PopJump: {SWAP2, SWAP1, POP, JUMP}, + Swap1PopSwap2Swap1: {SWAP1, POP, SWAP2, SWAP1}, + PopSwap2Swap1Pop: {POP, SWAP2, SWAP1, POP}, + Push2Jump: {PUSH2, JUMP}, // PUSH2 embeds 2-byte immediate + Push2JumpI: {PUSH2, JUMPI}, + Push1Push1: {PUSH1, PUSH1}, + Push1Add: {PUSH1, ADD}, + Push1Shl: {PUSH1, SHL}, + Push1Dup1: {PUSH1, DUP1}, + Swap1Pop: {SWAP1, POP}, + PopJump: {POP, JUMP}, + Pop2: {POP, POP}, + Swap2Swap1: {SWAP2, SWAP1}, + Swap2Pop: {SWAP2, POP}, + Dup2LT: {DUP2, LT}, + JumpIfZero: {ISZERO, PUSH2, JUMPI}, // PUSH2 embeds 2-byte immediate + IsZeroPush2: {ISZERO, PUSH2}, + Dup2MStorePush1Add: {DUP2, MSTORE, PUSH1, ADD}, + Dup1Push4EqPush2: {DUP1, PUSH4, EQ, PUSH2}, + Push1CalldataloadPush1ShrDup1Push4GtPush2: {PUSH1, CALLDATALOAD, PUSH1, SHR, DUP1, PUSH4, GT, PUSH2}, + Push1Push1Push1SHLSub: {PUSH1, PUSH1, PUSH1, SHL, SUB}, + AndDup2AddSwap1Dup2LT: {AND, DUP2, ADD, SWAP1, DUP2, LT}, + Swap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT: {SWAP1, PUSH1, DUP1, NOT, SWAP2, ADD, AND, DUP2, ADD, SWAP1, DUP2, LT}, + Dup3And: {DUP3, AND}, + Swap2Swap1Dup3SubSwap2Dup3GtPush2: {SWAP2, SWAP1, DUP3, SUB, SWAP2, DUP3, GT, PUSH2}, + Swap1Dup2: {SWAP1, DUP2}, + SHRSHRDup1MulDup1: {SHR, SHR, DUP1, MUL, DUP1}, + Swap3PopPopPop: {SWAP3, POP, POP, POP}, + SubSLTIsZeroPush2: {SUB, SLT, ISZERO, PUSH2}, + Dup11MulDup3SubMulDup1: {DUP11, MUL, DUP3, SUB, MUL, DUP1}, +} + +// DecomposeSuperInstruction returns the underlying opcode sequence of a fused +// super-instruction. If the provided opcode is not a super-instruction (or is +// unknown), the second return value will be false. +func DecomposeSuperInstruction(op OpCode) ([]OpCode, bool) { + seq, ok := superInstructionMap[op] + return seq, ok +} + +// DecomposeSuperInstructionByName works like DecomposeSuperInstruction but takes the +// textual name (case-insensitive) instead of the opcode constant. +func DecomposeSuperInstructionByName(name string) ([]OpCode, bool) { + op := StringToOp(strings.ToUpper(name)) + return DecomposeSuperInstruction(op) +} + +func (in *EVMInterpreter) executeSingleOpcode(pc *uint64, op OpCode, contract *Contract, stack *Stack, mem *Memory, callCtx *ScopeContext) error { + operation := in.table[op] + if operation == nil { + return fmt.Errorf("unknown opcode %02x", op) + } + + // -------- check static gas -------- + if contract.Gas < operation.constantGas { + return ErrOutOfGas + } + contract.Gas -= operation.constantGas + + // -------- check dynamic gas -------- + var memorySize uint64 + if operation.memorySize != nil { + memSize, overflow := operation.memorySize(stack) + if overflow { + return ErrGasUintOverflow + } + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return ErrGasUintOverflow + } + } + + if operation.dynamicGas != nil { + dyn, err := operation.dynamicGas(in.evm, contract, stack, mem, memorySize) + if err != nil { + return err + } + if contract.Gas < dyn { + return ErrOutOfGas + } + contract.Gas -= dyn + } + + if memorySize > 0 { + mem.Resize(memorySize) + } + + // -------- execute -------- + _, err := operation.execute(pc, in, callCtx) + return err +} + +// tryFallbackForSuperInstruction break down superinstruction to normal opcode and execute in sequence, until gas deplete or succeed +// return nil show successful execution of si or OOG in the middle (and updated pc/gas), shall continue in main loop +func (in *EVMInterpreter) tryFallbackForSuperInstruction(pc *uint64, seq []OpCode, contract *Contract, stack *Stack, mem *Memory, callCtx *ScopeContext) error { + for _, sub := range seq { + if err := in.executeSingleOpcode(pc, sub, contract, stack, mem, callCtx); err != nil { + log.Error("[FALLBACK-EXEC]", "op", sub.String(), "err", err, "gasLeft", contract.Gas) + return err // OutOfGas or other errors, will let upper level handle + } + } + return nil +} diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 6bf4ac5476..5c1d366386 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -1096,3 +1096,233 @@ func copyJumpTable(source *JumpTable) *JumpTable { } return &dest } + +func createOptimizedOpcodeTable(tbl *JumpTable) *JumpTable { + // super instructions + tbl[Nop] = &operation{ + execute: opNop, + constantGas: 0, + minStack: minStack(0, 0), + maxStack: maxStack(0, 0), + } + + tbl[AndSwap1PopSwap2Swap1] = &operation{ + execute: opAndSwap1PopSwap2Swap1, + constantGas: 4*GasFastestStep + GasQuickStep, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + } + + tbl[Swap2Swap1PopJump] = &operation{ + execute: opSwap2Swap1PopJump, + constantGas: 2*GasFastestStep + GasQuickStep + GasMidStep, + minStack: minStack(3, 3), + maxStack: maxStack(3, 3), + } + + tbl[Swap1PopSwap2Swap1] = &operation{ + execute: opSwap1PopSwap2Swap1, + constantGas: 3*GasFastestStep + GasQuickStep, + minStack: minStack(4, 4), + maxStack: maxStack(4, 4), + } + + tbl[PopSwap2Swap1Pop] = &operation{ + execute: opPopSwap2Swap1Pop, + constantGas: 2*GasFastestStep + 2*GasQuickStep, + minStack: minStack(4, 4), + maxStack: maxStack(4, 4), + } + + tbl[Push2Jump] = &operation{ + execute: opPush2Jump, + constantGas: GasFastestStep + GasMidStep, + minStack: minStack(0, 0), + maxStack: maxStack(0, 0), + } + + tbl[Push2JumpI] = &operation{ + execute: opPush2JumpI, + constantGas: GasFastestStep + GasSlowStep, + minStack: minStack(1, 0), + maxStack: maxStack(1, 0), + } + + tbl[Push1Push1] = &operation{ + execute: opPush1Push1, + constantGas: 2 * GasFastestStep, + minStack: minStack(0, 2), + maxStack: maxStack(0, 2), + } + + tbl[Push1Add] = &operation{ + execute: opPush1Add, + constantGas: 2 * GasFastestStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + + tbl[Push1Shl] = &operation{ + execute: opPush1Shl, + constantGas: 2 * GasFastestStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + + tbl[Push1Dup1] = &operation{ + execute: opPush1Dup1, + constantGas: 2 * GasFastestStep, + minStack: minStack(0, 2), + maxStack: maxStack(0, 2), + } + + tbl[Swap1Pop] = &operation{ + execute: opSwap1Pop, + constantGas: GasFastestStep + GasQuickStep, + minStack: minStack(1, 0), + maxStack: maxStack(1, 0), + } + + tbl[PopJump] = &operation{ + execute: opPopJump, + constantGas: GasQuickStep + GasMidStep, + minStack: minStack(1, 0), + maxStack: maxStack(1, 0), + } + + tbl[Pop2] = &operation{ + execute: opPop2, + constantGas: 2 * GasQuickStep, + minStack: minStack(2, 0), + maxStack: maxStack(2, 0), + } + + tbl[Swap2Swap1] = &operation{ + execute: opSwap2Swap1, + constantGas: 2 * GasFastestStep, + minStack: minStack(3, 3), + maxStack: maxStack(3, 3), + } + + tbl[Swap2Pop] = &operation{ + execute: opSwap2Pop, + constantGas: GasFastestStep + GasQuickStep, + minStack: minStack(3, 2), + maxStack: maxStack(3, 2), + } + + tbl[Dup2LT] = &operation{ + execute: opDup2LT, + constantGas: 2 * GasFastestStep, + minStack: minStack(2, 2), + maxStack: maxStack(2, 2), + } + + tbl[JumpIfZero] = &operation{ + execute: opJumpIfZero, + constantGas: 2*GasFastestStep + GasSlowStep, + minStack: minStack(1, 0), + maxStack: maxStack(1, 0), + } + + tbl[IsZeroPush2] = &operation{ + execute: opIsZeroPush2, + constantGas: GasFastestStep + GasFastestStep, + minStack: minStack(1, 1), + maxStack: maxStack(1, 1), + } + + tbl[Dup2MStorePush1Add] = &operation{ + execute: opDup2MStorePush1Add, + constantGas: 4 * GasFastestStep, + dynamicGas: gasMStore, + minStack: minStack(2, 1), + maxStack: maxStack(2, 1), + memorySize: memoryDup2MStorePush1Add, + } + + tbl[Dup1Push4EqPush2] = &operation{ + execute: opDup1Push4EqPush2, + constantGas: GasFastestStep + GasFastestStep + GasFastestStep + GasFastestStep, + minStack: minStack(1, 2), + maxStack: maxStack(1, 2), + } + + tbl[Push1CalldataloadPush1ShrDup1Push4GtPush2] = &operation{ + execute: opPush1CalldataloadPush1ShrDup1Push4GtPush2, + constantGas: 8 * GasFastestStep, + minStack: minStack(0, 5), + maxStack: maxStack(0, 5), + } + + tbl[Push1Push1Push1SHLSub] = &operation{ + execute: opPush1Push1Push1SHLSub, + constantGas: 5 * GasFastestStep, + minStack: minStack(0, 3), + maxStack: maxStack(0, 3), + } + + tbl[AndDup2AddSwap1Dup2LT] = &operation{ + execute: opAndDup2AddSwap1Dup2LT, + constantGas: 6 * GasFastestStep, + minStack: minStack(0, 3), + maxStack: maxStack(0, 3), + } + + tbl[Swap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT] = &operation{ + execute: opSwap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT, + constantGas: 12 * GasFastestStep, + minStack: minStack(1, 4), + maxStack: maxStack(1, 4), + } + + tbl[Dup3And] = &operation{ + execute: opDup3And, + constantGas: 2 * GasFastestStep, + minStack: minStack(3, 0), + maxStack: maxStack(0, 0), + } + + tbl[Swap2Swap1Dup3SubSwap2Dup3GtPush2] = &operation{ + execute: opSwap2Swap1Dup3SubSwap2Dup3GtPush2, + constantGas: 8 * GasFastestStep, + minStack: minStack(3, 0), + maxStack: maxStack(0, 2), + } + + tbl[Swap1Dup2] = &operation{ + execute: opSwap1Dup2, + constantGas: 2 * GasFastestStep, + minStack: minStack(2, 0), + maxStack: maxStack(0, 1), + } + + tbl[SHRSHRDup1MulDup1] = &operation{ + execute: opSHRSHRDup1MulDup1, + constantGas: 4*GasFastestStep + GasFastStep, + minStack: minStack(3, 0), + maxStack: maxStack(0, 1), + } + + tbl[Swap3PopPopPop] = &operation{ + execute: opSwap3PopPopPop, + constantGas: GasFastestStep + 3*GasQuickStep, + minStack: minStack(4, 0), + maxStack: maxStack(0, 0), + } + + tbl[SubSLTIsZeroPush2] = &operation{ + execute: opSubSLTIsZeroPush2, + constantGas: 4 * GasFastestStep, + minStack: minStack(3, 0), + maxStack: maxStack(0, 1), + } + + tbl[Dup11MulDup3SubMulDup1] = &operation{ + execute: opDup11MulDup3SubMulDup1, + constantGas: 4*GasFastestStep + 2*GasFastStep, + minStack: minStack(11, 0), + maxStack: maxStack(0, 1), + } + return tbl +} diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 47e4885e73..e1b66c6048 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -111,3 +111,7 @@ func memoryRevert(stack *Stack) (uint64, bool) { func memoryLog(stack *Stack) (uint64, bool) { return calcMemSize64(stack.Back(0), stack.Back(1)) } + +func memoryDup2MStorePush1Add(stack *Stack) (uint64, bool) { + return calcMemSize64WithUint(stack.Back(1), 32) +} diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 0820b20fb1..896f0e5a40 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -211,6 +211,48 @@ const ( LOG4 ) +// 0xb0 range - customized instructions. +const ( + Nop OpCode = 0xb0 + iota + AndSwap1PopSwap2Swap1 + Swap2Swap1PopJump + Swap1PopSwap2Swap1 + PopSwap2Swap1Pop + Push2Jump + Push2JumpI + Push1Push1 + Push1Add + Push1Shl + Push1Dup1 + Swap1Pop + PopJump + Pop2 + Swap2Swap1 + Swap2Pop + Dup2LT + JumpIfZero // 0xc1 + + IsZeroPush2 + Dup2MStorePush1Add + Dup1Push4EqPush2 + Push1CalldataloadPush1ShrDup1Push4GtPush2 + Push1Push1Push1SHLSub + AndDup2AddSwap1Dup2LT + Swap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT + Dup3And + Swap2Swap1Dup3SubSwap2Dup3GtPush2 + Swap1Dup2 + SHRSHRDup1MulDup1 + Swap3PopPopPop + SubSLTIsZeroPush2 + Dup11MulDup3SubMulDup1 // 0xcf + //Swap3Swap2PopPop + //Push8Dup3GtORPush2 + //Dup2AddSwap1Push2Swap2Swap1Push2 + //Swap4Swap3PopPopPop + //Swap2AddAndDup2Add +) + // 0xd0 range - eof operations. const ( DATALOAD OpCode = 0xd0 @@ -414,6 +456,40 @@ var opCodeToString = [256]string{ LOG3: "LOG3", LOG4: "LOG4", + // 0xb0 range. + Nop: "NOP", + AndSwap1PopSwap2Swap1: "ANDSWAP1POPSWAP2SWAP1", + Swap2Swap1PopJump: "SWAP2SWAP1POPJUMP", + Swap1PopSwap2Swap1: "SWAP1POPSWAP2SWAP1", + PopSwap2Swap1Pop: "POPSWAP2SWAP1POP", + Push2Jump: "PUSH2JUMP", + Push2JumpI: "PUSH2JUMPI", + Push1Push1: "PUSH1PUSH1", + Push1Add: "PUSH1ADD", + Push1Shl: "PUSH1SHL", + Push1Dup1: "PUSH1DUP1", + Swap1Pop: "SWAP1POP", + PopJump: "POPJUMP", + Pop2: "POP2", + Swap2Swap1: "SWAP2SWAP1", + Swap2Pop: "SWAP2POP", + Dup2LT: "DUP2LT", + JumpIfZero: "JUMPIFZERO", + IsZeroPush2: "ISZEROPUSH2", + Dup2MStorePush1Add: "DUP2MSTOREPUSH1ADD", + Dup1Push4EqPush2: "DUP1PUSH4EQPUSH2", + Push1CalldataloadPush1ShrDup1Push4GtPush2: "PUSH1CALLDATALOADPUSH1SHRDUP1PUSH4GTPUSH2", + Push1Push1Push1SHLSub: "PUSH1PUSH1PUSH1SHLSUB", + AndDup2AddSwap1Dup2LT: "ANDDUP2ADDSWAP1DUP2LT", + Swap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT: "SWAP1PUSH1DUP1NOTSWAP2ADDANDDUP2ADDSWAP1DUP2LT", + Dup3And: "DUP3AND", + Swap2Swap1Dup3SubSwap2Dup3GtPush2: "SWAP2SWAP1DUP3SUBSWAP2DUP3GTPUSH2", + Swap1Dup2: "SWAP1DUP2", + SHRSHRDup1MulDup1: "SHRSHRDUP1MULDUP1", + Swap3PopPopPop: "SWAP3POPPOPPOP", + SubSLTIsZeroPush2: "SUBSLTISZEROPUSH2", + Dup11MulDup3SubMulDup1: "DUP11MULDUP3SUBMULDUP1", + // 0xd range - eof ops. DATALOAD: "DATALOAD", DATALOADN: "DATALOADN", @@ -628,6 +704,39 @@ var stringToOp = map[string]OpCode{ "REVERT": REVERT, "INVALID": INVALID, "SELFDESTRUCT": SELFDESTRUCT, + + "NOP": Nop, + "ANDSWAP1POPSWAP2SWAP1": AndSwap1PopSwap2Swap1, + "SWAP2SWAP1POPJUMP": Swap2Swap1PopJump, + "SWAP1POPSWAP2SWAP1": Swap1PopSwap2Swap1, + "POPSWAP2SWAP1POP": PopSwap2Swap1Pop, + "PUSH2JUMP": Push2Jump, + "PUSH2JUMPI": Push2JumpI, + "PUSH1PUSH1": Push1Push1, + "PUSH1ADD": Push1Add, + "PUSH1SHL": Push1Shl, + "PUSH1DUP1": Push1Dup1, + "SWAP1POP": Swap1Pop, + "POPJUMP": PopJump, + "POP2": Pop2, + "SWAP2SWAP1": Swap2Swap1, + "SWAP2POP": Swap2Pop, + "DUP2LT": Dup2LT, + "JUMPIFZERO": JumpIfZero, + "ISZEROPUSH2": IsZeroPush2, + "DUP2MSTOREPUSH1ADD": Dup2MStorePush1Add, + "DUP1PUSH4EQPUSH2": Dup1Push4EqPush2, + "PUSH1CALLDATALOADPUSH1SHRDUP1PUSH4GTPUSH2": Push1CalldataloadPush1ShrDup1Push4GtPush2, + "PUSH1PUSH1PUSH1SHLSUB": Push1Push1Push1SHLSub, + "ANDDUP2ADDSWAP1DUP2LT": AndDup2AddSwap1Dup2LT, + "SWAP1PUSH1DUP1NOTSWAP2ADDANDDUP2ADDSWAP1DUP2LT": Swap1Push1Dup1NotSwap2AddAndDup2AddSwap1Dup2LT, + "DUP3AND": Dup3And, + "SWAP2SWAP1DUP3SUBSWAP2DUP3GTPUSH2": Swap2Swap1Dup3SubSwap2Dup3GtPush2, + "SWAP1DUP2": Swap1Dup2, + "SHRSHRDUP1MULDUP1": SHRSHRDup1MulDup1, + "SWAP3POPPOPPOP": Swap3PopPopPop, + "SUBSLTISZEROPUSH2": SubSLTIsZeroPush2, + "DUP11MULDUP3SUBMULDUP1": Dup11MulDup3SubMulDup1, } // StringToOp finds the opcode whose name is stored in `str`. diff --git a/core/vm/runtime/runtime_example_test.go b/core/vm/runtime/runtime_example_test.go index b7d0ddc384..5988698c04 100644 --- a/core/vm/runtime/runtime_example_test.go +++ b/core/vm/runtime/runtime_example_test.go @@ -20,11 +20,13 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/runtime" ) func ExampleExecute() { - ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil) + ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, + &runtime.Config{EVMConfig: vm.Config{EnableOpcodeOptimizations: false}}) if err != nil { fmt.Println(err) } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index d75a5b0459..f2c80a8f1d 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -24,11 +24,13 @@ import ( "strconv" "strings" "testing" + "time" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/opcodeCompiler/compiler" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" @@ -676,6 +678,7 @@ func TestColdAccountAccessCost(t *testing.T) { step++ }, }, + EnableOpcodeOptimizations: false, }, }) if want := tc.want; have != want { @@ -787,7 +790,185 @@ func TestRuntimeJSTracer(t *testing.T) { GasLimit: 1000000, State: statedb, EVMConfig: vm.Config{ - Tracer: tracer.Hooks, + Tracer: tracer.Hooks, + EnableOpcodeOptimizations: false, + }}) + if err != nil { + t.Fatal("didn't expect error", err) + } + res, err := tracer.GetResult() + if err != nil { + t.Fatal(err) + } + if have, want := string(res), tc.results[i]; have != want { + t.Errorf("wrong result for tracer %d testcase %d, have \n%v\nwant\n%v\n", i, j, have, want) + } + } + } +} + +func TestRuntimeJSTracerWithOpcodeOptimizer(t *testing.T) { + jsTracers := []string{ + `{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, steps:0, + step: function() { this.steps++}, + fault: function() {}, + result: function() { + return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",") + }, + enter: function(frame) { + this.enters++; + this.enterGas = frame.getGas(); + }, + exit: function(res) { + this.exits++; + this.gasUsed = res.getGasUsed(); + }}`, + `{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, steps:0, + fault: function() {}, + result: function() { + return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",") + }, + enter: function(frame) { + this.enters++; + this.enterGas = frame.getGas(); + }, + exit: function(res) { + this.exits++; + this.gasUsed = res.getGasUsed(); + }}`} + tests := []struct { + code []byte + // One result per tracer + results []string + }{ + { + // CREATE + code: []byte{ + // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) + byte(vm.PUSH5), + // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + // length, offset, value + byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, + byte(vm.CREATE), + byte(vm.POP), + }, + results: []string{`"1,1,952853,6,11"`, `"1,1,952853,6,0"`}, + }, + { + // CREATE2 + code: []byte{ + // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes) + byte(vm.PUSH5), + // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps) + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN), + byte(vm.PUSH1), 0, + byte(vm.MSTORE), + // salt, length, offset, value + byte(vm.PUSH1), 1, byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0, + byte(vm.CREATE2), + byte(vm.POP), + }, + results: []string{`"1,1,952844,6,11"`, `"1,1,952844,6,0"`}, + }, + { + // CALL + code: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, + byte(vm.PUSH1), 0, // value + byte(vm.PUSH1), 0xbb, //address + byte(vm.GAS), // gas + byte(vm.CALL), + byte(vm.POP), + }, + results: []string{`"1,1,981796,6,9"`, `"1,1,981796,6,0"`}, + }, + { + // CALLCODE + code: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, + byte(vm.PUSH1), 0, // value + byte(vm.PUSH1), 0xcc, //address + byte(vm.GAS), // gas + byte(vm.CALLCODE), + byte(vm.POP), + }, + results: []string{`"1,1,981796,6,9"`, `"1,1,981796,6,0"`}, + }, + { + // STATICCALL + code: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, + byte(vm.PUSH1), 0xdd, //address + byte(vm.GAS), // gas + byte(vm.STATICCALL), + byte(vm.POP), + }, + results: []string{`"1,1,981799,6,9"`, `"1,1,981799,6,0"`}, + }, + { + // DELEGATECALL + code: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, + byte(vm.PUSH1), 0xee, //address + byte(vm.GAS), // gas + byte(vm.DELEGATECALL), + byte(vm.POP), + }, + results: []string{`"1,1,981799,6,9"`, `"1,1,981799,6,0"`}, + }, + { + // CALL self-destructing contract + code: []byte{ + // outsize, outoffset, insize, inoffset + byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, + byte(vm.PUSH1), 0, // value + byte(vm.PUSH1), 0xff, //address + byte(vm.GAS), // gas + byte(vm.CALL), + byte(vm.POP), + }, + results: []string{`"2,2,0,5003,9"`, `"2,2,0,5003,0"`}, + }, + } + calleeCode := []byte{ + byte(vm.PUSH1), 0, + byte(vm.PUSH1), 0, + byte(vm.RETURN), + } + depressedCode := []byte{ + byte(vm.PUSH1), 0xaa, + byte(vm.SELFDESTRUCT), + } + main := common.HexToAddress("0xaa") + compiler.EnableOptimization() + for i, jsTracer := range jsTracers { + for j, tc := range tests { + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + statedb.SetCode(main, tc.code) + statedb.SetCode(common.HexToAddress("0xbb"), calleeCode) + statedb.SetCode(common.HexToAddress("0xcc"), calleeCode) + statedb.SetCode(common.HexToAddress("0xdd"), calleeCode) + statedb.SetCode(common.HexToAddress("0xee"), calleeCode) + statedb.SetCode(common.HexToAddress("0xff"), depressedCode) + /* wait for optimized code to be generated */ + time.Sleep(time.Second) + tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig) + if err != nil { + t.Fatal(err) + } + _, _, err = Call(main, nil, &Config{ + GasLimit: 1000000, + State: statedb, + EVMConfig: vm.Config{ + Tracer: tracer.Hooks, + EnableOpcodeOptimizations: true, }}) if err != nil { t.Fatal("didn't expect error", err) diff --git a/core/vm/stack.go b/core/vm/stack.go index 879dc9aa6d..41965b1478 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -54,12 +54,25 @@ func (st *Stack) push(d *uint256.Int) { st.data = append(st.data, *d) } +// Push two values to the top of the stack +func (st *Stack) push2(a *uint256.Int, b *uint256.Int) { + // NOTE push limit (1024) is checked in baseCheck + st.data = append(st.data, *a, *b) +} + +// pop the top most elem in stack or cache func (st *Stack) pop() (ret uint256.Int) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] return } +func (st *Stack) pop2() (ret uint256.Int, ret1 uint256.Int) { + ret, ret1 = st.data[len(st.data)-1], st.data[len(st.data)-2] + st.data = st.data[:len(st.data)-2] + return +} + func (st *Stack) len() int { return len(st.data) } diff --git a/core/vote/vote_manager.go b/core/vote/vote_manager.go index cf5da97502..c3db8f0881 100644 --- a/core/vote/vote_manager.go +++ b/core/vote/vote_manager.go @@ -22,7 +22,7 @@ import ( // the new node may cast votes for the same block height that the previous node already voted on. // To avoid double-voting issues, the node should wait for a few blocks // before participating in voting after it starts mining. -const blocksNumberSinceMining = 20 +const blocksNumberSinceMining = 40 var diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures var votesManagerCounter = metrics.NewRegisteredCounter("votesManager/local", nil) @@ -158,10 +158,10 @@ func (voteManager *VoteManager) loop() { if err != nil { log.Debug("failed to get BlockInterval when voting") } - nextBlockMinedTime := time.UnixMilli(int64((curHead.MilliTimestamp() + blockInterval))) - timeForBroadcast := 50 * time.Millisecond // enough to broadcast a vote - if time.Now().Add(timeForBroadcast).After(nextBlockMinedTime) { - log.Warn("too late to vote", "Head.Time(Second)", curHead.Time, "Now(Millisecond)", time.Now().UnixMilli()) + voteAssembledTime := time.UnixMilli(int64((curHead.MilliTimestamp() + p.GetAncestorGenerationDepth(curHead)*blockInterval))) + timeForBroadcast := 50 * time.Millisecond // enough to broadcast a vote in the same region + if time.Now().Add(timeForBroadcast).After(voteAssembledTime) { + log.Warn("too late to vote", "Head.Time(Millisecond)", curHead.MilliTimestamp(), "Now(Millisecond)", time.Now().UnixMilli()) continue } } diff --git a/core/vote/vote_pool.go b/core/vote/vote_pool.go index ee4dace62f..c7a0cf7b3d 100644 --- a/core/vote/vote_pool.go +++ b/core/vote/vote_pool.go @@ -345,7 +345,7 @@ func (pool *VotePool) GetVotes() []*types.VoteEnvelope { return votesRes } -func (pool *VotePool) FetchVoteByBlockHash(blockHash common.Hash) []*types.VoteEnvelope { +func (pool *VotePool) FetchVotesByBlockHash(blockHash common.Hash) []*types.VoteEnvelope { pool.mu.RLock() defer pool.mu.RUnlock() if _, ok := pool.curVotes[blockHash]; ok { diff --git a/core/vote/vote_pool_test.go b/core/vote/vote_pool_test.go index d31db709b3..c6ee87501e 100644 --- a/core/vote/vote_pool_test.go +++ b/core/vote/vote_pool_test.go @@ -284,7 +284,7 @@ func testVotePool(t *testing.T, isValidRules bool) { // Test future votes scenario: votes number within latestBlockHeader ~ latestBlockHeader + 11 futureVote := &types.VoteEnvelope{ Data: &types.VoteData{ - TargetNumber: 294, + TargetNumber: 314, }, } if err := voteManager.signer.SignVote(futureVote); err != nil { @@ -304,7 +304,7 @@ func testVotePool(t *testing.T, isValidRules bool) { // Test duplicate vote case, shouldn'd be put into vote pool duplicateVote := &types.VoteEnvelope{ Data: &types.VoteData{ - TargetNumber: 294, + TargetNumber: 314, }, } if err := voteManager.signer.SignVote(duplicateVote); err != nil { @@ -333,14 +333,14 @@ func testVotePool(t *testing.T, isValidRules bool) { t.Fatalf("put vote failed") } - // Test transfer votes from future to cur, latest block header is #308 after the following generation - // For the above BlockNumber 279, it did not have blockHash, should be assigned as well below. - curNumber := 288 + // Test transfer votes from future to cur, latest block header is #328 after the following generation + // For the above BlockNumber 314, it did not have blockHash, should be assigned as well below. + curNumber := 308 var futureBlockHash common.Hash for i := 0; i < 20; i++ { bs, _ = core.GenerateChain(params.TestChainConfig, bs[len(bs)-1], ethash.NewFaker(), db, 1, nil) curNumber += 1 - if curNumber == 294 { + if curNumber == 314 { futureBlockHash = bs[0].Hash() futureVotesMap := votePool.futureVotes voteBox := futureVotesMap[common.Hash{}] diff --git a/eth/backend.go b/eth/backend.go index ac1dd0fa22..63f1fa9121 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -322,24 +322,33 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { journalFilePath := stack.ResolvePath(path) + "/" + JournalFileName var ( options = &core.BlockChainConfig{ - TrieCleanLimit: config.TrieCleanCache, - NoPrefetch: config.NoPrefetch, - TrieDirtyLimit: config.TrieDirtyCache, - ArchiveMode: config.NoPruning, - TrieTimeLimit: config.TrieTimeout, - NoTries: noTries, - SnapshotLimit: config.SnapshotCache, - TriesInMemory: config.TriesInMemory, - Preimages: config.Preimages, - StateHistory: config.StateHistory, - StateScheme: config.StateScheme, - PathSyncFlush: config.PathSyncFlush, - JournalFilePath: journalFilePath, - JournalFile: config.JournalFileEnabled, - ChainHistoryMode: config.HistoryMode, - TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)), + TrieCleanLimit: config.TrieCleanCache, + NoPrefetch: config.NoPrefetch, + EnableBAL: config.EnableBAL, + TrieDirtyLimit: config.TrieDirtyCache, + ArchiveMode: config.NoPruning, + TrieTimeLimit: config.TrieTimeout, + NoTries: noTries, + SnapshotLimit: config.SnapshotCache, + TriesInMemory: config.TriesInMemory, + Preimages: config.Preimages, + StateHistory: config.StateHistory, + StateScheme: config.StateScheme, + PathSyncFlush: config.PathSyncFlush, + JournalFilePath: journalFilePath, + JournalFile: config.JournalFileEnabled, + EnableIncr: config.EnableIncrSnapshots, + IncrHistoryPath: config.IncrSnapshotPath, + IncrHistory: config.IncrSnapshotBlockInterval, + IncrStateBuffer: config.IncrSnapshotStateBuffer, + IncrKeptBlocks: config.IncrSnapshotKeptBlocks, + UseRemoteIncrSnapshot: config.UseRemoteIncrSnapshot, + RemoteIncrURL: config.RemoteIncrSnapshotURL, + ChainHistoryMode: config.HistoryMode, + TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)), VmConfig: vm.Config{ - EnablePreimageRecording: config.EnablePreimageRecording, + EnablePreimageRecording: config.EnablePreimageRecording, + EnableOpcodeOptimizations: config.EnableOpcodeOptimizing, }, } ) @@ -430,8 +439,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { RequiredBlocks: config.RequiredBlocks, DirectBroadcast: config.DirectBroadcast, EnableEVNFeatures: stack.Config().EnableEVNFeatures, + EnableBAL: config.EnableBAL, EVNNodeIdsWhitelist: stack.Config().P2P.EVNNodeIdsWhitelist, ProxyedValidatorAddresses: stack.Config().P2P.ProxyedValidatorAddresses, + ProxyedNodeIds: stack.Config().P2P.ProxyedNodeIds, DisablePeerTxBroadcast: config.DisablePeerTxBroadcast, PeerSet: newPeerSet(), EnableQuickBlockFetching: stack.Config().EnableQuickBlockFetching, diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 968241c792..3b3faa18ea 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -51,29 +51,30 @@ var FullNodeGPO = gasprice.Config{ // Defaults contains default settings for use on the BSC main net. var Defaults = Config{ - HistoryMode: history.KeepAll, - SyncMode: SnapSync, - NetworkId: 0, // enable auto configuration of networkID == chainID - TxLookupLimit: 2350000, - TransactionHistory: 2350000, - BlockHistory: 0, - StateHistory: params.FullImmutabilityThreshold, - DatabaseCache: 512, - TrieCleanCache: 154, - TrieDirtyCache: 256, - TrieTimeout: 10 * time.Minute, - TriesInMemory: 128, - TriesVerifyMode: core.LocalVerify, - SnapshotCache: 102, - FilterLogCacheSize: 32, - Miner: minerconfig.DefaultConfig, - TxPool: legacypool.DefaultConfig, - BlobPool: blobpool.DefaultConfig, - RPCGasCap: 50000000, - RPCEVMTimeout: 5 * time.Second, - GPO: FullNodeGPO, - RPCTxFeeCap: 1, // 1 ether - BlobExtraReserve: params.DefaultExtraReserveForBlobRequests, // Extra reserve threshold for blob, blob never expires when -1 is set, default 28800 + HistoryMode: history.KeepAll, + SyncMode: SnapSync, + NetworkId: 0, // enable auto configuration of networkID == chainID + TxLookupLimit: 2350000, + TransactionHistory: 2350000, + BlockHistory: 0, + StateHistory: params.FullImmutabilityThreshold, + DatabaseCache: 512, + TrieCleanCache: 154, + TrieDirtyCache: 256, + TrieTimeout: 10 * time.Minute, + TriesInMemory: 128, + TriesVerifyMode: core.LocalVerify, + SnapshotCache: 102, + FilterLogCacheSize: 32, + Miner: minerconfig.DefaultConfig, + TxPool: legacypool.DefaultConfig, + BlobPool: blobpool.DefaultConfig, + RPCGasCap: 50000000, + RPCEVMTimeout: 5 * time.Second, + GPO: FullNodeGPO, + RPCTxFeeCap: 1, // 1 ether + BlobExtraReserve: params.DefaultExtraReserveForBlobRequests, // Extra reserve threshold for blob, blob never expires when -1 is set, default 28800 + EnableOpcodeOptimizing: false, } //go:generate go run github.com/fjl/gencodec -type Config -formats toml -out gen_config.go @@ -111,6 +112,7 @@ type Config struct { NoPruning bool // Whether to disable pruning and flush everything to disk NoPrefetch bool // Whether to disable prefetching and only load state on demand + EnableBAL bool DirectBroadcast bool DisableSnapProtocol bool // Whether disable snap protocol RangeLimit bool @@ -213,6 +215,17 @@ type Config struct { // blob setting BlobExtraReserve uint64 + + //opcode optimization setting + EnableOpcodeOptimizing bool + // incremental snapshot config + EnableIncrSnapshots bool + IncrSnapshotPath string + IncrSnapshotBlockInterval uint64 + IncrSnapshotStateBuffer uint64 + IncrSnapshotKeptBlocks uint64 + UseRemoteIncrSnapshot bool + RemoteIncrSnapshotURL string } // CreateConsensusEngine creates a consensus engine for the given chain config. diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 038fec8aec..5a2c8315e3 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -18,64 +18,73 @@ import ( // MarshalTOML marshals as TOML. func (c Config) MarshalTOML() (interface{}, error) { type Config struct { - Genesis *core.Genesis `toml:",omitempty"` - NetworkId uint64 - SyncMode SyncMode - DisablePeerTxBroadcast bool - EVNNodeIDsToAdd []enode.ID - EVNNodeIDsToRemove []enode.ID - HistoryMode history.HistoryMode - EthDiscoveryURLs []string - SnapDiscoveryURLs []string - BscDiscoveryURLs []string - NoPruning bool - NoPrefetch bool - DirectBroadcast bool - DisableSnapProtocol bool - RangeLimit bool - TxLookupLimit uint64 `toml:",omitempty"` - TransactionHistory uint64 `toml:",omitempty"` - BlockHistory uint64 `toml:",omitempty"` - LogHistory uint64 `toml:",omitempty"` - LogNoHistory bool `toml:",omitempty"` - LogExportCheckpoints string - StateHistory uint64 `toml:",omitempty"` - StateScheme string `toml:",omitempty"` - PathSyncFlush bool `toml:",omitempty"` - JournalFileEnabled bool - DisableTxIndexer bool `toml:",omitempty"` - RequiredBlocks map[uint64]common.Hash `toml:"-"` - SkipBcVersionCheck bool `toml:"-"` - DatabaseHandles int `toml:"-"` - DatabaseCache int - DatabaseFreezer string - DatabaseEra string - PruneAncientData bool - TrieCleanCache int - TrieDirtyCache int - TrieTimeout time.Duration - SnapshotCache int - TriesInMemory uint64 - TriesVerifyMode core.VerifyMode - Preimages bool - FilterLogCacheSize int - Miner minerconfig.Config - TxPool legacypool.Config - BlobPool blobpool.Config - GPO gasprice.Config - EnablePreimageRecording bool - VMTrace string - VMTraceJsonConfig string - RPCGasCap uint64 - RPCEVMTimeout time.Duration - RPCTxFeeCap float64 - OverridePassedForkTime *uint64 `toml:",omitempty"` - OverrideLorentz *uint64 `toml:",omitempty"` - OverrideMaxwell *uint64 `toml:",omitempty"` - OverrideFermi *uint64 `toml:",omitempty"` - OverrideOsaka *uint64 `toml:",omitempty"` - OverrideVerkle *uint64 `toml:",omitempty"` - BlobExtraReserve uint64 + Genesis *core.Genesis `toml:",omitempty"` + NetworkId uint64 + SyncMode SyncMode + DisablePeerTxBroadcast bool + EVNNodeIDsToAdd []enode.ID + EVNNodeIDsToRemove []enode.ID + HistoryMode history.HistoryMode + EthDiscoveryURLs []string + SnapDiscoveryURLs []string + BscDiscoveryURLs []string + NoPruning bool + NoPrefetch bool + EnableBAL bool + DirectBroadcast bool + DisableSnapProtocol bool + RangeLimit bool + TxLookupLimit uint64 `toml:",omitempty"` + TransactionHistory uint64 `toml:",omitempty"` + BlockHistory uint64 `toml:",omitempty"` + LogHistory uint64 `toml:",omitempty"` + LogNoHistory bool `toml:",omitempty"` + LogExportCheckpoints string + StateHistory uint64 `toml:",omitempty"` + StateScheme string `toml:",omitempty"` + PathSyncFlush bool `toml:",omitempty"` + JournalFileEnabled bool + DisableTxIndexer bool `toml:",omitempty"` + RequiredBlocks map[uint64]common.Hash `toml:"-"` + SkipBcVersionCheck bool `toml:"-"` + DatabaseHandles int `toml:"-"` + DatabaseCache int + DatabaseFreezer string + DatabaseEra string + PruneAncientData bool + TrieCleanCache int + TrieDirtyCache int + TrieTimeout time.Duration + SnapshotCache int + TriesInMemory uint64 + TriesVerifyMode core.VerifyMode + Preimages bool + FilterLogCacheSize int + Miner minerconfig.Config + TxPool legacypool.Config + BlobPool blobpool.Config + GPO gasprice.Config + EnablePreimageRecording bool + VMTrace string + VMTraceJsonConfig string + RPCGasCap uint64 + RPCEVMTimeout time.Duration + RPCTxFeeCap float64 + OverridePassedForkTime *uint64 `toml:",omitempty"` + OverrideLorentz *uint64 `toml:",omitempty"` + OverrideMaxwell *uint64 `toml:",omitempty"` + OverrideFermi *uint64 `toml:",omitempty"` + OverrideOsaka *uint64 `toml:",omitempty"` + OverrideVerkle *uint64 `toml:",omitempty"` + BlobExtraReserve uint64 + EnableIncrSnapshots bool + IncrSnapshotPath string + IncrSnapshotBlockInterval uint64 + IncrSnapshotStateBuffer uint64 + IncrSnapshotKeptBlocks uint64 + UseRemoteIncrSnapshot bool + RemoteIncrSnapshotURL string + EnableOpcodeOptimizing bool } var enc Config enc.Genesis = c.Genesis @@ -90,6 +99,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.BscDiscoveryURLs = c.BscDiscoveryURLs enc.NoPruning = c.NoPruning enc.NoPrefetch = c.NoPrefetch + enc.EnableBAL = c.EnableBAL enc.DirectBroadcast = c.DirectBroadcast enc.DisableSnapProtocol = c.DisableSnapProtocol enc.RangeLimit = c.RangeLimit @@ -136,70 +146,87 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.OverrideOsaka = c.OverrideOsaka enc.OverrideVerkle = c.OverrideVerkle enc.BlobExtraReserve = c.BlobExtraReserve + enc.EnableOpcodeOptimizing = c.EnableOpcodeOptimizing + enc.EnableIncrSnapshots = c.EnableIncrSnapshots + enc.IncrSnapshotPath = c.IncrSnapshotPath + enc.IncrSnapshotBlockInterval = c.IncrSnapshotBlockInterval + enc.IncrSnapshotStateBuffer = c.IncrSnapshotStateBuffer + enc.IncrSnapshotKeptBlocks = c.IncrSnapshotKeptBlocks + enc.UseRemoteIncrSnapshot = c.UseRemoteIncrSnapshot + enc.RemoteIncrSnapshotURL = c.RemoteIncrSnapshotURL return &enc, nil } // UnmarshalTOML unmarshals from TOML. func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { type Config struct { - Genesis *core.Genesis `toml:",omitempty"` - NetworkId *uint64 - SyncMode *SyncMode - DisablePeerTxBroadcast *bool - EVNNodeIDsToAdd []enode.ID - EVNNodeIDsToRemove []enode.ID - HistoryMode *history.HistoryMode - EthDiscoveryURLs []string - SnapDiscoveryURLs []string - BscDiscoveryURLs []string - NoPruning *bool - NoPrefetch *bool - DirectBroadcast *bool - DisableSnapProtocol *bool - RangeLimit *bool - TxLookupLimit *uint64 `toml:",omitempty"` - TransactionHistory *uint64 `toml:",omitempty"` - BlockHistory *uint64 `toml:",omitempty"` - LogHistory *uint64 `toml:",omitempty"` - LogNoHistory *bool `toml:",omitempty"` - LogExportCheckpoints *string - StateHistory *uint64 `toml:",omitempty"` - StateScheme *string `toml:",omitempty"` - PathSyncFlush *bool `toml:",omitempty"` - JournalFileEnabled *bool - DisableTxIndexer *bool `toml:",omitempty"` - RequiredBlocks map[uint64]common.Hash `toml:"-"` - SkipBcVersionCheck *bool `toml:"-"` - DatabaseHandles *int `toml:"-"` - DatabaseCache *int - DatabaseFreezer *string - DatabaseEra *string - PruneAncientData *bool - TrieCleanCache *int - TrieDirtyCache *int - TrieTimeout *time.Duration - SnapshotCache *int - TriesInMemory *uint64 - TriesVerifyMode *core.VerifyMode - Preimages *bool - FilterLogCacheSize *int - Miner *minerconfig.Config - TxPool *legacypool.Config - BlobPool *blobpool.Config - GPO *gasprice.Config - EnablePreimageRecording *bool - VMTrace *string - VMTraceJsonConfig *string - RPCGasCap *uint64 - RPCEVMTimeout *time.Duration - RPCTxFeeCap *float64 - OverridePassedForkTime *uint64 `toml:",omitempty"` - OverrideLorentz *uint64 `toml:",omitempty"` - OverrideMaxwell *uint64 `toml:",omitempty"` - OverrideFermi *uint64 `toml:",omitempty"` - OverrideOsaka *uint64 `toml:",omitempty"` - OverrideVerkle *uint64 `toml:",omitempty"` - BlobExtraReserve *uint64 + Genesis *core.Genesis `toml:",omitempty"` + NetworkId *uint64 + SyncMode *SyncMode + DisablePeerTxBroadcast *bool + EVNNodeIDsToAdd []enode.ID + EVNNodeIDsToRemove []enode.ID + HistoryMode *history.HistoryMode + EthDiscoveryURLs []string + SnapDiscoveryURLs []string + BscDiscoveryURLs []string + NoPruning *bool + NoPrefetch *bool + EnableBAL *bool + DirectBroadcast *bool + DisableSnapProtocol *bool + RangeLimit *bool + TxLookupLimit *uint64 `toml:",omitempty"` + TransactionHistory *uint64 `toml:",omitempty"` + BlockHistory *uint64 `toml:",omitempty"` + LogHistory *uint64 `toml:",omitempty"` + LogNoHistory *bool `toml:",omitempty"` + LogExportCheckpoints *string + StateHistory *uint64 `toml:",omitempty"` + StateScheme *string `toml:",omitempty"` + PathSyncFlush *bool `toml:",omitempty"` + JournalFileEnabled *bool + DisableTxIndexer *bool `toml:",omitempty"` + RequiredBlocks map[uint64]common.Hash `toml:"-"` + SkipBcVersionCheck *bool `toml:"-"` + DatabaseHandles *int `toml:"-"` + DatabaseCache *int + DatabaseFreezer *string + DatabaseEra *string + PruneAncientData *bool + TrieCleanCache *int + TrieDirtyCache *int + TrieTimeout *time.Duration + SnapshotCache *int + TriesInMemory *uint64 + TriesVerifyMode *core.VerifyMode + Preimages *bool + FilterLogCacheSize *int + Miner *minerconfig.Config + TxPool *legacypool.Config + BlobPool *blobpool.Config + GPO *gasprice.Config + EnablePreimageRecording *bool + VMTrace *string + VMTraceJsonConfig *string + RPCGasCap *uint64 + RPCEVMTimeout *time.Duration + RPCTxFeeCap *float64 + OverridePassedForkTime *uint64 `toml:",omitempty"` + OverrideLorentz *uint64 `toml:",omitempty"` + OverrideMaxwell *uint64 `toml:",omitempty"` + OverrideFermi *uint64 `toml:",omitempty"` + OverrideOsaka *uint64 `toml:",omitempty"` + OverrideVerkle *uint64 `toml:",omitempty"` + BlobExtraReserve *uint64 + EnableIncrSnapshots *bool + IncrSnapshotPath *string + IncrSnapshotBlockInterval *uint64 + IncrSnapshotStateBuffer *uint64 + IncrSnapshotKeptBlocks *uint64 + UseRemoteIncrSnapshot *bool + RemoteIncrSnapshotURL *string + EnableOpcodeOptimizing *bool } var dec Config if err := unmarshal(&dec); err != nil { @@ -241,6 +268,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.NoPrefetch != nil { c.NoPrefetch = *dec.NoPrefetch } + if dec.EnableBAL != nil { + c.EnableBAL = *dec.EnableBAL + } if dec.DirectBroadcast != nil { c.DirectBroadcast = *dec.DirectBroadcast } @@ -379,5 +409,29 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.BlobExtraReserve != nil { c.BlobExtraReserve = *dec.BlobExtraReserve } + if dec.EnableOpcodeOptimizing != nil { + c.EnableOpcodeOptimizing = *dec.EnableOpcodeOptimizing + } + if dec.EnableIncrSnapshots != nil { + c.EnableIncrSnapshots = *dec.EnableIncrSnapshots + } + if dec.IncrSnapshotPath != nil { + c.IncrSnapshotPath = *dec.IncrSnapshotPath + } + if dec.IncrSnapshotBlockInterval != nil { + c.IncrSnapshotBlockInterval = *dec.IncrSnapshotBlockInterval + } + if dec.IncrSnapshotStateBuffer != nil { + c.IncrSnapshotStateBuffer = *dec.IncrSnapshotStateBuffer + } + if dec.IncrSnapshotKeptBlocks != nil { + c.IncrSnapshotKeptBlocks = *dec.IncrSnapshotKeptBlocks + } + if dec.UseRemoteIncrSnapshot != nil { + c.UseRemoteIncrSnapshot = *dec.UseRemoteIncrSnapshot + } + if dec.RemoteIncrSnapshotURL != nil { + c.RemoteIncrSnapshotURL = *dec.RemoteIncrSnapshotURL + } return nil } diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go index 2794726b57..ee909e9e66 100644 --- a/eth/fetcher/block_fetcher.go +++ b/eth/fetcher/block_fetcher.go @@ -889,7 +889,7 @@ func (f *BlockFetcher) importBlocks(op *blockOrHeaderInject) { hash := block.Hash() // Run the import on a new thread - log.Debug("Importing propagated block", "peer", peer, "number", block.Number(), "hash", hash) + log.Debug("Importing propagated block", "peer", peer, "number", block.Number(), "hash", hash, "balSize", block.BALSize()) go func() { // If the parent's unknown, abort insertion parent := f.getBlock(block.ParentHash()) diff --git a/eth/filters/api.go b/eth/filters/api.go index 3b3b87117c..b577d58d14 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -44,6 +44,7 @@ var ( errPendingLogsUnsupported = errors.New("pending logs are not supported") errExceedMaxTopics = errors.New("exceed max topics") errExceedMaxAddresses = errors.New("exceed max addresses") + errExceedMaxTxHashes = errors.New("exceed max number of transaction hashes allowed per transactionReceipts subscription") ) const ( @@ -53,6 +54,8 @@ const ( maxTopics = 4 // The maximum number of allowed topics within a topic criteria maxSubTopics = 1000 + // The maximum number of transaction hash criteria allowed in a single subscription + maxTxHashes = 200 ) // filter is a helper struct that holds meta information over the filter type @@ -415,6 +418,71 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc return rpcSub, nil } +// TransactionReceiptsFilter defines criteria for transaction receipts subscription. +// If TransactionHashes is nil or empty, receipts for all transactions included in new blocks will be delivered. +// Otherwise, only receipts for the specified transactions will be delivered. +type TransactionReceiptsFilter struct { + TransactionHashes []common.Hash `json:"transactionHashes,omitempty"` +} + +// TransactionReceipts creates a subscription that fires transaction receipts when transactions are included in blocks. +func (api *FilterAPI) TransactionReceipts(ctx context.Context, filter *TransactionReceiptsFilter) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + // Validate transaction hashes limit + if filter != nil && len(filter.TransactionHashes) > maxTxHashes { + return nil, errExceedMaxTxHashes + } + + var ( + rpcSub = notifier.CreateSubscription() + matchedReceipts = make(chan []*ReceiptWithTx) + txHashes []common.Hash + ) + + if filter != nil { + txHashes = filter.TransactionHashes + } + + receiptsSub := api.events.SubscribeTransactionReceipts(txHashes, matchedReceipts) + + gopool.Submit(func() { + defer receiptsSub.Unsubscribe() + + signer := types.LatestSigner(api.sys.backend.ChainConfig()) + + for { + select { + case receiptsWithTxs := <-matchedReceipts: + if len(receiptsWithTxs) > 0 { + // Convert to the same format as eth_getTransactionReceipt + marshaledReceipts := make([]map[string]interface{}, len(receiptsWithTxs)) + for i, receiptWithTx := range receiptsWithTxs { + marshaledReceipts[i] = ethapi.MarshalReceipt( + receiptWithTx.Receipt, + receiptWithTx.Receipt.BlockHash, + receiptWithTx.Receipt.BlockNumber.Uint64(), + signer, + receiptWithTx.Transaction, + int(receiptWithTx.Receipt.TransactionIndex), + ) + } + + // Send a batch of tx receipts in one notification + notifier.Notify(rpcSub.ID, marshaledReceipts) + } + case <-rpcSub.Err(): + return + } + } + }) + + return rpcSub, nil +} + // FilterCriteria represents a request to create a new filter. // Same as ethereum.FilterQuery but with UnmarshalJSON() method. type FilterCriteria ethereum.FilterQuery diff --git a/eth/filters/filter.go b/eth/filters/filter.go index a67aa30be3..b5c6486cb9 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -26,6 +26,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/history" "github.com/ethereum/go-ethereum/core/types" @@ -559,3 +560,70 @@ func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]commo } return true } + +// ReceiptWithTx contains a receipt and its corresponding transaction +type ReceiptWithTx struct { + Receipt *types.Receipt + Transaction *types.Transaction +} + +// filterReceipts returns the receipts matching the given criteria +// In addition to returning receipts, it also returns the corresponding transactions. +// This is because receipts only contain low-level data, while user-facing data +// may require additional information from the Transaction. +func filterReceipts(txHashes []common.Hash, ev core.ChainEvent) []*ReceiptWithTx { + var ret []*ReceiptWithTx + + receipts := ev.Receipts + txs := ev.Transactions + + if len(receipts) != len(txs) { + log.Warn("Receipts and transactions length mismatch", "receipts", len(receipts), "transactions", len(txs)) + return ret + } + + if len(txHashes) == 0 { + // No filter, send all receipts with their transactions. + ret = make([]*ReceiptWithTx, len(receipts)) + for i, receipt := range receipts { + ret[i] = &ReceiptWithTx{ + Receipt: receipt, + Transaction: txs[i], + } + } + } else if len(txHashes) == 1 { + // Filter by single transaction hash. + // This is a common case, so we distinguish it from filtering by multiple tx hashes and made a small optimization. + for i, receipt := range receipts { + if receipt.TxHash == txHashes[0] { + ret = append(ret, &ReceiptWithTx{ + Receipt: receipt, + Transaction: txs[i], + }) + break + } + } + } else { + // Filter by multiple transaction hashes. + txHashMap := make(map[common.Hash]bool, len(txHashes)) + for _, hash := range txHashes { + txHashMap[hash] = true + } + + for i, receipt := range receipts { + if txHashMap[receipt.TxHash] { + ret = append(ret, &ReceiptWithTx{ + Receipt: receipt, + Transaction: txs[i], + }) + + // Early exit if all receipts are found + if len(ret) == len(txHashes) { + break + } + } + } + } + + return ret +} diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 0391fd7b17..41dd1fc867 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -163,6 +163,8 @@ const ( VotesSubscription // FinalizedHeadersSubscription queries hashes for finalized headers that are reached FinalizedHeadersSubscription + // TransactionReceiptsSubscription queries for transaction receipts when transactions are included in blocks + TransactionReceiptsSubscription // LastIndexSubscription keeps track of the last index LastIndexSubscription ) @@ -193,6 +195,8 @@ type subscription struct { txs chan []*types.Transaction headers chan *types.Header votes chan *types.VoteEnvelope + receipts chan []*ReceiptWithTx + txHashes []common.Hash // contains transaction hashes for transactionReceipts subscription filtering installed chan struct{} // closed when the filter is installed err chan error // closed when the filter is uninstalled } @@ -291,6 +295,7 @@ func (sub *Subscription) Unsubscribe() { case <-sub.f.txs: case <-sub.f.headers: case <-sub.f.votes: + case <-sub.f.receipts: } } @@ -370,6 +375,7 @@ func (es *EventSystem) subscribeLogs(crit ethereum.FilterQuery, logs chan []*typ txs: make(chan []*types.Transaction), headers: make(chan *types.Header), votes: make(chan *types.VoteEnvelope), + receipts: make(chan []*ReceiptWithTx), installed: make(chan struct{}), err: make(chan error), } @@ -387,6 +393,7 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti txs: make(chan []*types.Transaction), headers: headers, votes: make(chan *types.VoteEnvelope), + receipts: make(chan []*ReceiptWithTx), installed: make(chan struct{}), err: make(chan error), } @@ -404,6 +411,7 @@ func (es *EventSystem) SubscribeNewFinalizedHeaders(headers chan *types.Header) txs: make(chan []*types.Transaction), headers: headers, votes: make(chan *types.VoteEnvelope), + receipts: make(chan []*ReceiptWithTx), installed: make(chan struct{}), err: make(chan error), } @@ -421,6 +429,7 @@ func (es *EventSystem) SubscribePendingTxs(txs chan []*types.Transaction) *Subsc txs: txs, headers: make(chan *types.Header), votes: make(chan *types.VoteEnvelope), + receipts: make(chan []*ReceiptWithTx), installed: make(chan struct{}), err: make(chan error), } @@ -438,6 +447,26 @@ func (es *EventSystem) SubscribeNewVotes(votes chan *types.VoteEnvelope) *Subscr txs: make(chan []*types.Transaction), headers: make(chan *types.Header), votes: votes, + receipts: make(chan []*ReceiptWithTx), + installed: make(chan struct{}), + err: make(chan error), + } + return es.subscribe(sub) +} + +// SubscribeTransactionReceipts creates a subscription that writes transaction receipts for +// transactions when they are included in blocks. If txHashes is provided, only receipts +// for those specific transaction hashes will be delivered. +func (es *EventSystem) SubscribeTransactionReceipts(txHashes []common.Hash, receipts chan []*ReceiptWithTx) *Subscription { + sub := &subscription{ + id: rpc.NewID(), + typ: TransactionReceiptsSubscription, + created: time.Now(), + logs: make(chan []*types.Log), + txs: make(chan []*types.Transaction), + headers: make(chan *types.Header), + receipts: receipts, + txHashes: txHashes, installed: make(chan struct{}), err: make(chan error), } @@ -474,6 +503,14 @@ func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent) for _, f := range filters[BlocksSubscription] { f.headers <- ev.Header } + + // Handle transaction receipts subscriptions when a new block is added + for _, f := range filters[TransactionReceiptsSubscription] { + matchedReceipts := filterReceipts(f.txHashes, ev) + if len(matchedReceipts) > 0 { + f.receipts <- matchedReceipts + } + } } func (es *EventSystem) handleFinalizedHeaderEvent(filters filterIndex, ev core.FinalizedHeaderEvent) { diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 5289dec266..1ec4739979 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/filtermaps" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -770,3 +771,143 @@ func TestVoteSubscription(t *testing.T) { <-sub0.Err() } + +// TestTransactionReceiptsSubscription tests the transaction receipts subscription functionality +func TestTransactionReceiptsSubscription(t *testing.T) { + t.Parallel() + + const txNum = 5 + + // Setup test environment + var ( + db = rawdb.NewMemoryDatabase() + backend, sys = newTestFilterSystem(db, Config{}) + api = NewFilterAPI(sys, false) + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + signer = types.NewLondonSigner(big.NewInt(1)) + genesis = &core.Genesis{ + Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000000000000000)}}, // 1 ETH + Config: params.TestChainConfig, + BaseFee: big.NewInt(params.InitialBaseFee), + } + _, chain, _ = core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), 1, func(i int, gen *core.BlockGen) { + // Add transactions to the block + for j := 0; j < txNum; j++ { + toAddr := common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268") + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(j), + GasPrice: gen.BaseFee(), + Gas: 21000, + To: &toAddr, + Value: big.NewInt(1000), + Data: nil, + }), signer, key1) + gen.AddTx(tx) + } + }) + ) + + // Insert the blocks into the chain + blockchain, err := core.NewBlockChain(db, genesis, ethash.NewFaker(), nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + if n, err := blockchain.InsertChain(chain); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + + // Prepare test data + receipts := blockchain.GetReceiptsByHash(chain[0].Hash()) + if receipts == nil { + t.Fatalf("failed to get receipts") + } + + chainEvent := core.ChainEvent{ + Header: chain[0].Header(), + Receipts: receipts, + Transactions: chain[0].Transactions(), + } + + txHashes := make([]common.Hash, txNum) + for i := 0; i < txNum; i++ { + txHashes[i] = chain[0].Transactions()[i].Hash() + } + + testCases := []struct { + name string + filterTxHashes []common.Hash + expectedReceiptTxHashes []common.Hash + expectError bool + }{ + { + name: "no filter - should return all receipts", + filterTxHashes: nil, + expectedReceiptTxHashes: txHashes, + expectError: false, + }, + { + name: "single tx hash filter", + filterTxHashes: []common.Hash{txHashes[0]}, + expectedReceiptTxHashes: []common.Hash{txHashes[0]}, + expectError: false, + }, + { + name: "multiple tx hashes filter", + filterTxHashes: []common.Hash{txHashes[0], txHashes[1], txHashes[2]}, + expectedReceiptTxHashes: []common.Hash{txHashes[0], txHashes[1], txHashes[2]}, + expectError: false, + }, + } + + // Run test cases + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + receiptsChan := make(chan []*ReceiptWithTx) + sub := api.events.SubscribeTransactionReceipts(tc.filterTxHashes, receiptsChan) + + // Send chain event + backend.chainFeed.Send(chainEvent) + + // Wait for receipts + timeout := time.After(1 * time.Second) + var receivedReceipts []*types.Receipt + for { + select { + case receiptsWithTx := <-receiptsChan: + for _, receiptWithTx := range receiptsWithTx { + receivedReceipts = append(receivedReceipts, receiptWithTx.Receipt) + } + case <-timeout: + t.Fatalf("timeout waiting for receipts") + } + if len(receivedReceipts) >= len(tc.expectedReceiptTxHashes) { + break + } + } + + // Verify receipt count + if len(receivedReceipts) != len(tc.expectedReceiptTxHashes) { + t.Errorf("Expected %d receipts, got %d", len(tc.expectedReceiptTxHashes), len(receivedReceipts)) + } + + // Verify specific transaction hashes are present + if tc.expectedReceiptTxHashes != nil { + receivedHashes := make(map[common.Hash]bool) + for _, receipt := range receivedReceipts { + receivedHashes[receipt.TxHash] = true + } + + for _, expectedHash := range tc.expectedReceiptTxHashes { + if !receivedHashes[expectedHash] { + t.Errorf("Expected receipt for tx %x not found", expectedHash) + } + } + } + + // Cleanup + sub.Unsubscribe() + <-sub.Err() + }) + } +} diff --git a/eth/handler.go b/eth/handler.go index 38ba881f68..6f2bb7cdde 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -63,7 +63,7 @@ const ( voteChanSize = 256 // deltaTdThreshold is the threshold of TD difference for peers to broadcast votes. - deltaTdThreshold = 20 + deltaTdThreshold = 1000 // txMaxBroadcastSize is the max size of a transaction that will be broadcasted. // All transactions with a higher size will be announced and need to be fetched @@ -141,8 +141,10 @@ type handlerConfig struct { PeerSet *peerSet EnableQuickBlockFetching bool EnableEVNFeatures bool + EnableBAL bool EVNNodeIdsWhitelist []enode.ID ProxyedValidatorAddresses []common.Address + ProxyedNodeIds []enode.ID } type handler struct { @@ -150,8 +152,10 @@ type handler struct { networkID uint64 disablePeerTxBroadcast bool enableEVNFeatures bool + enableBAL bool evnNodeIdsWhitelistMap map[enode.ID]struct{} proxyedValidatorAddressMap map[common.Address]struct{} + proxyedNodeIdsMap map[enode.ID]struct{} snapSync atomic.Bool // Flag whether snap sync is enabled (gets disabled if we already have blocks) synced atomic.Bool // Flag whether we're considered synchronised (enables transaction processing) @@ -220,8 +224,10 @@ func newHandler(config *handlerConfig) (*handler, error) { requiredBlocks: config.RequiredBlocks, directBroadcast: config.DirectBroadcast, enableEVNFeatures: config.EnableEVNFeatures, + enableBAL: config.EnableBAL, evnNodeIdsWhitelistMap: make(map[enode.ID]struct{}), proxyedValidatorAddressMap: make(map[common.Address]struct{}), + proxyedNodeIdsMap: make(map[enode.ID]struct{}), quitSync: make(chan struct{}), handlerDoneCh: make(chan struct{}), handlerStartCh: make(chan struct{}), @@ -233,6 +239,9 @@ func newHandler(config *handlerConfig) (*handler, error) { for _, address := range config.ProxyedValidatorAddresses { h.proxyedValidatorAddressMap[address] = struct{}{} } + for _, nodeID := range config.ProxyedNodeIds { + h.proxyedNodeIdsMap[nodeID] = struct{}{} + } if h.chain.NoTries() { } else if config.Sync == ethconfig.FullSync { // The database seems empty as the current block is the genesis. Yet the snap @@ -328,7 +337,7 @@ func newHandler(config *handlerConfig) (*handler, error) { if p.bscExt == nil { return nil, fmt.Errorf("peer does not support bsc protocol, peer: %v", p.ID()) } - if p.bscExt.Version() != bsc.Bsc2 { + if p.bscExt.Version() < bsc.Bsc2 { return nil, fmt.Errorf("remote peer does not support the required Bsc2 protocol version, peer: %v", p.ID()) } res, err := p.bscExt.RequestBlocksByRange(startHeight, startHash, count) @@ -340,6 +349,7 @@ func newHandler(config *handlerConfig) (*handler, error) { for i, item := range res { block := types.NewBlockWithHeader(item.Header).WithBody(types.Body{Transactions: item.Txs, Uncles: item.Uncles}) block = block.WithSidecars(item.Sidecars) + block = block.WithBAL(item.BAL) block.ReceivedAt = time.Now() block.ReceivedFrom = p.ID() if err := block.SanityCheck(); err != nil { @@ -396,6 +406,7 @@ func newHandler(config *handlerConfig) (*handler, error) { func (h *handler) protoTracker() { defer h.wg.Done() + h.peers.setProxyedPeers(h.proxyedNodeIdsMap) if h.enableEVNFeatures && h.synced.Load() { h.peers.enableEVNFeatures(h.queryValidatorNodeIDsMap(), h.evnNodeIdsWhitelistMap) } @@ -409,6 +420,7 @@ func (h *handler) protoTracker() { case <-h.handlerDoneCh: active-- case <-updateTicker.C: + h.peers.setProxyedPeers(h.proxyedNodeIdsMap) if h.enableEVNFeatures && h.synced.Load() { // add onchain validator p2p node list later, it will enable the direct broadcast + no tx broadcast feature // here check & enable peer broadcast features periodically, and it's a simple way to handle the peer change and the list change scenarios. @@ -455,12 +467,15 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { peer.Log().Error("Snapshot extension barrier failed", "err", err) return err } - bsc, err := h.peers.waitBscExtension(peer) + bscExt, err := h.peers.waitBscExtension(peer) if err != nil { peer.Log().Error("Bsc extension barrier failed", "err", err) return err } - + if bscExt != nil && bscExt.Version() == bsc.Bsc3 { + peer.CanHandleBAL.Store(true) + log.Debug("runEthPeer", "bscExt.Version", bscExt.Version(), "CanHandleBAL", peer.CanHandleBAL.Load()) + } // Execute the Ethereum handshake var ( head = h.chain.CurrentHeader() @@ -510,7 +525,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { } // Register the peer locally - if err := h.peers.registerPeer(peer, snap, bsc); err != nil { + if err := h.peers.registerPeer(peer, snap, bscExt); err != nil { peer.Log().Error("Ethereum peer registration failed", "err", err) return err } @@ -812,40 +827,74 @@ func (h *handler) BroadcastBlock(block *types.Block, propagate bool) { log.Error("Propagating dangling block", "number", block.Number(), "hash", hash) return } - // Send the block to a subset of our peers - var transfer []*ethPeer - if h.directBroadcast { - transfer = peers[:] - } else { - transfer = peers[:int(math.Sqrt(float64(len(peers))))] + + // Broadcast the block to peers based on broadcast strategy. + totalPeers := len(peers) + + // Step 1: Select target peers for initial broadcast. + limit := totalPeers + if !h.directBroadcast { + limit = int(math.Sqrt(float64(totalPeers))) } - for _, peer := range transfer { - log.Debug("broadcast block to peer", "hash", hash, "peer", peer.ID(), "EVNPeerFlag", peer.EVNPeerFlag.Load()) + // Step 2: Broadcast to selected peers. + transferPeersCnt := limit + for _, peer := range peers[:limit] { + log.Debug("Broadcast block to peer", + "hash", hash, "peer", peer.ID(), + "EVNPeerFlag", peer.EVNPeerFlag.Load(), + "CanHandleBAL", peer.CanHandleBAL.Load(), + ) peer.AsyncSendNewBlock(block, td) } - // check if the block should be broadcast to more peers in EVN - var morePeers []*ethPeer - if h.needFullBroadcastInEVN(block) { - for i := len(transfer); i < len(peers); i++ { - if peers[i].EVNPeerFlag.Load() { - morePeers = append(morePeers, peers[i]) + // Step 3: Handle proxyed peers. + proxyedPeersCnt := 0 + if len(h.proxyedNodeIdsMap) > 0 { + for _, peer := range peers[limit:] { + if peer.ProxyedPeerFlag.Load() { + log.Debug("Broadcast block to proxyed peer", + "hash", hash, "peer", peer.ID(), + "EVNPeerFlag", peer.EVNPeerFlag.Load(), + "CanHandleBAL", peer.CanHandleBAL.Load(), + ) + peer.AsyncSendNewBlock(block, td) + proxyedPeersCnt++ } } - for _, peer := range morePeers { - log.Debug("broadcast block to extra peer", "hash", hash, "peer", peer.ID(), "EVNPeerFlag", peer.EVNPeerFlag.Load()) - peer.AsyncSendNewBlock(block, td) + } + + // Step 4: Handle EVN peers if full broadcast is required. + evnPeersCnt := 0 + if h.needFullBroadcastInEVN(block) { + for _, peer := range peers[limit:] { + if !peer.ProxyedPeerFlag.Load() && peer.EVNPeerFlag.Load() { + log.Debug("Broadcast block to EVN peer", + "hash", hash, "peer", peer.ID(), + "EVNPeerFlag", peer.EVNPeerFlag.Load(), + "CanHandleBAL", peer.CanHandleBAL.Load(), + ) + peer.AsyncSendNewBlock(block, td) + evnPeersCnt++ + } } } - log.Debug("Propagated block", "hash", hash, "recipients", len(transfer), "extra", len(morePeers), "duration", common.PrettyDuration(time.Since(block.ReceivedAt))) + // Step 5: Log summary. + log.Debug("Propagated block", + "hash", hash, + "recipients", transferPeersCnt, + "proxyed", proxyedPeersCnt, + "evn", evnPeersCnt, + "duration", common.PrettyDuration(time.Since(block.ReceivedAt)), + ) return } // Otherwise if the block is indeed in our own chain, announce it if h.chain.HasBlock(hash, block.NumberU64()) { for _, peer := range peers { - log.Debug("Announced block to peer", "hash", hash, "peer", peer.ID(), "EVNPeerFlag", peer.EVNPeerFlag.Load()) + log.Debug("Announced block to peer", "hash", hash, "peer", peer.ID(), + "EVNPeerFlag", peer.EVNPeerFlag.Load(), "CanHandleBAL", peer.CanHandleBAL.Load()) peer.AsyncSendNewBlockHash(block) } log.Debug("Announced block", "hash", hash, "recipients", len(peers), "duration", common.PrettyDuration(time.Since(block.ReceivedAt))) @@ -1004,9 +1053,16 @@ func (h *handler) BroadcastVote(vote *types.VoteEnvelope) { headBlock := h.chain.CurrentBlock() currentTD := h.chain.GetTd(headBlock.Hash(), headBlock.Number.Uint64()) for _, peer := range peers { + if peer.bscExt == nil { + continue + } + if peer.ProxyedPeerFlag.Load() || peer.EVNPeerFlag.Load() { + voteMap[peer] = vote + continue + } _, peerTD := peer.Head() deltaTD := new(big.Int).Abs(new(big.Int).Sub(currentTD, peerTD)) - if deltaTD.Cmp(big.NewInt(deltaTdThreshold)) < 1 && peer.bscExt != nil { + if deltaTD.Cmp(big.NewInt(deltaTdThreshold)) <= 0 { voteMap[peer] = vote } } diff --git a/eth/handler_eth.go b/eth/handler_eth.go index 4a1c2b50ea..ff8cfdf5a6 100644 --- a/eth/handler_eth.go +++ b/eth/handler_eth.go @@ -141,6 +141,9 @@ func (h *ethHandler) handleBlockBroadcast(peer *eth.Peer, packet *eth.NewBlockPa if sidecars != nil { block = block.WithSidecars(sidecars) } + if packet.Bal != nil && h.chain.Engine().VerifyBAL(block, packet.Bal) == nil { + block = block.WithBAL(packet.Bal) + } // Schedule the block for import log.Debug("handleBlockBroadcast", "peer", peer.ID(), "block", block.Number(), "hash", block.Hash()) diff --git a/eth/handler_test.go b/eth/handler_test.go index 03cbe7803d..a09285443d 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -376,7 +376,7 @@ func (t *testVotePool) PutVote(vote *types.VoteEnvelope) { t.voteFeed.Send(core.NewVoteEvent{Vote: vote}) } -func (t *testVotePool) FetchVoteByBlockHash(blockHash common.Hash) []*types.VoteEnvelope { +func (t *testVotePool) FetchVotesByBlockHash(blockHash common.Hash) []*types.VoteEnvelope { panic("implement me") } diff --git a/eth/peerset.go b/eth/peerset.go index 2c75ad3636..3c5b30c526 100644 --- a/eth/peerset.go +++ b/eth/peerset.go @@ -328,6 +328,24 @@ func (ps *peerSet) peer(id string) *ethPeer { return ps.peers[id] } +func (ps *peerSet) setProxyedPeers(proxyedNodeIdsMap map[enode.ID]struct{}) { + ps.lock.RLock() + peers := make([]*ethPeer, 0, len(ps.peers)) + for _, peer := range ps.peers { + peers = append(peers, peer) + } + ps.lock.RUnlock() + + proxyedPeerCnt := 0 + for _, peer := range peers { + if _, ok := proxyedNodeIdsMap[peer.NodeID()]; ok { + peer.ProxyedPeerFlag.Store(true) + proxyedPeerCnt++ + } + } + log.Debug("setProxyedPeers", "total", len(peers), "proxyedPeerCnt", proxyedPeerCnt) +} + // enableEVNFeatures enables the given features for the given peers. func (ps *peerSet) enableEVNFeatures(validatorNodeIDsMap map[common.Address][]enode.ID, evnWhitelistMap map[enode.ID]struct{}) { // clone current all peers, and update the validatorNodeIDsMap diff --git a/eth/protocols/bsc/handler.go b/eth/protocols/bsc/handler.go index 37d3d29f9b..994a362e3a 100644 --- a/eth/protocols/bsc/handler.go +++ b/eth/protocols/bsc/handler.go @@ -166,15 +166,17 @@ func handleGetBlocksByRange(backend Backend, msg Decoder, peer *Peer) error { return fmt.Errorf("msg %v, cannot get start block: %v, %v", GetBlocksByRangeMsg, req.StartBlockHeight, req.StartBlockHash) } blocks = append(blocks, NewBlockData(block)) + balSize := block.BALSize() for i := uint64(1); i < req.Count; i++ { block = backend.Chain().GetBlockByHash(block.ParentHash()) if block == nil { break } + balSize += block.BALSize() blocks = append(blocks, NewBlockData(block)) } - log.Debug("reply GetBlocksByRange msg", "from", peer.id, "req", req.Count, "blocks", len(blocks)) + log.Debug("reply GetBlocksByRange msg", "from", peer.id, "req", req.Count, "blocks", len(blocks), "balSize", balSize) return p2p.Send(peer.rw, BlocksByRangeMsg, &BlocksByRangePacket{ RequestId: req.RequestId, Blocks: blocks, diff --git a/eth/protocols/bsc/peer.go b/eth/protocols/bsc/peer.go index 3bddc9cc3e..c74a956e86 100644 --- a/eth/protocols/bsc/peer.go +++ b/eth/protocols/bsc/peer.go @@ -23,8 +23,8 @@ const ( // used to avoid of DDOS attack // It's the max number of received votes per second from one peer // 21 validators exist now, so 21 votes will be produced every one block interval - // so the limit is 28 = 21/0.75, here set it to 40 with a buffer. - receiveRateLimitPerSecond = 40 + // so the limit is 47 ~= 21/0.45, here set it to 68 with a buffer. + receiveRateLimitPerSecond = 68 // the time span of one period secondsPerPeriod = float64(30) diff --git a/eth/protocols/bsc/protocol.go b/eth/protocols/bsc/protocol.go index 50a08599af..572c24debb 100644 --- a/eth/protocols/bsc/protocol.go +++ b/eth/protocols/bsc/protocol.go @@ -12,6 +12,7 @@ import ( const ( Bsc1 = 1 Bsc2 = 2 + Bsc3 = 3 // to BAL process ) // ProtocolName is the official short name of the `bsc` protocol used during @@ -20,11 +21,11 @@ const ProtocolName = "bsc" // ProtocolVersions are the supported versions of the `bsc` protocol (first // is primary). -var ProtocolVersions = []uint{Bsc1, Bsc2} +var ProtocolVersions = []uint{Bsc1, Bsc2, Bsc3} // protocolLengths are the number of implemented message corresponding to // different protocol versions. -var protocolLengths = map[uint]uint64{Bsc1: 2, Bsc2: 4} +var protocolLengths = map[uint]uint64{Bsc1: 2, Bsc2: 4, Bsc3: 4} // maxMessageSize is the maximum cap on the size of a protocol message. const maxMessageSize = 10 * 1024 * 1024 @@ -84,8 +85,9 @@ type BlockData struct { Header *types.Header Txs []*types.Transaction Uncles []*types.Header - Withdrawals []*types.Withdrawal `rlp:"optional"` - Sidecars types.BlobSidecars `rlp:"optional"` + Withdrawals []*types.Withdrawal `rlp:"optional"` + Sidecars types.BlobSidecars `rlp:"optional"` + BAL *types.BlockAccessListEncode `rlp:"optional"` } // NewBlockData creates a new BlockData object from a block @@ -96,6 +98,7 @@ func NewBlockData(block *types.Block) *BlockData { Uncles: block.Uncles(), Withdrawals: block.Withdrawals(), Sidecars: block.Sidecars(), + BAL: block.BAL(), } } diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index 8fb4e17d5a..81c3501cdd 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -376,6 +376,13 @@ func handleNewBlock(backend Backend, msg Decoder, peer *Peer) error { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } + if ann.Bal != nil { + log.Debug("handleNewBlock, BAL", "number", ann.Block.NumberU64(), "hash", ann.Block.Hash(), "peer", peer.ID(), + "version", ann.Bal.Version, "signData", len(ann.Bal.SignData), "accounts", len(ann.Bal.Accounts), "balSize", ann.Block.BALSize()) + } else { + log.Debug("handleNewBlock, no BAL", "number", ann.Block.NumberU64(), "hash", ann.Block.Hash(), "peer", peer.ID(), + "txNum", len(ann.Block.Transactions()), "balSize", ann.Block.BALSize()) + } // Now that we have our packet, perform operations using the interface methods if err := ann.sanityCheck(); err != nil { return err diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go index 654385e410..d7ef06b265 100644 --- a/eth/protocols/eth/peer.go +++ b/eth/protocols/eth/peer.go @@ -25,6 +25,7 @@ import ( mapset "github.com/deckarep/golang-set/v2" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" @@ -311,10 +312,23 @@ func (p *Peer) AsyncSendNewBlockHash(block *types.Block) { func (p *Peer) SendNewBlock(block *types.Block, td *big.Int) error { // Mark all the block hash as known, but ensure we don't overflow our limits p.knownBlocks.Add(block.Hash()) + bal := block.BAL() + if !p.CanHandleBAL.Load() { + bal = nil + } + if bal != nil { + log.Debug("SendNewBlock", "number", block.NumberU64(), "hash", block.Hash(), "peer", p.ID(), + "balSize", block.BALSize(), "version", bal.Version, "canHandleBAL", p.CanHandleBAL.Load()) + } else { + log.Debug("SendNewBlock no BAL", "number", block.NumberU64(), "hash", block.Hash(), "peer", p.ID(), + "txNum", len(block.Transactions()), "canHandleBAL", p.CanHandleBAL.Load()) + } + return p2p.Send(p.rw, NewBlockMsg, &NewBlockPacket{ Block: block, TD: td, Sidecars: block.Sidecars(), + Bal: bal, }) } diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index 5d4566b7c2..60d8a2f6b9 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -233,7 +233,8 @@ type BlockHeadersRLPPacket struct { type NewBlockPacket struct { Block *types.Block TD *big.Int - Sidecars types.BlobSidecars `rlp:"optional"` + Sidecars types.BlobSidecars `rlp:"optional"` + Bal *types.BlockAccessListEncode `rlp:"optional"` } // sanityCheck verifies that the values are reasonable, as a DoS protection diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 339d3bf9a9..fbb891538e 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/opcodeCompiler/compiler" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -50,8 +51,17 @@ func testCtx() *vmContext { } func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { + return runTraceWithOption(tracer, vmctx, chaincfg, contractCode, false) +} + +func runTraceWithOptiEnabled(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { + return runTraceWithOption(tracer, vmctx, chaincfg, contractCode, true) +} + +func runTraceWithOption(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte, enableOpti bool) (json.RawMessage, error) { var ( - evm = vm.NewEVM(vmctx.blockCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.Hooks}) + evm = vm.NewEVM(vmctx.blockCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.Hooks, + EnableOpcodeOptimizations: enableOpti}) gasLimit uint64 = 31000 startGas uint64 = 10000 value = uint256.NewInt(0) @@ -65,6 +75,14 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo contract.Code = contractCode } + if enableOpti { + // reset the code also require flush code cache. + compiler.DeleteCodeCache(contract.CodeHash) + optimized, _ := compiler.GenOrRewriteOptimizedCode(contract.CodeHash, contract.Code) + contract.Code = optimized + contract.SetOptimizedForTest() + } + tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice}), contract.Caller()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig()) ret, err := evm.Interpreter().Run(contract, []byte{}, false) @@ -162,6 +180,82 @@ func TestTracer(t *testing.T) { } } +func TestTracerWithOptimization(t *testing.T) { + execTracer := func(code string, contract []byte) ([]byte, string) { + t.Helper() + chainConfig := params.TestChainConfig + tracer, err := newJsTracer(code, nil, nil, chainConfig) + if err != nil { + t.Fatal(err) + } + ret, err := runTraceWithOptiEnabled(tracer, testCtx(), params.TestChainConfig, contract) + if err != nil { + return nil, err.Error() // Stringify to allow comparison without nil checks + } + return ret, "" + } + for i, tt := range []struct { + code string + want string + fail string + contract []byte + }{ + { // tests that we don't panic on bad arguments to memory access + code: "{depths: [], step: function(log) { this.depths.push(log.memory.slice(-1,-2)); }, fault: function() {}, result: function() { return this.depths; }}", + want: ``, + fail: "tracer accessed out of bound memory: offset -1, end -2 at step (:1:53(13)) in server-side tracer function 'step'", + }, { // tests that we don't panic on bad arguments to stack peeks + code: "{depths: [], step: function(log) { this.depths.push(log.stack.peek(-1)); }, fault: function() {}, result: function() { return this.depths; }}", + want: ``, + fail: "tracer accessed out of bound stack: size 0, index -1 at step (:1:53(11)) in server-side tracer function 'step'", + }, { // tests that we don't panic on bad arguments to memory getUint + code: "{ depths: [], step: function(log, db) { this.depths.push(log.memory.getUint(-64));}, fault: function() {}, result: function() { return this.depths; }}", + want: ``, + fail: "tracer accessed out of bound memory: available 0, offset -64, size 32 at step (:1:58(11)) in server-side tracer function 'step'", + }, { // tests some general counting + code: "{count: 0, step: function() { this.count += 1; }, fault: function() {}, result: function() { return this.count; }}", + want: `2`, + }, { // tests that depth is reported correctly + code: "{depths: [], step: function(log) { this.depths.push(log.stack.length()); }, fault: function() {}, result: function() { return this.depths; }}", + want: `[0,2]`, + }, { // tests memory length + code: "{lengths: [], step: function(log) { this.lengths.push(log.memory.length()); }, fault: function() {}, result: function() { return this.lengths; }}", + want: `[0,0]`, + }, { // tests to-string of opcodes + code: "{opcodes: [], step: function(log) { this.opcodes.push(log.op.toString()); }, fault: function() {}, result: function() { return this.opcodes; }}", + want: `["PUSH1PUSH1","STOP"]`, + }, { // tests gasUsed + code: "{depths: [], step: function() {}, fault: function() {}, result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed; }}", + want: `"100000.21006"`, + }, { + code: "{res: null, step: function(log) {}, fault: function() {}, result: function() { return toWord('0xffaa') }}", + want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":255,"31":170}`, + }, { // test feeding a buffer back into go + code: "{res: null, step: function(log) { var address = log.contract.getAddress(); this.res = toAddress(address); }, fault: function() {}, result: function() { return this.res }}", + want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0}`, + }, { + code: "{res: null, step: function(log) { var address = '0x0000000000000000000000000000000000000000'; this.res = toAddress(address); }, fault: function() {}, result: function() { return this.res }}", + want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0}`, + }, { + code: "{res: null, step: function(log) { var address = Array.prototype.slice.call(log.contract.getAddress()); this.res = toAddress(address); }, fault: function() {}, result: function() { return this.res }}", + want: `{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0}`, + }, { + code: "{res: [], step: function(log) { var op = log.op.toString(); if (op === 'MSTORE8' || op === 'STOP') { this.res.push(log.memory.slice(0, 2)) } }, fault: function() {}, result: function() { return this.res }}", + want: `[{"0":0,"1":0},{"0":255,"1":0}]`, + contract: []byte{byte(vm.PUSH1), byte(0xff), byte(vm.PUSH1), byte(0x00), byte(vm.MSTORE8), byte(vm.STOP)}, + }, { + code: "{res: [], step: function(log) { if (log.op.toString() === 'STOP') { this.res.push(log.memory.slice(5, 1025 * 1024)) } }, fault: function() {}, result: function() { return this.res }}", + want: "", + fail: "reached limit for padding memory slice: 1049568 at step (:1:83(20)) in server-side tracer function 'step'", + contract: []byte{byte(vm.PUSH1), byte(0xff), byte(vm.PUSH1), byte(0x00), byte(vm.MSTORE8), byte(vm.STOP)}, + }, + } { + if have, err := execTracer(tt.code, tt.contract); tt.want != string(have) || tt.fail != err { + t.Errorf("testcase %d: expected return value to be '%s' got '%s', error to be '%s' got '%s'\n\tcode: %v", i, tt.want, string(have), tt.fail, err, tt.code) + } + } +} + func TestHalt(t *testing.T) { timeout := errors.New("stahp") chainConfig := params.TestChainConfig diff --git a/ethdb/database.go b/ethdb/database.go index 96d9d5abb3..b6510bc64d 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -171,6 +171,8 @@ type AncientWriter interface { // ResetTable will reset certain table with new start point ResetTable(kind string, startAt uint64, onlyEmpty bool) error + + ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error } type FreezerEnv struct { @@ -182,6 +184,10 @@ type FreezerEnv struct { type AncientFreezer interface { // SetupFreezerEnv provides params.ChainConfig for checking hark forks, like isCancun. SetupFreezerEnv(env *FreezerEnv, blockHistory uint64) error + + // CleanBlock cleans block data in pebble and chain freezer. + // WARN: it's only used in the incremental snapshot situation. + CleanBlock(kvStore KeyValueStore, start uint64) error } // AncientWriteOp is given to the function argument of ModifyAncients. diff --git a/ethdb/remotedb/remotedb.go b/ethdb/remotedb/remotedb.go index 7e90c1b136..53c6c0ff06 100644 --- a/ethdb/remotedb/remotedb.go +++ b/ethdb/remotedb/remotedb.go @@ -133,6 +133,10 @@ func (db *Database) ResetTable(kind string, startAt uint64, onlyEmpty bool) erro panic("not supported") } +func (db *Database) ResetTableForIncr(kind string, startAt uint64, onlyEmpty bool) error { + panic("not supported") +} + func (db *Database) SyncAncient() error { return nil } @@ -174,6 +178,10 @@ func (db *Database) SetupFreezerEnv(env *ethdb.FreezerEnv, blockHistory uint64) panic("not supported") } +func (db *Database) CleanBlock(ethdb.KeyValueStore, uint64) error { + panic("not supported") +} + func New(client *rpc.Client) ethdb.Database { if client == nil { return nil diff --git a/go.mod b/go.mod index 3e5b574078..8654f46e54 100644 --- a/go.mod +++ b/go.mod @@ -58,6 +58,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/panjf2000/ants/v2 v2.4.5 github.com/peterh/liner v1.2.0 + github.com/pierrec/lz4/v4 v4.1.22 github.com/pion/stun/v2 v2.0.0 github.com/pkg/errors v0.9.1 github.com/protolambda/bls12-381-util v0.1.0 @@ -257,7 +258,7 @@ require ( github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b // indirect github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c // indirect github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.48.2 // indirect + github.com/quic-go/quic-go v0.49.1 // indirect github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect diff --git a/go.sum b/go.sum index 4ed4ad6a06..b7000cfa85 100644 --- a/go.sum +++ b/go.sum @@ -871,6 +871,8 @@ github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCr github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= @@ -991,8 +993,8 @@ github.com/prysmaticlabs/prysm/v5 v5.3.2 h1:yV44gm5DENWG2l17JjjaWMCagJkwSZz5ZG6z github.com/prysmaticlabs/prysm/v5 v5.3.2/go.mod h1:2SaUMpJ+O8r/pcnNDMHbrk0Ki9ObQXvfRc+rQHovzVk= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= -github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= +github.com/quic-go/quic-go v0.49.1 h1:e5JXpUyF0f2uFjckQzD8jTghZrOUK1xxDqqZhlwixo0= +github.com/quic-go/quic-go v0.49.1/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg= github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d3fc2fdcf0..d54f34a839 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -456,13 +456,13 @@ func decodeHash(s string) (h common.Hash, inputLength int, err error) { if (len(s) & 1) > 0 { s = "0" + s } + if len(s) > 64 { + return common.Hash{}, len(s) / 2, errors.New("hex string too long, want at most 32 bytes") + } b, err := hex.DecodeString(s) if err != nil { return common.Hash{}, 0, errors.New("hex string invalid") } - if len(b) > 32 { - return common.Hash{}, len(b), errors.New("hex string too long, want at most 32 bytes") - } return common.BytesToHash(b), len(b), nil } @@ -713,7 +713,7 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp result := make([]map[string]interface{}, len(receipts)) for i, receipt := range receipts { - result[i] = marshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i) + result[i] = MarshalReceipt(receipt, block.Hash(), block.NumberU64(), signer, txs[i], i) } return result, nil @@ -1830,7 +1830,7 @@ func (api *TransactionAPI) GetTransactionDataAndReceipt(ctx context.Context, has return nil, err } signer := types.MakeSigner(api.b.ChainConfig(), header.Number, header.Time) - fields := marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)) + fields := MarshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)) // TODO use nil basefee before landon fork is enabled rpcTransaction := newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, nil, api.b.ChainConfig()) @@ -1874,11 +1874,11 @@ func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash commo return nil, err } // Derive the sender. - return marshalReceipt(receipt, blockHash, blockNumber, api.signer, tx, int(index)), nil + return MarshalReceipt(receipt, blockHash, blockNumber, api.signer, tx, int(index)), nil } -// marshalReceipt marshals a transaction receipt into a JSON object. -func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber uint64, signer types.Signer, tx *types.Transaction, txIndex int) map[string]interface{} { +// MarshalReceipt marshals a transaction receipt into a JSON object. +func MarshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber uint64, signer types.Signer, tx *types.Transaction, txIndex int) map[string]interface{} { from, _ := types.Sender(signer, tx) fields := map[string]interface{}{ diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index d0c4420eeb..74d33fc8ad 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -652,9 +652,6 @@ func (b testBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { panic("implement me") } -func (b testBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription { - panic("implement me") -} func (b *testBackend) MevRunning() bool { return false } func (b *testBackend) HasBuilder(builder common.Address) bool { return false } diff --git a/miner/bid_simulator.go b/miner/bid_simulator.go index 2b873450fd..b81ab2614e 100644 --- a/miner/bid_simulator.go +++ b/miner/bid_simulator.go @@ -508,7 +508,7 @@ func (b *bidSimulator) newBidLoop() { // get block interval for current block by using parent header func (b *bidSimulator) getBlockInterval(parentHeader *types.Header) uint64 { if parentHeader == nil { - return 750 // maxwellBlockInterval + return 450 // fermiBlockInterval } parlia, _ := b.engine.(*parlia.Parlia) // only `Number` and `ParentHash` are used when `BlockInterval` diff --git a/miner/miner.go b/miner/miner.go index cd2f75d214..3479f81dce 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -67,7 +67,7 @@ func New(eth Backend, config *minerconfig.Config, mux *event.TypeMux, engine con exitCh: make(chan struct{}), startCh: make(chan struct{}), stopCh: make(chan struct{}), - worker: newWorker(config, engine, eth, mux, false), + worker: newWorker(config, engine, eth, mux), } miner.bidSimulator = newBidSimulator(&config.Mev, config.DelayLeftOver, config.GasPrice, eth, eth.BlockChain().Config(), engine, miner.worker) @@ -178,17 +178,10 @@ func (miner *Miner) TryWaitProposalDoneWhenStopping() { miner.worker.tryWaitProposalDoneWhenStopping() } -// Pending returns the currently pending block and associated receipts, logs +// Pending returns the latest block and associated receipts, logs // and statedb. The returned values can be nil in case the pending block is // not initialized. func (miner *Miner) Pending() (*types.Block, types.Receipts, *state.StateDB) { - if miner.worker.isRunning() { - pendingBlock, pendingReceipts, pendingState := miner.worker.pending() - if pendingState != nil && pendingBlock != nil { - return pendingBlock, pendingReceipts, pendingState - } - } - // fallback to latest block block := miner.worker.chain.CurrentBlock() if block == nil { return nil, nil, nil @@ -234,12 +227,6 @@ func (miner *Miner) SetGasCeil(ceil uint64) { miner.worker.setGasCeil(ceil) } -// SubscribePendingLogs starts delivering logs from pending transactions -// to the given channel. -func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription { - return miner.worker.pendingLogsFeed.Subscribe(ch) -} - // BuildPayload builds the payload according to the provided parameters. func (miner *Miner) BuildPayload(args *BuildPayloadArgs, witness bool) (*Payload, error) { return miner.worker.buildPayload(args, witness) diff --git a/miner/minerconfig/config.go b/miner/minerconfig/config.go index 7873702bf2..fc13440b30 100644 --- a/miner/minerconfig/config.go +++ b/miner/minerconfig/config.go @@ -32,11 +32,12 @@ var ( defaultRecommit = 10 * time.Second defaultMaxWaitProposalInSecs = uint64(45) - defaultDelayLeftOver = 20 * time.Millisecond + // Extra time for finalizing and committing blocks (excludes writing to disk). + defaultDelayLeftOver = 25 * time.Millisecond defaultBidSimulationLeftOver = 30 * time.Millisecond - // Bid simulation speed on mainnet ranges from 400 to 700 mgasps. - // Here we assume 500 for estimation. - defaultNoInterruptLeftOver = 170 * time.Millisecond // For gas limit 75M + // For estimation, assume 500 Mgas/s: + // (100M gas / 500 Mgas/s) * 1000 ms + 10 ms buffer + defaultDelayLeftOver ≈ 235 ms. + defaultNoInterruptLeftOver = 235 * time.Millisecond ) // Other default MEV-related configurations @@ -66,7 +67,7 @@ type Config struct { // DefaultConfig contains default settings for miner. var DefaultConfig = Config{ - GasCeil: 75000000, + GasCeil: 100000000, GasPrice: big.NewInt(params.GWei), // The default recommit time is chosen as two seconds since diff --git a/miner/worker.go b/miner/worker.go index ea462a96ba..dbabc947dd 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -46,7 +46,6 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/miner/minerconfig" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/trie" ) const ( @@ -67,8 +66,8 @@ const ( // staleThreshold is the maximum depth of the acceptable stale block. staleThreshold = 11 - // the current 4 mining loops could have asynchronous risk of mining block with - // save height, keep recently mined blocks to avoid double sign for safety, + // the current mining loops could have asynchronous risk of mining block with + // same height, keep recently mined blocks to avoid double sign for safety, recentMinedCacheLimit = 20 ) @@ -79,8 +78,10 @@ var ( bestBidGasUsedGauge = metrics.NewRegisteredGauge("worker/bestBidGasUsed", nil) // MGas bestWorkGasUsedGauge = metrics.NewRegisteredGauge("worker/bestWorkGasUsed", nil) // MGas - writeBlockTimer = metrics.NewRegisteredTimer("worker/writeblock", nil) - finalizeBlockTimer = metrics.NewRegisteredTimer("worker/finalizeblock", nil) + writeBlockTimer = metrics.NewRegisteredTimer("worker/writeblock", nil) + finalizeBlockTimer = metrics.NewRegisteredTimer("worker/finalizeblock", nil) + pendingPlainTxsTimer = metrics.NewRegisteredTimer("worker/pendingPlainTxs", nil) + pendingBlobTxsTimer = metrics.NewRegisteredTimer("worker/pendingBlobTxs", nil) errBlockInterruptedByNewHead = errors.New("new head arrived while building block") errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block") @@ -110,33 +111,6 @@ type environment struct { committed bool } -// copy creates a deep copy of environment. -func (env *environment) copy() *environment { - cpy := &environment{ - signer: env.signer, - state: env.state.Copy(), - tcount: env.tcount, - coinbase: env.coinbase, - header: types.CopyHeader(env.header), - receipts: copyReceipts(env.receipts), - committed: env.committed, - } - if env.gasPool != nil { - gasPool := *env.gasPool - cpy.gasPool = &gasPool - } - cpy.txs = make([]*types.Transaction, len(env.txs)) - copy(cpy.txs, env.txs) - - if env.sidecars != nil { - cpy.sidecars = make(types.BlobSidecars, len(env.sidecars)) - copy(cpy.sidecars, env.sidecars) - cpy.blobs = env.blobs - } - - return cpy -} - // discard terminates the background prefetcher go-routine. It should // always be called for all created environment instances otherwise // the go-routine leak can happen. @@ -149,11 +123,11 @@ func (env *environment) discard() { // task contains all information for consensus engine sealing and result submitting. type task struct { - receipts []*types.Receipt - state *state.StateDB - block *types.Block - createdAt time.Time + receipts []*types.Receipt + state *state.StateDB + block *types.Block + createdAt time.Time miningStartAt time.Time } @@ -207,9 +181,6 @@ type worker struct { prio []common.Address // A list of senders to prioritize chain *core.BlockChain - // Feeds - pendingLogsFeed event.Feed - // Subscriptions mux *event.TypeMux chainHeadCh chan core.ChainHeadEvent @@ -236,28 +207,23 @@ type worker struct { pendingMu sync.RWMutex pendingTasks map[common.Hash]*task - snapshotMu sync.RWMutex // The lock used to protect the snapshots below - snapshotBlock *types.Block - snapshotReceipts types.Receipts - snapshotState *state.StateDB - // atomic status counters running atomic.Bool // The indicator whether the consensus engine is running or not. syncing atomic.Bool // The indicator whether the node is still syncing. // recommit is the time interval to re-create sealing work or to re-build // payload in proof-of-stake stage. - recommit time.Duration + recommit time.Duration + recentMinedBlocks *lru.Cache[uint64, []common.Hash] // 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. - fullTaskHook func() // Method to call before pushing the full sealing task. - resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. - recentMinedBlocks *lru.Cache[uint64, []common.Hash] + newTaskHook func(*task) // Method to call upon receiving a new sealing task. + skipSealHook func(*task) bool // Method to decide whether skipping the sealing. + fullTaskHook func() // Method to call before pushing the full sealing task. + resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. } -func newWorker(config *minerconfig.Config, engine consensus.Engine, eth Backend, mux *event.TypeMux, init bool) *worker { +func newWorker(config *minerconfig.Config, engine consensus.Engine, eth Backend, mux *event.TypeMux) *worker { chainConfig := eth.BlockChain().Config() worker := &worker{ prefetcher: core.NewStatePrefetcher(chainConfig, eth.BlockChain().HeadChain()), @@ -289,10 +255,6 @@ func newWorker(config *minerconfig.Config, engine consensus.Engine, eth Backend, if worker.config.Recommit != nil && *worker.config.Recommit > minRecommitInterval { recommit = *worker.config.Recommit } - if recommit < minRecommitInterval { - log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval) - recommit = minRecommitInterval - } worker.recommit = recommit worker.wg.Add(4) @@ -301,11 +263,6 @@ func newWorker(config *minerconfig.Config, engine consensus.Engine, eth Backend, go worker.resultLoop() go worker.taskLoop() - // Submit first work to initialize pending state. - if init { - worker.startCh <- struct{}{} - } - return worker } @@ -372,17 +329,6 @@ func (w *worker) setPrioAddresses(prio []common.Address) { w.prio = prio } -// Pending returns the currently pending block, associated receipts and statedb. -// The returned values can be nil in case the pending block is not initialized. -func (w *worker) pending() (*types.Block, types.Receipts, *state.StateDB) { - w.snapshotMu.RLock() - defer w.snapshotMu.RUnlock() - if w.snapshotState == nil { - return nil, nil, nil - } - return w.snapshotBlock, w.snapshotReceipts, w.snapshotState.Copy() -} - // start sets the running status as 1 and triggers new work submitting. func (w *worker) start() { w.running.Store(true) @@ -664,6 +610,13 @@ func (w *worker) resultLoop() { w.recentMinedBlocks.Add(block.NumberU64(), []common.Hash{block.ParentHash()}) } + // add BAL to the block + bal := task.state.GetEncodedBlockAccessList(block) + if bal != nil && w.engine.SignBAL(bal) == nil { + block = block.WithBAL(bal) + } + task.state.DumpAccessList(block) + // Commit block and state to database. start := time.Now() status, err := w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, w.mux) @@ -679,8 +632,8 @@ func (w *worker) resultLoop() { stats := w.chain.GetBlockStats(block.Hash()) stats.SendBlockTime.Store(time.Now().UnixMilli()) stats.StartMiningTime.Store(task.miningStartAt.UnixMilli()) - log.Info("Successfully seal and write new block", "number", block.Number(), "sealhash", sealhash, "hash", hash, - "elapsed", common.PrettyDuration(time.Since(task.createdAt))) + log.Info("Successfully seal and write new block", "number", block.Number(), "hash", hash, "time", block.Header().MilliTimestamp(), "sealhash", sealhash, + "block size(noBal)", block.Size(), "balSize", block.BALSize(), "elapsed", common.PrettyDuration(time.Since(task.createdAt))) w.mux.Post(core.NewMinedBlockEvent{Block: block}) case <-w.exitCh: @@ -694,7 +647,7 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co prevEnv *environment, witness bool) (*environment, error) { // Retrieve the parent state to execute on top and start a prefetcher for // the miner to speed block sealing up a bit - state, err := w.chain.StateAt(parent.Root) + state, err := w.chain.StateWithCacheAt(parent.Root) if err != nil { return nil, err } @@ -726,25 +679,6 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co return env, nil } -// updateSnapshot updates pending snapshot block, receipts and state. -func (w *worker) updateSnapshot(env *environment) { - w.snapshotMu.Lock() - defer w.snapshotMu.Unlock() - - body := types.Body{Transactions: env.txs} - if env.header.EmptyWithdrawalsHash() { - body.Withdrawals = make([]*types.Withdrawal, 0) - } - w.snapshotBlock = types.NewBlock( - env.header, - &body, - env.receipts, - trie.NewStackTrie(nil), - ) - w.snapshotReceipts = copyReceipts(env.receipts) - w.snapshotState = env.state.Copy() -} - func (w *worker) commitTransaction(env *environment, tx *types.Transaction, receiptProcessors ...core.ReceiptProcessor) ([]*types.Log, error) { if tx.Type() == types.BlobTxType { return w.commitBlobTransaction(env, tx, receiptProcessors...) @@ -813,7 +747,6 @@ func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transac } } - var coalescedLogs []*types.Log // initialize bloom processors processorCapacity := 100 if plainTxs.CurrentSize() < processorCapacity { @@ -829,7 +762,7 @@ func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transac tx := txsPrefetch.PeekWithUnwrap() if tx != nil { txCurr := &tx - w.prefetcher.PrefetchMining(txsPrefetch, env.header, env.gasPool.Gas(), env.state.CopyDoPrefetch(), *w.chain.GetVMConfig(), stopPrefetchCh, txCurr) + w.prefetcher.PrefetchMining(txsPrefetch, env.header, env.gasPool.Gas(), env.state.StateForPrefetch(), *w.chain.GetVMConfig(), stopPrefetchCh, txCurr) } signal := commitInterruptNone @@ -958,7 +891,7 @@ LOOP: // Start executing the transaction env.state.SetTxContext(tx.Hash(), env.tcount) - logs, err := w.commitTransaction(env, tx, bloomProcessors) + _, err := w.commitTransaction(env, tx, bloomProcessors) switch { case errors.Is(err, core.ErrNonceTooLow): // New head notification data race between the transaction pool and miner, shift @@ -966,8 +899,7 @@ LOOP: txs.Shift() case errors.Is(err, nil): - // Everything ok, collect the logs and shift in the next transaction from the same account - coalescedLogs = append(coalescedLogs, logs...) + // Everything ok, shift in the next transaction from the same account env.tcount++ txs.Shift() @@ -978,21 +910,7 @@ LOOP: txs.Pop() } } - if !w.isRunning() && len(coalescedLogs) > 0 { - // We don't push the pendingLogsEvent while we are sealing. The reason is that - // when we are sealing, the worker will regenerate a sealing block every 3 seconds. - // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing. - - // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined - // logs by filling in the block hash when the block was mined by the local miner. This can - // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. - cpy := make([]*types.Log, len(coalescedLogs)) - for i, l := range coalescedLogs { - cpy[i] = new(types.Log) - *cpy[i] = *l - } - w.pendingLogsFeed.Send(cpy) - } + return signalToErr(signal) } @@ -1125,11 +1043,16 @@ func (w *worker) fillTransactions(interruptCh chan int32, env *environment, stop if env.header.ExcessBlobGas != nil { filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(w.chainConfig, env.header)) } + filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false + plainTxsStart := time.Now() pendingPlainTxs := w.eth.TxPool().Pending(filter) + pendingPlainTxsTimer.UpdateSince(plainTxsStart) filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true + blobTxsStart := time.Now() pendingBlobTxs := w.eth.TxPool().Pending(filter) + pendingBlobTxsTimer.UpdateSince(blobTxsStart) if bidTxs != nil { filterBidTxs := func(commonTxs map[common.Address][]*txpool.LazyTransaction) { @@ -1316,6 +1239,25 @@ LOOP: log.Debug("Not enough time for commitWork") break } else { + if !w.inTurn() && len(workList) == 1 { + if parliaEngine, ok := w.engine.(*parlia.Parlia); ok { + // When mining out of turn, continuous access to the txpool and trie database + // may cause lock contention, slowing down transaction insertion and block importing. + // Applying a backoff delay mitigates this issue and significantly reduces CPU usage. + if blockInterval, err := parliaEngine.BlockInterval(w.chain, w.chain.CurrentBlock()); err == nil { + beforeSealing := time.Until(time.UnixMilli(int64(work.header.MilliTimestamp()))) + if wait := beforeSealing - time.Duration(blockInterval)*time.Millisecond; wait > 0 { + log.Debug("Applying backoff before mining", "block", work.header.Number, "waiting(ms)", wait.Milliseconds()) + select { + case <-time.After(wait): + case <-interruptCh: + log.Debug("CommitWork interrupted: new block imported or resubmission triggered", "block", work.header.Number) + return + } + } + } + } + } log.Debug("commitWork stopTimer", "block", work.header.Number, "header time", time.UnixMilli(int64(work.header.MilliTimestamp())), "commit delay", *delay, "DelayLeftOver", w.config.DelayLeftOver) @@ -1480,7 +1422,7 @@ LOOP: } } - w.commit(bestWork, w.fullTaskHook, true, start) + w.commit(bestWork, w.fullTaskHook, start) // Swap out the old work with the new one, terminating any leftover // prefetcher processes in the mean time and starting a new one. @@ -1498,9 +1440,7 @@ func (w *worker) inTurn() bool { // commit runs any post-transaction state modifications, assembles the final block // and commits new work if consensus engine is running. -// Note the assumption is held that the mutation is allowed to the passed env, do -// the deep copy first. -func (w *worker) commit(env *environment, interval func(), update bool, start time.Time) error { +func (w *worker) commit(env *environment, interval func(), start time.Time) error { if w.isRunning() { if env.committed { log.Warn("Invalid work commit: already committed", "number", env.header.Number.Uint64()) @@ -1530,10 +1470,6 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti if w.chainConfig.IsCancun(env.header.Number, env.header.Time) && env.sidecars == nil { env.sidecars = make(types.BlobSidecars, 0) } - // Create a local environment copy, avoid the data race with snapshot state. - // https://github.com/ethereum/go-ethereum/issues/24299 - env := env.copy() - block = block.WithSidecars(env.sidecars) select { @@ -1545,9 +1481,6 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti log.Info("Worker has exited") } } - if update { - w.updateSnapshot(env) - } return nil } @@ -1604,16 +1537,6 @@ func (w *worker) tryWaitProposalDoneWhenStopping() { time.Sleep(time.Duration(waitSecs) * time.Second) } -// copyReceipts makes a deep copy of the given receipts. -func copyReceipts(receipts []*types.Receipt) []*types.Receipt { - result := make([]*types.Receipt, len(receipts)) - for i, l := range receipts { - cpy := *l - result[i] = &cpy - } - return result -} - // signalToErr converts the interruption signal to a concrete error type for return. // The given signal must be a valid interruption signal. func signalToErr(signal int32) error { diff --git a/miner/worker_test.go b/miner/worker_test.go index d00ead1bcd..e0fab878b6 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -161,7 +161,7 @@ func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction { func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) { backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks) backend.txPool.Add(pendingTxs, true) - w := newWorker(testConfig, engine, backend, new(event.TypeMux), false) + w := newWorker(testConfig, engine, backend, new(event.TypeMux)) w.setEtherbase(testBankAddress) return w, backend } diff --git a/node/config.go b/node/config.go index bc8679756a..8e18fc7d62 100644 --- a/node/config.go +++ b/node/config.go @@ -104,6 +104,9 @@ type Config struct { // EnableQuickBlockFetching indicates whether to fetch new blocks using new messages. EnableQuickBlockFetching bool `toml:",omitempty"` + // EnableBAL enables the block access list feature + EnableBAL bool `toml:",omitempty"` + // RangeLimit enable 5000 blocks limit when handle range query RangeLimit bool `toml:",omitempty"` diff --git a/p2p/config.go b/p2p/config.go index 15ee29e1e6..932dcc8adf 100644 --- a/p2p/config.go +++ b/p2p/config.go @@ -98,6 +98,10 @@ type Config struct { // it usually used for sentry nodes ProxyedValidatorAddresses []common.Address `toml:",omitempty"` + // ProxyedNodeIds lists node IDs that receive direct broadcasts of blocks and votes, + // excluding transactions, to prevent delays in block and vote propagation. + ProxyedNodeIds []enode.ID `toml:",omitempty"` + // Connectivity can be restricted to certain IP networks. // If this option is set to a non-nil value, only hosts which match one of the // IP networks contained in the list are considered. diff --git a/p2p/config_toml.go b/p2p/config_toml.go index d4acda59ee..c331a92ad4 100644 --- a/p2p/config_toml.go +++ b/p2p/config_toml.go @@ -33,6 +33,7 @@ func (c Config) MarshalTOML() (interface{}, error) { TrustedNodes []*enode.Node EVNNodeIdsWhitelist []enode.ID `toml:",omitempty"` ProxyedValidatorAddresses []common.Address `toml:",omitempty"` + ProxyedNodeIds []enode.ID `toml:",omitempty"` NetRestrict *netutil.Netlist `toml:",omitempty"` NodeDatabase string `toml:",omitempty"` Protocols []Protocol `toml:"-" json:"-"` @@ -62,6 +63,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.TrustedNodes = c.TrustedNodes enc.EVNNodeIdsWhitelist = c.EVNNodeIdsWhitelist enc.ProxyedValidatorAddresses = c.ProxyedValidatorAddresses + enc.ProxyedNodeIds = c.ProxyedNodeIds enc.NetRestrict = c.NetRestrict enc.NodeDatabase = c.NodeDatabase enc.Protocols = c.Protocols @@ -95,6 +97,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { TrustedNodes []*enode.Node EVNNodeIdsWhitelist []enode.ID `toml:",omitempty"` ProxyedValidatorAddresses []common.Address `toml:",omitempty"` + ProxyedNodeIds []enode.ID `toml:",omitempty"` NetRestrict *netutil.Netlist `toml:",omitempty"` NodeDatabase *string `toml:",omitempty"` Protocols []Protocol `toml:"-" json:"-"` @@ -159,6 +162,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.ProxyedValidatorAddresses != nil { c.ProxyedValidatorAddresses = dec.ProxyedValidatorAddresses } + if dec.ProxyedNodeIds != nil { + c.ProxyedNodeIds = dec.ProxyedNodeIds + } if dec.NetRestrict != nil { c.NetRestrict = dec.NetRestrict } diff --git a/p2p/peer.go b/p2p/peer.go index e59a547fed..f9e9d424da 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -129,6 +129,12 @@ type Peer struct { // it indicates the peer is in the validator network, it will directly broadcast when miner/sentry broadcast mined block, // and won't broadcast any txs between EVN peers. EVNPeerFlag atomic.Bool + + // Indicates whether this peer is proxyed. + ProxyedPeerFlag atomic.Bool + + // it indicates the peer can handle BAL(block access list) packet + CanHandleBAL atomic.Bool } // NewPeer returns a peer for testing purposes. @@ -645,7 +651,7 @@ func (p *Peer) Info() *PeerInfo { info.Network.RemoteAddress = p.RemoteAddr().String() info.Network.Inbound = p.rw.is(inboundConn) // After Maxwell, we treat all EVN peers as trusted - info.Network.Trusted = p.rw.is(trustedConn) || p.EVNPeerFlag.Load() + info.Network.Trusted = p.rw.is(trustedConn) || p.EVNPeerFlag.Load() || p.ProxyedPeerFlag.Load() info.Network.Static = p.rw.is(staticDialedConn) // Gather all the running protocol infos diff --git a/params/config.go b/params/config.go index 7532767c63..6c107231a2 100644 --- a/params/config.go +++ b/params/config.go @@ -272,7 +272,7 @@ var ( PragueTime: newUint64(1740452880), // 2025-02-25 03:08:00 AM UTC LorentzTime: newUint64(1744097580), // 2025-04-08 07:33:00 AM UTC MaxwellTime: newUint64(1748243100), // 2025-05-26 07:05:00 AM UTC - FermiTime: nil, + FermiTime: newUint64(1762741500), // 2025-11-10 02:25:00 AM UTC Parlia: &ParliaConfig{}, BlobScheduleConfig: &BlobScheduleConfig{ diff --git a/params/network_params.go b/params/network_params.go index 804336ccb8..ae5ca01c2d 100644 --- a/params/network_params.go +++ b/params/network_params.go @@ -22,6 +22,9 @@ package params const ( // StableStateThreshold is the reserve number of block state save to disk before delete ancientdb StableStateThreshold uint64 = 128 + + // MaxBALSize is the maximum bytes of the rlp encoded block access list: 1MB + MaxBALSize uint64 = 1048576 ) var ( @@ -29,5 +32,5 @@ var ( // considered immutable (i.e. soft finality). It is used by the downloader as a // hard limit against deep ancestors, by the blockchain against deep reorgs, by // the freezer as the cutoff threshold and by clique as the snapshot trust limit. - FullImmutabilityThreshold uint64 = 360_000 + FullImmutabilityThreshold uint64 = 600_000 ) diff --git a/params/protocol_params.go b/params/protocol_params.go index 89b8c966ce..3ec5bfd950 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -192,10 +192,10 @@ const ( ) var ( - // maxwellBlockInterval = 0.75 + // fermiBlockInterval = 0.45 MinTimeDurationForBlobRequests uint64 = uint64(float64(24*3600) * 18.2) // it keeps blob data available for 18.2 days in local - MinBlocksForBlobRequests uint64 = uint64(float64(MinTimeDurationForBlobRequests) / 0.75) // ref: https://github.com/bnb-chain/BEPs/blob/master/BEPs/BEP-524.md#421-change-table. - DefaultExtraReserveForBlobRequests uint64 = uint64(24 * 3600 / 0.75) // it adds more time for expired blobs for some request cases, like expiry blob when remote peer is syncing, default 1 day. + MinBlocksForBlobRequests uint64 = uint64(float64(MinTimeDurationForBlobRequests) / 0.45) // ref: https://github.com/bnb-chain/BEPs/blob/master/BEPs/BEP-524.md#421-change-table. + DefaultExtraReserveForBlobRequests uint64 = uint64(24 * 3600 / 0.45) // it adds more time for expired blobs for some request cases, like expiry blob when remote peer is syncing, default 1 day. BreatheBlockInterval uint64 = 24 * 3600 // Controls the interval for updateValidatorSetV2 diff --git a/rpc/json-rpc-api.md b/rpc/json-rpc-api.md index 4a398bf07a..fcc173450f 100644 --- a/rpc/json-rpc-api.md +++ b/rpc/json-rpc-api.md @@ -43,9 +43,9 @@ This document provides a comprehensive list of JSON-RPC API methods supported. E | eth_getTransactionByHash(...) | `TxHash` | | eth_getRawTransactionByHash(...) | `TxHash` | | eth_getTransactionByBlockHashAndIndex(...) | `BlockHash`, `Integer` | -| eth_retRawTransactionByBlockHashAndIndex(...) | `BlockHash`, `Integer` | +| eth_getRawTransactionByBlockHashAndIndex(...) | `BlockHash`, `Integer` | | eth_getTransactionByBlockNumberAndIndex(...) | `BlockNumber\|Tag`, `Integer` | -| eth_retRawTransactionByBlockNumberAndIndex(...) | `BlockNumber\|Tag`, `Integer` | +| eth_getRawTransactionByBlockNumberAndIndex(...) | `BlockNumber\|Tag`, `Integer` | | eth_getTransactionReceipt(...) | `TxHash` | | eth_getBlockReceipts(...) | `BlockNumber\|Tag` | | | | diff --git a/tests/truffle/config/config-validator.toml b/tests/truffle/config/config-validator.toml index b36bfa8330..a3844284c4 100644 --- a/tests/truffle/config/config-validator.toml +++ b/tests/truffle/config/config-validator.toml @@ -11,7 +11,7 @@ TrieTimeout = 6000000000 EnablePreimageRecording = false [Eth.Miner] -GasCeil = 75000000 +GasCeil = 100000000 GasPrice = 100000000 [Eth.GPO] diff --git a/triedb/database.go b/triedb/database.go index 661fe1b427..46619b1a0c 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -441,3 +441,50 @@ func (db *Database) IsVerkle() bool { func (db *Database) Disk() ethdb.Database { return db.disk } + +// MergeIncrState merges the state in incremental snapshot into base snapshot +func (db *Database) MergeIncrState(incrDir string) error { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + log.Error("Not supported") + return nil + } + return pdb.MergeIncrState(incrDir) +} + +// WriteContractCodes used to write contract codes into incremental db. +func (db *Database) WriteContractCodes(codes map[common.Address]rawdb.ContractCode) error { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + log.Error("Not supported") + return errors.New("not supported WriteContractCodes") + } + return pdb.WriteContractCodes(codes) +} + +// IsIncrEnabled returns true if incremental is enabled, otherwise false. +func (db *Database) IsIncrEnabled() bool { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + return false + } + return pdb.IsIncrEnabled() +} + +// SetStateGenerator is used to set state generator. +func (db *Database) SetStateGenerator() { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + return + } + pdb.SetStateGenerator() +} + +// RepairIncrStore is used to repair incr store. +func (db *Database) GetStartBlock() (uint64, error) { + pdb, ok := db.backend.(*pathdb.Database) + if !ok { + return 0, errors.New("not supported GetStartBlock") + } + return pdb.GetStartBlock() +} diff --git a/triedb/pathdb/async_incr_state.go b/triedb/pathdb/async_incr_state.go new file mode 100644 index 0000000000..e4477e6708 --- /dev/null +++ b/triedb/pathdb/async_incr_state.go @@ -0,0 +1,509 @@ +package pathdb + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie/trienode" +) + +// asyncIncrStateBuffer writes the incremental state trie nodes into incr state db. +type asyncIncrStateBuffer struct { + mux sync.RWMutex + current *incrNodeBuffer + background *incrNodeBuffer + + truncateChan chan uint64 + flushedStateID atomic.Uint64 + + isFlushing atomic.Bool + stopFlushing atomic.Bool + done chan struct{} +} + +// newAsyncIncrStateBuffer initializes the async incremental state buffer. +func newAsyncIncrStateBuffer(limit, batchSize uint64) *asyncIncrStateBuffer { + b := &asyncIncrStateBuffer{ + current: newIncrNodeBuffer(limit, batchSize, nil, nil, 0), + background: newIncrNodeBuffer(limit, batchSize, nil, nil, 0), + done: make(chan struct{}), + truncateChan: make(chan uint64, 1), + } + + // Start monitoring goroutine + go b.monitorStateBuffer() + + return b +} + +// monitorStateBuffer monitors the state buffer every 5 minutes +func (a *asyncIncrStateBuffer) monitorStateBuffer() { + ticker := time.NewTicker(5 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + a.printBufferInfo() + case <-a.done: + log.Debug("Monitor buffer stopped due to done signal") + return + } + } +} + +// printBufferInfo prints detailed info about both current and background buffer +func (a *asyncIncrStateBuffer) printBufferInfo() { + a.mux.RLock() + defer a.mux.RUnlock() + + log.Info("Current buffer Status", "empty", a.current.empty(), "full", a.current.full(), + "totalSize", common.StorageSize(a.current.size()), "nodesSize", common.StorageSize(a.current.nodes.size), + "statesSize", common.StorageSize(a.current.states.size), "layers", a.current.layers, + "immutable", atomic.LoadUint64(&a.current.immutable) == 1, + "stateIDRange", fmt.Sprintf("%d-%d", a.current.stateIDArray[0], a.current.stateIDArray[1]), + "blockNumberRange", fmt.Sprintf("%d-%d", a.current.blockNumberArray[0], a.current.blockNumberArray[1]), + "limit", common.StorageSize(a.current.limit), "batchSize", common.StorageSize(a.current.batchSize)) + + log.Info("Background buffer Status", "empty", a.background.empty(), "full", a.background.full(), + "totalSize", common.StorageSize(a.background.size()), "nodesSize", common.StorageSize(a.background.nodes.size), + "statesSize", common.StorageSize(a.background.states.size), "layers", a.background.layers, + "immutable", atomic.LoadUint64(&a.background.immutable) == 1, + "stateIDRange", fmt.Sprintf("%d-%d", a.background.stateIDArray[0], a.background.stateIDArray[1]), + "blockNumberRange", fmt.Sprintf("%d-%d", a.background.blockNumberArray[0], a.background.blockNumberArray[1]), + "limit", common.StorageSize(a.current.limit), "batchSize", common.StorageSize(a.current.batchSize)) +} + +// commit merges the provided states and trie nodes into the buffer. +func (a *asyncIncrStateBuffer) commit(root common.Hash, nodes *nodeSet, states *stateSet, stateID, blockNumber uint64) *asyncIncrStateBuffer { + a.mux.Lock() + defer a.mux.Unlock() + + err := a.current.commit(root, nodes, states, stateID, blockNumber) + if err != nil { + log.Crit("Failed to commit nodes to async incremental state buffer", "error", err) + } + return a +} + +// empty returns an indicator if buffer contains any state transition inside. +func (a *asyncIncrStateBuffer) empty() bool { + a.mux.RLock() + defer a.mux.RUnlock() + + return a.current.empty() && a.background.empty() +} + +func (a *asyncIncrStateBuffer) getFlushedStateID() uint64 { + old := a.flushedStateID.Load() + a.flushedStateID.Store(0) + return old +} + +// flush persists the in-memory trie nodes to ancient db if the memory threshold is reached. +func (a *asyncIncrStateBuffer) flush(incrDB *rawdb.IncrSnapDB, force bool) error { + a.mux.Lock() + defer a.mux.Unlock() + + if a.stopFlushing.Load() { + return nil + } + + if force { + for { + if atomic.LoadUint64(&a.background.immutable) == 1 { + log.Info("Waiting background incr state buffer flushed to disk for forcing flush") + time.Sleep(3 * time.Second) + continue + } + atomic.StoreUint64(&a.current.immutable, 1) + a.flushedStateID.Store(a.current.stateIDArray[1]) + return a.current.flush(incrDB) + } + } + + if !a.current.full() { + return nil + } + + if atomic.LoadUint64(&a.background.immutable) == 1 { + return nil + } + + atomic.StoreUint64(&a.current.immutable, 1) + a.current, a.background = a.background, a.current + + a.isFlushing.Store(true) + go func() { + defer a.isFlushing.Store(false) + for { + err := a.background.flush(incrDB) + if err == nil { + log.Info("Successfully flushed incremental state buffer to ancient db") + a.forwardTruncateSignal(a.background) + return + } + log.Error("Failed to flush incremental state buffer to ancient db", "error", err) + } + }() + + return nil +} + +// waitAndStopFlushing waits for ongoing flush operations to complete and stops flushing +func (a *asyncIncrStateBuffer) waitAndStopFlushing() { + // Stop monitoring goroutine immediately using done channel + close(a.done) + + // Stop flushing operations + a.stopFlushing.Store(true) + + // Wait for flush operations to complete + for a.isFlushing.Load() { + time.Sleep(time.Second) + log.Warn("Waiting for incremental state buffer flush to complete") + } +} + +// getTruncateSignal returns the truncate signal channel +func (a *asyncIncrStateBuffer) getTruncateSignal() <-chan uint64 { + return a.truncateChan +} + +// forwardTruncateSignal forwards truncate signal from buffer to truncate channel +func (a *asyncIncrStateBuffer) forwardTruncateSignal(buffer *incrNodeBuffer) { + select { + case lastStateID := <-buffer.getTruncateSignal(): + select { + case a.truncateChan <- lastStateID: + log.Info("Forwarded truncate signal", "stateID", lastStateID) + default: + log.Debug("Truncate channel full, skipping signal") + } + default: + } +} + +// incrNodeBuffer is a specialized buffer for incremental trie nodes +type incrNodeBuffer struct { + *buffer + root common.Hash + batchSize uint64 // Maximum flush batch size + stateIDArray [2]uint64 + blockNumberArray [2]uint64 + immutable uint64 // atomic flag: 1 = immutable, 0 = mutable + truncateSignal chan uint64 +} + +var emptyArray = [2]uint64{0, 0} + +// newIncrNodeBuffer creates a new incremental node buffer +func newIncrNodeBuffer(limit, batchSize uint64, nodes *nodeSet, states *stateSet, layers uint64) *incrNodeBuffer { + return &incrNodeBuffer{ + buffer: newBuffer(int(limit), nodes, states, layers), + batchSize: batchSize, + stateIDArray: emptyArray, + blockNumberArray: emptyArray, + immutable: 0, + truncateSignal: make(chan uint64, 1), + } +} + +// commit adds nodes and states to the buffer +func (c *incrNodeBuffer) commit(root common.Hash, nodes *nodeSet, states *stateSet, stateID, blockNumber uint64) error { + if atomic.LoadUint64(&c.immutable) == 1 { + return fmt.Errorf("cannot commit to immutable cache") + } + + c.buffer.commit(nodes, states) + c.root = root + if c.stateIDArray[0] == 0 && c.stateIDArray[1] == 0 { + c.stateIDArray[0] = stateID + c.stateIDArray[1] = stateID + c.blockNumberArray[0] = blockNumber + c.blockNumberArray[1] = blockNumber + } else { + c.stateIDArray[1] = stateID + c.blockNumberArray[1] = blockNumber + } + return nil +} + +// flush writes the immutable buffer to the incremental state db. +func (c *incrNodeBuffer) flush(incrDB *rawdb.IncrSnapDB) error { + if atomic.LoadUint64(&c.immutable) != 1 { + return fmt.Errorf("cannot flush mutable cache") + } + + if err := c.flushTrieNodes(incrDB); err != nil { + return err + } + if err := c.flushStates(incrDB); err != nil { + return err + } + + c.resetIncrBuffer() + return nil +} + +func (c *incrNodeBuffer) flushStates(incrDB *rawdb.IncrSnapDB) error { + var acc accounts + var storages []storage + currentSize := uint64(0) + + // Helper function to write current batch and reset + writeBatch := func() error { + if len(acc.AddrHashes) == 0 && len(storages) == 0 { + return nil + } + + s := statesData{ + RawStorageKey: c.states.rawStorageKey, + Acc: acc, + Storages: storages, + } + + if err := c.writeStatesToAncientDB(incrDB, s); err != nil { + return err + } + + // Reset for next batch + acc = accounts{} + storages = make([]storage, 0) + currentSize = 0 + return nil + } + + // Process account data + for addrHash, blob := range c.states.accountData { + accountSize := uint64(len(addrHash[:]) + len(blob)) + + if currentSize+accountSize > c.batchSize && (len(acc.AddrHashes) > 0 || len(storages) > 0) { + if err := writeBatch(); err != nil { + return err + } + } + + acc.AddrHashes = append(acc.AddrHashes, addrHash) + acc.Accounts = append(acc.Accounts, blob) + currentSize += accountSize + } + + // Process storage data + for addrHash, slots := range c.states.storageData { + keys := make([]common.Hash, 0, len(slots)) + vals := make([][]byte, 0, len(slots)) + storageSize := uint64(len(addrHash[:])) + + for key, val := range slots { + slotSize := uint64(len(key[:]) + len(val)) + + if currentSize+storageSize+slotSize > c.batchSize && (len(acc.AddrHashes) > 0 || len(storages) > 0 || len(keys) > 0) { + // Finish current storage if we have keys + if len(keys) > 0 { + storages = append(storages, storage{ + AddrHash: addrHash, + Keys: keys, + Vals: vals, + }) + } + + if err := writeBatch(); err != nil { + return err + } + + // Start new storage + keys = make([]common.Hash, 0, len(slots)) + vals = make([][]byte, 0, len(slots)) + storageSize = uint64(len(addrHash[:])) + } + + keys = append(keys, key) + vals = append(vals, val) + storageSize += slotSize + } + + if len(keys) > 0 { + storages = append(storages, storage{ + AddrHash: addrHash, + Keys: keys, + Vals: vals, + }) + currentSize += storageSize + } + } + + // Write remaining data + return writeBatch() +} + +func (c *incrNodeBuffer) flushTrieNodes(incrDB *rawdb.IncrSnapDB) error { + jn := make([]journalNodes, 0, len(c.nodes.storageNodes)+1) + totalSize := uint64(0) + + processNodes := func(owner common.Hash, nodes map[string]*trienode.Node) error { + entry := journalNodes{Owner: owner} + ownerSize := uint64(len(owner[:])) + nodesListSize := uint64(0) + + for path, node := range nodes { + entry.Nodes = append(entry.Nodes, journalNode{Path: []byte(path), Blob: node.Blob}) + + nodeSize := uint64(len([]byte(path)) + len(node.Blob)) + nodesListSize += nodeSize + currentEntrySize := ownerSize + nodesListSize + newTotalSize := totalSize + currentEntrySize + + if newTotalSize >= c.batchSize { + log.Info("Batch size limit reached during node iteration, flushing nodes to ancient db", + "newTotalSize", common.StorageSize(newTotalSize), "entryCount", len(jn)+1) + if err := c.writeTrieNodesToAncientDB(incrDB, append(jn, entry)); err != nil { + return err + } + + jn = make([]journalNodes, 0, len(c.nodes.storageNodes)+1) + totalSize = 0 + entry = journalNodes{Owner: owner} // Reset entry for remaining nodes + ownerSize = uint64(len(owner[:])) // Reset owner size + nodesListSize = 0 + } + } + + if len(entry.Nodes) > 0 { + jn = append(jn, entry) + entrySize := ownerSize + nodesListSize + totalSize += entrySize + } + + if totalSize >= c.batchSize { + log.Info("Batch size limit reached after adding entry, flushing nodes to ancient db", + "totalSize", common.StorageSize(totalSize), "entryCount", len(jn)) + if err := c.writeTrieNodesToAncientDB(incrDB, jn); err != nil { + return err + } + jn = make([]journalNodes, 0, len(c.nodes.storageNodes)+1) + totalSize = 0 + } + return nil + } + + if len(c.nodes.accountNodes) > 0 { + if err := processNodes(common.Hash{}, c.nodes.accountNodes); err != nil { + return err + } + } + for owner, subset := range c.nodes.storageNodes { + if err := processNodes(owner, subset); err != nil { + return err + } + } + + // Flush remaining nodes and states + if len(jn) > 0 { + log.Info("Flushing remaining trie nodes to ancient db", "entryCount", len(jn)) + if err := c.writeTrieNodesToAncientDB(incrDB, jn); err != nil { + return err + } + } + + log.Info("Flushed incremental state buffer to ancient db", "size", common.StorageSize(c.nodes.size)) + return nil +} + +// writeTrieNodesToAncientDB writes a batch of trie nodes to the incremental state db. +func (c *incrNodeBuffer) writeTrieNodesToAncientDB(incrDB *rawdb.IncrSnapDB, jn []journalNodes) error { + if len(jn) == 0 { + return nil + } + + ancients, _ := incrDB.GetStateFreezer().Ancients() + incrementalID := ancients + 1 + + encodedBatch, err := rlp.EncodeToBytes(jn) + if err != nil { + return fmt.Errorf("failed to RLP encode trie node batch: %v", err) + } + m := rawdb.IncrStateMetadata{ + Root: c.root, + HasStates: false, + NodeCount: uint64(len(jn)), + Layers: c.layers, + StateIDArray: c.stateIDArray, + BlockNumberArray: c.blockNumberArray, + } + metaBytes, err := rlp.EncodeToBytes(m) + if err != nil { + return fmt.Errorf("failed to RLP encode metadata: %v", err) + } + + if err = incrDB.WriteIncrTrieNodes(incrementalID, metaBytes, encodedBatch); err != nil { + log.Error("Failed to write incr trie nodes", "error", err, "ancients", ancients, + "incrementalID", incrementalID) + return err + } + + select { + case c.truncateSignal <- c.stateIDArray[1]: + log.Debug("Sent truncate signal after WriteIncrState", "incrementalID", incrementalID) + default: + log.Debug("Truncate signal channel full, skipping signal") + } + + log.Info("Wrote incr trie nodes to ancient db", "incrementalID", incrementalID, "nodeCount", len(jn), + "layers", c.layers, "nodesSize", common.StorageSize(len(encodedBatch)), "stateIDArray", c.stateIDArray, + "blockNumberArray", c.blockNumberArray) + return nil +} + +// writeStatesToAncientDB writes a batch of states to the incremental state db. +func (c *incrNodeBuffer) writeStatesToAncientDB(incrDB *rawdb.IncrSnapDB, s statesData) error { + ancients, _ := incrDB.GetStateFreezer().Ancients() + incrementalID := ancients + 1 + + encodedBatch, err := rlp.EncodeToBytes(s) + if err != nil { + return fmt.Errorf("failed to RLP encode trie node batch: %v", err) + } + m := rawdb.IncrStateMetadata{ + Root: c.root, + HasStates: true, + NodeCount: 0, + Layers: c.layers, + StateIDArray: c.stateIDArray, + BlockNumberArray: c.blockNumberArray, + } + metaBytes, err := rlp.EncodeToBytes(m) + if err != nil { + return fmt.Errorf("failed to RLP encode metadata: %v", err) + } + + if err = incrDB.WriteIncrState(incrementalID, metaBytes, encodedBatch); err != nil { + log.Error("Failed to write incr state", "error", err, "ancients", ancients, + "incrementalID", incrementalID) + return err + } + + log.Info("Wrote incr state batch to ancient db", "incrementalID", incrementalID, + "statesSize", common.StorageSize(len(encodedBatch))) + return nil +} + +// resetIncrBuffer resets the incr buffer +func (c *incrNodeBuffer) resetIncrBuffer() { + atomic.StoreUint64(&c.immutable, 0) + c.reset() + c.root = common.Hash{} + c.stateIDArray = emptyArray + c.blockNumberArray = emptyArray +} + +// getTruncateSignal returns the truncate signal channel +func (c *incrNodeBuffer) getTruncateSignal() <-chan uint64 { + return c.truncateSignal +} diff --git a/triedb/pathdb/buffer.go b/triedb/pathdb/buffer.go index 138962110f..0bc30951f7 100644 --- a/triedb/pathdb/buffer.go +++ b/triedb/pathdb/buffer.go @@ -207,3 +207,37 @@ func (b *buffer) waitFlush() error { <-b.done return b.flushErr } + +// flushIncrSnapshot persists incr trie nodes and states to disk. +func (b *buffer) flushIncrSnapshot(root common.Hash, db ethdb.KeyValueStore, freezer ethdb.AncientWriter, progress []byte, + id uint64) error { + var ( + start = time.Now() + batch = db.NewBatchWithSize((b.nodes.dbsize() + b.states.dbsize()) * 11 / 10) // extra 10% for potential pebble internal stuff + ) + if freezer != nil { + if err := freezer.SyncAncient(); err != nil { + return err + } + } + var nodes, accs, slots int + if b.nodes != nil { + nodes = b.nodes.write(batch, nil) + rawdb.WritePersistentStateID(batch, id) + } + if b.states != nil { + accs, slots = b.states.write(batch, progress, nil) + rawdb.WriteSnapshotRoot(batch, root) + } + + // Flush all mutations in a single batch + size := batch.ValueSize() + if err := batch.Write(); err != nil { + return err + } + + b.reset() + log.Info("Persisted buffer content", "nodes", nodes, "accounts", accs, "slots", slots, + "bytes", common.StorageSize(size), "elapsed", common.PrettyDuration(time.Since(start))) + return nil +} diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 0f6280fb8b..71ea3413ba 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -22,11 +22,13 @@ import ( "fmt" "io" "os" + "path/filepath" "sort" "strconv" "sync" "time" + "github.com/cockroachdb/pebble" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -140,8 +142,15 @@ type Config struct { NoAsyncFlush bool // Flag whether the background buffer flushing is allowed NoAsyncGeneration bool // Flag whether the background generation is allowed - JournalFilePath string - JournalFile bool + JournalFilePath string // The path of journal file + JournalFile bool // Flag whether store memory diffLayer into file + + EnableIncr bool // Flag whether the freezer db stores incr block and state history + MergeIncr bool // Flag to merge incr snapshots + IncrHistory uint64 // Amount of block and state history stored in incr freezer db + IncrHistoryPath string // The path to store incr block and chain files + IncrStateBuffer uint64 // Maximum memory allowance (in bytes) for incr state buffer + IncrKeptBlocks uint64 // Amount of block kept in incr snapshot } // sanitize checks the provided user configurations and changes anything that's @@ -237,6 +246,7 @@ type Database struct { freezer ethdb.ResettableAncientStore // Freezer for storing trie histories, nil possible in tests lock sync.RWMutex // Lock to prevent mutations from happening at the same time indexer *historyIndexer // History indexer + incr *incrManager // used to store incremental data: block, state and contract codes } // New attempts to load an already existing layer from a persistent key-value @@ -273,6 +283,16 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { if err := db.repairHistory(); err != nil { log.Crit("Failed to repair state history", "err", err) } + + if db.config.EnableIncr { + db.checkIncrConfig() + if err := db.repairIncrStore(); err != nil { + log.Crit("Failed to repair incremental history", "error", err) + } + // Start incremental store async workers + db.incr.Start() + } + // Disable database in case node is still in the initial state sync stage. if rawdb.ReadSnapSyncStatusFlag(diskdb) == rawdb.StateSyncRunning && !db.readOnly { if err := db.Disable(); err != nil { @@ -283,8 +303,10 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { // mandatory. This ensures that uncovered flat states are not accessed, // even if background generation is not allowed. If permitted, the generation // might be scheduled. - if err := db.setStateGenerator(); err != nil { - log.Crit("Failed to setup the generator", "err", err) + if !config.MergeIncr { + if err := db.setStateGenerator(); err != nil { + log.Crit("Failed to setup the generator", "err", err) + } } // TODO (rjl493456442) disable the background indexing in read-only mode if db.freezer != nil && db.config.EnableStateIndexing { @@ -299,6 +321,13 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { return db } +// SetStateGenerator sets state generator. +func (db *Database) SetStateGenerator() { + if err := db.setStateGenerator(); err != nil { + log.Crit("Failed to setup the generator", "err", err) + } +} + // repairHistory truncates leftover state history objects, which may occur due // to an unclean shutdown or other unexpected reasons. func (db *Database) repairHistory() error { @@ -413,6 +442,92 @@ func (db *Database) setStateGenerator() error { return nil } +func (db *Database) checkIncrConfig() { + ancientDir, err := db.diskdb.AncientDatadir() + if err != nil { + log.Crit("Failed to get ancient data dir", "err", err) + } + + if db.config.IncrHistoryPath == "" { + db.config.IncrHistoryPath = filepath.Join(ancientDir, rawdb.IncrementalPath) + } + if db.config.IncrHistory == 0 { + db.config.IncrHistory = 100000 + } + if db.config.IncrStateBuffer == 0 { + db.config.IncrStateBuffer = DefaultIncrStateBufferSize + } + if db.config.IncrKeptBlocks < DefaultKeptBlocks { + db.config.IncrKeptBlocks = DefaultKeptBlocks + } else { + if db.config.IncrKeptBlocks > db.config.IncrHistory { + db.config.IncrKeptBlocks = db.config.IncrHistory + log.Warn("IncrKeptBlocks shouldn't be greater than IncrHistory", "IncrHistory", db.config.IncrHistory, + "IncrKeptBlocks", db.config.IncrKeptBlocks) + } + } + + log.Info("Incr snapshot config", "IncrHistoryPath", db.config.IncrHistoryPath, "IncrHistory", db.config.IncrHistory, + "IncrStateBuffer", common.StorageSize(db.config.IncrStateBuffer), "IncrKeptBlocks", db.config.IncrKeptBlocks) +} + +// repairIncrStore init incremental manager and align incr chain and state freezer. +func (db *Database) repairIncrStore() error { + if err := db.initIncrManager(); err != nil { + log.Error("Failed to initialize incr manager", "error", err) + return err + } + + // Get disk layer state ID for validation + diskLayerID := db.tree.bottom().stateID() + if diskLayerID == 0 { + stateAncients, err := db.incr.incrDB.GetStateFreezer().Ancients() + if err != nil { + log.Error("Failed to retrieve head of incr state history", "error", err) + return err + } + + if stateAncients != 0 { + block, err := db.GetStartBlock() + if err != nil { + log.Error("Failed to retrieve start block", "error", err) + return err + } + if err = db.incr.incrDB.ResetAllIncr(block); err != nil { + log.Error("Failed to reset incremental state histories", "error", err) + return err + } + log.Warn("Reset all incremental state histories") + } + return nil + } + + // Align incremental data with disk layer + return db.alignIncrData(diskLayerID) +} + +func (db *Database) GetStartBlock() (uint64, error) { + var block uint64 + if dl := db.tree.bottomDiffLayer(); dl != nil { + // use the bottom diff layer block number + block = dl.block + } else if db.tree.bottom() != nil { + // force kill case, use the block next to the disk layer block + disk := db.tree.bottom() + var m meta + blob := rawdb.ReadStateHistoryMeta(db.freezer, disk.id) + if err := m.decode(blob); err != nil { + log.Error("Failed to decode state histories", "err", err) + return 0, err + } + block = m.block + 1 + } else { + // start from genesis + block = 1 + } + return block, nil +} + // Update adds a new layer into the tree, if that can be linked to an existing // old parent. It is disallowed to insert a disk layer (the origin of all). Apart // from that this function will flatten the extra diff layers at bottom into disk @@ -668,6 +783,23 @@ func (db *Database) Close() error { if db.freezer == nil { return nil } + + if db.config.EnableIncr { + log.Info("Closing incremental store") + + // Wait for all async write tasks to complete before closing + if db.incr != nil { + log.Info("Waiting for async write tasks to complete", "pending", db.incr.GetQueueLength()) + db.incr.LogStats() + db.incr.Stop() + } + + if err := db.incr.incrDB.Close(); err != nil { + log.Error("Failed to close incremental db", "err", err) + return err + } + } + return db.freezer.Close() } @@ -836,3 +968,363 @@ func (db *Database) StorageIterator(root common.Hash, account common.Hash, seek } return newFastStorageIterator(db, root, account, seek) } + +// IsIncrEnabled returns true if incremental is enabled, otherwise false. +func (db *Database) IsIncrEnabled() bool { + return db.config.EnableIncr +} + +// MergeIncrState merges incremental state data into local data. +func (db *Database) MergeIncrState(incrDir string) error { + incrStateFreezer, err := rawdb.OpenIncrStateFreezer(incrDir, true) + if err != nil { + log.Error("Failed to open incremental state freezer", "error", err) + return err + } + defer incrStateFreezer.Close() + + incrAncients, _ := incrStateFreezer.Ancients() + tail, _ := incrStateFreezer.Tail() + log.Info("Merged incr state freezer info", "ancients", incrAncients, "tail", tail) + + incrStateMeta := rawdb.ReadIncrStateHistoryMeta(incrStateFreezer, incrAncients) + if incrStateMeta == nil { + log.Error("Failed to read incremental chain freezer", "error", err) + return err + } + if err = rawdb.ResetStateTableToNewStartPoint(db.freezer, incrStateMeta.StateIDArray[1]); err != nil { + log.Error("Failed to reset state freezer with new start point", "error", err, + "lastStateID", incrStateMeta.StateIDArray[1]) + return err + } + + dl := db.tree.bottom() + err = dl.mergeIncrNodesWithStates(db.diskdb, db.freezer, incrStateFreezer, tail+1, incrAncients) + if err != nil { + log.Error("Failed to merge incremental trie nodes", "error", err) + return err + } + + root, err := db.hasher(rawdb.ReadAccountTrieNode(db.diskdb, nil)) + if err != nil { + log.Crit("Failed to compute node hash", "err", err) + } + dl = newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0), nil) + db.tree = newLayerTree(dl) + log.Info("Completed merging incr state") + return nil +} + +// WriteContractCodes wrote codes into incremental chain freezer +func (db *Database) WriteContractCodes(codes map[common.Address]rawdb.ContractCode) error { + return db.incr.incrDB.WriteIncrContractCodes(codes) +} + +// incrInfo holds information about incremental data state +type incrInfo struct { + stateFreezer ethdb.ResettableAncientStore + chainFreezer ethdb.ResettableAncientStore + stateAncients uint64 + chainAncients uint64 + lastChainStateID uint64 + lastStateID uint64 + lastStateBlock uint64 +} + +func (info *incrInfo) isEmpty() bool { + return info.stateAncients == 0 || info.chainAncients == 0 +} + +// initIncrManager initializes the incremental manager +func (db *Database) initIncrManager() error { + block, err := db.GetStartBlock() + if err != nil { + return err + } + + incrDB, err := rawdb.NewIncrSnapDB(db.config.IncrHistoryPath, db.readOnly, block, db.config.IncrHistory) + if err != nil { + log.Error("Failed to open incremental db", "error", err) + return err + } + + db.incr = NewIncrManager(db, incrDB) + return nil +} + +// loadIncrInfo loads current incremental data information +func (db *Database) loadIncrInfo() (*incrInfo, error) { + info := &incrInfo{} + + info.stateFreezer = db.incr.incrDB.GetStateFreezer() + info.chainFreezer = db.incr.incrDB.GetChainFreezer() + + var err error + info.stateAncients, err = info.stateFreezer.Ancients() + if err != nil { + log.Error("Failed to retrieve head of incr state history", "error", err) + return nil, err + } + info.chainAncients, err = info.chainFreezer.Ancients() + if err != nil { + log.Error("Failed to retrieve head of incr chain history", "error", err) + return nil, err + } + + // Load last state info if data exists + if !info.isEmpty() { + // Read last chain state ID + info.lastChainStateID, err = rawdb.ReadIncrChainMapping(info.chainFreezer, info.chainAncients-1) + if err != nil { + log.Error("Failed to read incr chain mapping", "error", err) + return nil, err + } + + // Read last state metadata + metadata := rawdb.ReadIncrStateHistoryMeta(info.stateFreezer, info.stateAncients) + if metadata == nil { + return nil, fmt.Errorf("last incr state history not found: %d", info.stateAncients) + } + + info.lastStateID = metadata.StateIDArray[1] + info.lastStateBlock = metadata.BlockNumberArray[1] + log.Info("Incr data info", "lastChainStateID", info.lastChainStateID, + "lastStateID", info.lastStateID, "lastChainBlock", info.chainAncients-1, + "lastStateBlock", info.lastStateBlock) + } + + return info, nil +} + +func (db *Database) alignIncrData(diskLayerID uint64) error { + // Load current incremental data info + info, err := db.loadIncrInfo() + if err != nil { + return err + } + + var recordFirstStateID uint64 + data, err := db.incr.incrDB.GetKVDB().Get(rawdb.FirstStateID) + if err != nil { + if errors.Is(err, pebble.ErrNotFound) { + db.incr.incrDB.WriteFirstStateID(diskLayerID) + recordFirstStateID = diskLayerID + } else { + return err + } + } else { + recordFirstStateID = binary.BigEndian.Uint64(data) + } + + // Get start block to avoid duplicate data writing + startBlock, err := db.GetStartBlock() + if err != nil { + log.Error("Failed to get start block", "error", err) + return err + } + + log.Info("Incremental data alignment check", "stateAncients", info.stateAncients, + "chainAncients", info.chainAncients, "diskLayerID", diskLayerID, "startBlock", startBlock, "recordFirstStateID", recordFirstStateID) + + if info.isEmpty() { + log.Info("Force kill with empty data") + if info.chainAncients == 0 && info.stateAncients == 0 { + if err = db.setBlockCount(startBlock, 0); err != nil { + return err + } + return nil + } + if diskLayerID > recordFirstStateID { + h, err := readHistory(db.freezer, recordFirstStateID) + if err != nil { + return err + } + + if err = db.Recover(h.meta.root); err != nil { + log.Error("Failed to recover state after force kill", "root", h.meta.root, "stateID", info.lastStateID, "error", err) + } else { + log.Info("Successfully recovered state after force kill", "root", h.meta.root, "stateID", recordFirstStateID) + } + + db.incr.duplicateEndBlock = h.meta.block + } else { + // use current dir block + start, _, err := db.incr.incrDB.ParseCurrDirBlockNumber() + if err != nil { + return err + } + db.incr.duplicateEndBlock = start - 1 + log.Info("recordFirstStateID is bigger", "start", start) + } + + if err = info.stateFreezer.Reset(); err != nil { + return err + } + if err = info.chainFreezer.Reset(); err != nil { + return err + } + if err = db.incr.resetIncrChainFreezer(db.diskdb, db.incr.duplicateEndBlock+1); err != nil { + return err + } + if err = db.setBlockCount(startBlock, 0); err != nil { + return err + } + return nil + } + + log.Info("Both incr chain and state have data, comparing for alignment", + "lastChainStateID", info.lastChainStateID, "lastStateID", info.lastStateID, + "lastStateBlock", info.lastStateBlock, "chainAncients", info.chainAncients) + + // handle force kill with incr state and chain data + if info.chainAncients-1 != info.lastStateBlock { + log.Info("Force kill with data") + if diskLayerID > info.lastStateID { + h, err := readHistory(db.freezer, info.lastStateID) + if err != nil { + return err + } + if h.meta.block != info.lastStateBlock { + return fmt.Errorf("history block [%d] is unequal to incr recorded block [%d]", h.meta.block, info.lastStateBlock) + } + + if err = db.Recover(h.meta.root); err != nil { + log.Error("Failed to recover state after force kill", "root", h.meta.root, "stateID", info.lastStateID, "error", err) + } else { + log.Info("Successfully recovered state after force kill", "root", h.meta.root, "stateID", info.lastStateID) + } + + db.incr.duplicateEndBlock = h.meta.block + } else { + db.incr.duplicateEndBlock = info.lastStateBlock + } + + if err = info.chainFreezer.Reset(); err != nil { + return err + } + if err = db.incr.resetIncrChainFreezer(db.diskdb, info.lastStateBlock); err != nil { + return err + } + if err = db.setBlockCount(startBlock, info.lastStateBlock); err != nil { + return err + } + return nil + } + + // Find the minimum state ID to ensure consistency + var finalStateID, finalBlock uint64 + if info.lastChainStateID < info.lastStateID { + finalStateID = info.lastChainStateID + finalBlock = info.chainAncients - 1 + } else if info.lastStateID < info.lastChainStateID { + finalStateID = info.lastStateID + finalBlock = info.lastStateBlock + } else { + finalStateID = info.lastStateID + finalBlock = info.lastStateBlock + } + + if finalStateID < diskLayerID { + return fmt.Errorf("Final state ID is less than disk layer ID, diskLayerID: %d, finalStateID: %d", diskLayerID, finalStateID) + } + + // Truncate incr state freezer + if err = db.truncateIncrStateFreezer(info, finalStateID); err != nil { + return err + } + // Truncate incr chain freezer + if err = db.truncateIncrChainFreezer(info, finalBlock); err != nil { + return err + } + + if err = db.setBlockCount(startBlock, finalBlock); err != nil { + return err + } + return nil +} + +// truncateIncrStateFreezer truncates the incr state freezer to align with final state +func (db *Database) truncateIncrStateFreezer(info *incrInfo, finalStateID uint64) error { + truncatePos := info.stateAncients + + // Find the correct truncate position if needed + if info.lastChainStateID < info.lastStateID { + for index := info.stateAncients; index >= 1; index-- { + metadata := rawdb.ReadIncrStateHistoryMeta(info.stateFreezer, index) + if metadata == nil { + return fmt.Errorf("incr state history not found: %d", index) + } + + if finalStateID >= metadata.StateIDArray[0] && finalStateID <= metadata.StateIDArray[1] { + truncatePos = index + break + } + } + } + + pruned, err := truncateFromHead(db.diskdb, info.stateFreezer, truncatePos) + if err != nil { + log.Error("Failed to truncate incr state histories", "error", err) + return err + } + if pruned != 0 { + log.Warn("Truncated incr state histories to align with chain", + "number", pruned, "finalStateID", finalStateID) + } + return nil +} + +// truncateIncrChainFreezer truncates the incr chain freezer to align with final block +func (db *Database) truncateIncrChainFreezer(info *incrInfo, finalBlock uint64) error { + chainTail, err := info.chainFreezer.Tail() + if err != nil { + log.Error("Failed to retrieve tail of incr chain history", "error", err) + return err + } + + if finalBlock < chainTail { + if err = info.chainFreezer.Reset(); err != nil { + log.Error("Failed to reset incr chain history", "error", err) + return err + } + log.Info("Reset incr chain history due to truncation is out of range", + "finalBlock", finalBlock, "tail", chainTail) + return nil + } + + pruned, err := truncateIncrChainFreezerFromHead(info.chainFreezer, finalBlock) + if err != nil { + log.Error("Failed to truncate incr chain histories", "error", err) + return err + } + if pruned != 0 { + log.Warn("Truncated incr chain histories to align with state", + "number", pruned, "finalBlock", finalBlock) + } + return nil +} + +func (db *Database) setBlockCount(startBlock, currBlock uint64) error { + dirStartBlock, dirEndBlock, err := db.incr.incrDB.ParseCurrDirBlockNumber() + if err != nil { + return err + } + + if startBlock > dirEndBlock+1 { + return fmt.Errorf("start block [%d] is beyond dir end block [%d], please reset incr dir", startBlock, dirEndBlock) + } + + var blockCount uint64 + if currBlock < dirStartBlock { + blockCount = 0 + } else if currBlock >= dirStartBlock && currBlock <= dirEndBlock { + blockCount = currBlock - dirStartBlock + } else { + blockCount = db.config.IncrHistory + } + + log.Info("SetBlockCount", "blockCount", blockCount, "dirStartBlock", dirStartBlock, "dirEndBlock", dirEndBlock, + "currBlock", currBlock) + db.incr.incrDB.SetBlockCount(blockCount) + return nil +} diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index 3814f91e81..6ad4f23fc8 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -19,6 +19,7 @@ package pathdb import ( "bytes" "fmt" + "strings" "sync" "time" @@ -26,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" ) @@ -362,6 +364,15 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { } } } + + if dl.db.config.EnableIncr { + err := dl.commitIncrData(bottom) + if err != nil { + log.Error("Failed to commit incremental data after retries", "err", err) + return nil, err + } + } + // Mark the diskLayer as stale before applying any mutations on top. dl.stale = true @@ -447,7 +458,7 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) { } // To remove outdated history objects from the end, we set the 'tail' parameter // to 'oldest-1' due to the offset between the freezer index and the history ID. - if overflow { + if overflow && !dl.db.config.EnableIncr { pruned, err := truncateFromTail(ndl.db.diskdb, ndl.db.freezer, oldest-1) if err != nil { return nil, err @@ -622,3 +633,96 @@ func (dl *diskLayer) terminate() error { } return nil } + +// commitIncrData attempts to commit incremental data with retry mechanism. +func (dl *diskLayer) commitIncrData(bottom *diffLayer) error { + const ( + maxRetries = 5 + baseDelay = 100 * time.Millisecond + maxDelay = 5 * time.Second + ) + + var lastErr error + for attempt := 0; attempt < maxRetries; attempt++ { + err := dl.db.incr.commit(bottom) + if err == nil { + if attempt > 0 { + log.Info("Incremental data commit succeeded after retries", + "block", bottom.block, "stateID", bottom.stateID(), "attempts", attempt+1) + } + return nil + } + lastErr = err + + // Check if this is a queue full error + if strings.Contains(err.Error(), "task queue is full") { + // Calculate delay with exponential backoff + delay := baseDelay * time.Duration(1< maxDelay { + delay = maxDelay + } + + // Check if directory switch is in progress + switching := dl.db.incr.incrDB.IsSwitching() + queueUsage := dl.db.incr.GetQueueUsageRate() + log.Warn("Task queue is full, retrying after delay", "block", bottom.block, + "stateID", bottom.stateID(), "attempt", attempt+1, "maxRetries", maxRetries, "delay", delay, + "switching", switching, "queueUsage", fmt.Sprintf("%.1f%%", queueUsage)) + + // If the directory switch is in progress, use longer delay + if switching { + delay = maxDelay + log.Info("Directory switch detected, using longer delay", "delay", delay) + } + time.Sleep(delay) + continue + } + + log.Error("Non-recoverable error committing incremental data", + "block", bottom.block, "stateID", bottom.stateID(), "err", err) + incrCommitErrorMeter.Mark(1) + return err + } + + incrCommitErrorMeter.Mark(1) + log.Error("Failed to commit incremental data after all retries", + "block", bottom.block, "stateID", bottom.stateID(), "maxRetries", maxRetries, "finalError", lastErr) + dl.db.incr.LogStats() + return fmt.Errorf("failed to commit incremental data after %d retries: %w", maxRetries, lastErr) +} + +// mergeIncrNodesWithStates merges incr trie nodes and states into local data. +func (dl *diskLayer) mergeIncrNodesWithStates(db ethdb.KeyValueStore, freezer ethdb.AncientWriter, + incrFreezer ethdb.ResettableAncientStore, start, end uint64) error { + persistID := rawdb.ReadPersistentStateID(db) + log.Info("Ancient db meta info", "persistent_state_id", persistID, "start", start, "end", end) + + for i := start; i <= end; i++ { + m := rawdb.ReadIncrStateHistoryMeta(incrFreezer, i) + if m == nil { + return fmt.Errorf("not found incr state history meta: %d", i) + } + var combined *buffer + if !m.HasStates { + nodes, err := readIncrTrieNodes(incrFreezer, i) + if err != nil { + return err + } + combined = dl.buffer.commit(nodes, newStates(nil, nil, false)) + } else { + states, err := readIncrStatesData(incrFreezer, i) + if err != nil { + return err + } + combined = dl.buffer.commit(newNodeSet(nil), states) + } + + if err := combined.flushIncrSnapshot(m.Root, db, freezer, nil, m.StateIDArray[1]); err != nil { + return err + } + log.Info("Flush incr nodes and states", "layers", m.Layers, "root", m.Root, "hasStates", m.HasStates) + } + + log.Info("Finished merging incremental state history") + return nil +} diff --git a/triedb/pathdb/history.go b/triedb/pathdb/history.go index 47f224170d..60081dd4b0 100644 --- a/triedb/pathdb/history.go +++ b/triedb/pathdb/history.go @@ -678,3 +678,58 @@ func truncateFromTail(db ethdb.Batcher, store ethdb.AncientStore, ntail uint64) } return int(ntail - otail), nil } + +// truncateIncrChainFreezerFromHead removes the extra incr chain histories from the head with the given +// parameters. It returns the number of items removed from the head. +func truncateIncrChainFreezerFromHead(store ethdb.AncientStore, nhead uint64) (int, error) { + ohead, err := store.Ancients() + if err != nil { + return 0, err + } + otail, err := store.Tail() + if err != nil { + return 0, err + } + // Ensure that the truncation target falls within the specified range. + if ohead < nhead || nhead < otail { + return 0, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", otail, ohead, nhead) + } + // Short circuit if nothing to truncate. + if ohead == nhead { + return 0, nil + } + ohead, err = store.TruncateHead(nhead) + if err != nil { + return 0, err + } + return int(ohead - nhead), nil +} + +// truncateIncrChainFreezerFromTail removes the extra incremental chain histories from the tail +// with the given parameters. It returns the number of items removed from the tail. +func truncateIncrChainFreezerFromTail(store ethdb.AncientStore, ntail uint64) (int, error) { + ohead, err := store.Ancients() + if err != nil { + return 0, err + } + otail, err := store.Tail() + if err != nil { + return 0, err + } + if ohead == otail { + return 0, nil + } + // Ensure that the truncation target falls within the specified range. + if otail > ntail || ntail > ohead { + return 0, fmt.Errorf("out of range, tail: %d, head: %d, target: %d", otail, ohead, ntail) + } + // Short circuit if nothing to truncate. + if otail == ntail { + return 0, nil + } + otail, err = store.TruncateTail(ntail) + if err != nil { + return 0, err + } + return int(ntail - otail), nil +} diff --git a/triedb/pathdb/incr_manager.go b/triedb/pathdb/incr_manager.go new file mode 100644 index 0000000000..3e9b394cbc --- /dev/null +++ b/triedb/pathdb/incr_manager.go @@ -0,0 +1,682 @@ +package pathdb + +import ( + "errors" + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie/trienode" +) + +const ( + // The default kept blocks in incremental chain freezer: 1024. + DefaultKeptBlocks = 1024 + + // Number of blocks after which to save the parlia snapshot to the database + parliaSnapCheckpointInterval = 1024 + + // The default number of blocks and state history stored in incr freezer db + DefaultBlockInterval = 100000 + + // The default memory allowance for incremental state buffer: 6GB + DefaultIncrStateBufferSize = 6 * 1024 * 1024 * 1024 + + // The maximum size of the batch to be flushed into the ancient db: 2GB + defaultFlushBatchSize = 2 * 1024 * 1024 * 1024 +) + +// writeStats tracks write operation statistics +type writeStats struct { + totalTasks uint64 + completedTasks uint64 + failedTasks uint64 + queueLength int32 + avgProcessTime uint64 // Average processing time in nanoseconds + maxProcessTime uint64 // Maximum processing time in nanoseconds + totalProcessTime uint64 // Total processing time for calculating average + lastResetTime time.Time // Last time stats were reset +} + +// UpdateProcessTime updates processing time statistics +func (ws *writeStats) UpdateProcessTime(duration time.Duration) { + durationNs := uint64(duration.Nanoseconds()) + + // Update max processing time + for { + current := atomic.LoadUint64(&ws.maxProcessTime) + if durationNs <= current || atomic.CompareAndSwapUint64(&ws.maxProcessTime, current, durationNs) { + break + } + } + + // Update total processing time for average calculation + atomic.AddUint64(&ws.totalProcessTime, durationNs) + + // Calculate and update average + completed := atomic.LoadUint64(&ws.completedTasks) + if completed > 0 { + avg := atomic.LoadUint64(&ws.totalProcessTime) / completed + atomic.StoreUint64(&ws.avgProcessTime, avg) + } +} + +// incrManager manages incremental state storage with async write capability +type incrManager struct { + db *Database // Reference to parent Database for accessing diskdb + incrDB *rawdb.IncrSnapDB + chainConfig *params.ChainConfig + + // used to skip duplicate blocks until end block + duplicateEndBlock uint64 + + // Async write control + writeQueue chan *diffLayer + stopChan chan struct{} + wg sync.WaitGroup + + stats writeStats + started bool + lock sync.RWMutex + + // Async incremental state buffer + asyncBuffer *asyncIncrStateBuffer + bufferLimit uint64 // Memory limit for buffer +} + +// NewIncrManager creates a new incremental manager with async write capability +func NewIncrManager(db *Database, incrDB *rawdb.IncrSnapDB) *incrManager { + im := &incrManager{ + db: db, + incrDB: incrDB, + writeQueue: make(chan *diffLayer, 100), + stopChan: make(chan struct{}), + started: false, + bufferLimit: db.config.IncrStateBuffer, + } + + chainConfig, err := rawdb.GetChainConfig(db.diskdb) + if err != nil { + log.Crit("Failed to get chain config", "error", err) + } + im.chainConfig = chainConfig + + // Initialize async incremental state buffer + im.asyncBuffer = newAsyncIncrStateBuffer(im.bufferLimit, defaultFlushBatchSize) + return im +} + +// Start starts the async write workers and directory switch checker +func (im *incrManager) Start() { + im.lock.Lock() + defer im.lock.Unlock() + + if im.started { + log.Warn("Incremental store already started") + return + } + + im.wg.Add(1) + go im.worker() + + im.wg.Add(1) + go im.listenTruncateSignal() + + im.started = true + log.Info("Incremental store async worker started") +} + +// Stop stops the async write workers and directory switch checker +func (im *incrManager) Stop() { + im.lock.Lock() + defer im.lock.Unlock() + + if !im.started { + return + } + + log.Info("Stopping incremental store", "pending_tasks", im.GetQueueLength()) + + // Set a timeout for graceful shutdown + shutdownTimeout := 30 * time.Second + shutdownComplete := make(chan struct{}) + + go func() { + im.drainQueue() + + // Stop workers + close(im.stopChan) + im.wg.Wait() + + close(shutdownComplete) + }() + + // Wait for graceful shutdown or timeout + select { + case <-shutdownComplete: + log.Info("Incremental store stopped gracefully") + case <-time.After(shutdownTimeout): + log.Warn("Incremental store shutdown timeout, forcing stop", + "timeout", shutdownTimeout, "remaining_tasks", im.GetQueueLength()) + } + + if im.asyncBuffer != nil { + if err := im.ForceFlushStateBuffer(); err != nil { + log.Crit("Failed to force flush data", "error", err) + } + } + + ancients, _ := im.incrDB.GetChainFreezer().Ancients() + _ = im.truncateExtraBlock(ancients - 1) + + im.started = false + im.LogStats() +} + +// listenTruncateSignal listens truncate state freezer and incr chain freezer signal. +func (im *incrManager) listenTruncateSignal() { + truncateTicker := time.NewTicker(time.Second * 3) + defer truncateTicker.Stop() + defer im.wg.Done() + + for { + select { + case <-truncateTicker.C: + ancients, err := im.incrDB.GetChainFreezer().Ancients() + if err != nil { + log.Error("Failed to get ancients in truncating", "error", err) + } + if ancients == 0 { + continue + } + if err = im.truncateExtraBlock(ancients - 1); err != nil { + continue + } + + case stateID := <-im.asyncBuffer.getTruncateSignal(): + if err := im.truncateStateFreezer(stateID); err != nil { + continue + } + + case <-im.stopChan: + log.Debug("Truncate signal listener stopped") + return + } + } +} + +// commit submits an async write task +func (im *incrManager) commit(bottom *diffLayer) error { + if !im.started { + return errors.New("incremental store not started") + } + + atomic.AddUint64(&im.stats.totalTasks, 1) + atomic.AddInt32(&im.stats.queueLength, 1) + + if im.incrDB.IsSwitching() { + log.Info("Directory switching in progress, waiting for completion", "block", bottom.block, "stateID", bottom.stateID()) + for im.incrDB.IsSwitching() { + time.Sleep(100 * time.Millisecond) + } + } + + select { + case im.writeQueue <- bottom: + return nil + + case <-im.stopChan: + atomic.AddInt32(&im.stats.queueLength, -1) + return errors.New("incremental store is stopping") + + default: + atomic.AddInt32(&im.stats.queueLength, -1) + log.Error("Task queue is full", "queueLength", im.GetQueueLength(), "block", bottom.block) + im.LogStats() + return fmt.Errorf("task queue is full (length %d, block %d)", im.GetQueueLength(), bottom.block) + } +} + +// worker processes write tasks asynchronously +func (im *incrManager) worker() { + defer im.wg.Done() + + for { + select { + case dl := <-im.writeQueue: + if dl == nil { + log.Crit("Diff layer is nil") + return + } + atomic.AddInt32(&im.stats.queueLength, -1) + + log.Debug("Worker received task", "block", dl.block, "stateID", dl.stateID(), "queueLength", im.GetQueueLength()) + + startTime := time.Now() + err := im.processWriteTask(dl) + processingTime := time.Since(startTime) + im.stats.UpdateProcessTime(processingTime) + if err != nil { + log.Error("Async write task failed", "block", dl.block, "stateID", dl.stateID(), + "processingTime", processingTime, "error", err) + incrProcessErrorMeter.Mark(1) + return + } else { + log.Debug("Task processed successfully", "block", dl.block, "stateID", dl.stateID(), "processingTime", processingTime) + } + + im.updateStats(err) + case <-im.stopChan: + log.Debug("Worker stopping") + return + } + } +} + +func (im *incrManager) processWriteTask(dl *diffLayer) error { + // skip already written incremental data + if dl.block <= im.duplicateEndBlock { + return nil + } + + // Write incremental data + if err := im.resetIncrChainFreezer(im.db.diskdb, dl.block); err != nil { + return err + } + if err := im.writeIncrData(dl); err != nil { + return err + } + + return nil +} + +// writeChainData writes incremental data: chain and state +func (im *incrManager) writeIncrData(dl *diffLayer) error { + head, err := im.incrDB.GetChainFreezer().Ancients() + if err != nil { + log.Error("Failed to get ancients from incr chain freezer", "error", err) + return err + } + + var startBlock uint64 + if dl.block == head { + startBlock = dl.block + } else if dl.block > head { + startBlock = head + } else { + if dl.block < head { + log.Crit("Block number should be greater than or equal to head", "blockNumber", dl.block, + "head", head) + } + } + + for i := startBlock; i <= dl.block; i++ { + // check if this block has state changes + isEmptyBlock := true + currStateID := dl.stateID() - 1 + if i == dl.block { + isEmptyBlock = false + currStateID = dl.stateID() + } + + if im.incrDB.Full() { + if err = im.truncateExtraBlock(i - 1); err != nil { + log.Error("Failed to truncate incr chain freezer", "blockNumber", i-1, "error", err) + return err + } + switched, err := im.incrDB.CheckAndInitiateSwitch(i, im) + if err != nil { + log.Error("Failed to check and switch incremental db", "error", err) + return err + } + + if switched { + im.asyncBuffer = newAsyncIncrStateBuffer(im.bufferLimit, defaultFlushBatchSize) + // record the first state id in pebble + im.incrDB.WriteFirstStateID(dl.stateID() - 1) + log.Info("Directory switch completed", "blockNumber", i, "stateID", dl.stateID()) + } + + if err = im.resetIncrChainFreezer(im.db.diskdb, i); err != nil { + log.Error("Failed to reset incr chain freezer", "blockNumber", i, "error", err) + return err + } + } + + if err = im.writeIncrBlock(im.db.diskdb, i, currStateID, isEmptyBlock); err != nil { + log.Error("Failed to write block data to freezer", "block", i, "stateID", dl.stateID(), "error", err) + return err + } + if !isEmptyBlock { + if err = im.writeIncrStateData(dl); err != nil { + log.Error("Failed to write incr state data", "block", dl.block, "stateID", dl.stateID(), "error", err) + return err + } + } + } + + log.Debug("Incremental block data processing completed", "startBlock", startBlock, "endBlock", dl.block, + "totalProcessed", dl.block-startBlock+1) + return nil +} + +func (im *incrManager) resetIncrChainFreezer(reader ethdb.Reader, blockNumber uint64) error { + blockHash := rawdb.ReadCanonicalHash(reader, blockNumber) + if blockHash == (common.Hash{}) { + return fmt.Errorf("canonical hash not found for block %d", blockNumber) + } + h, _ := rawdb.ReadHeaderAndRaw(reader, blockHash, blockNumber) + if h == nil { + return fmt.Errorf("block header missing, can't freeze block %d", blockNumber) + } + isCancun := im.chainConfig.IsCancun(h.Number, h.Time) + if err := rawdb.ResetEmptyIncrChainTable(im.incrDB.GetChainFreezer(), blockNumber, isCancun); err != nil { + log.Error("Failed to reset empty incr chain freezer", "block", blockNumber, "error", err) + return err + } + return nil +} + +// writeIncrStateData writes incr state data using async incremental state buffer +func (im *incrManager) writeIncrStateData(dl *diffLayer) error { + // Short circuit if states is not available + if dl.states == nil { + return errors.New("state change set is not available") + } + + start := time.Now() + // Commit to async buffer instead of direct write + im.asyncBuffer.commit(dl.root, dl.nodes, dl.states.stateSet, dl.stateID(), dl.block) + if err := im.asyncBuffer.flush(im.incrDB, false); err != nil { + return fmt.Errorf("failed to flush async incremental state buffer: %v", err) + } + log.Debug("Committed to incremental state buffer", "id", dl.stateID(), "block", dl.block, + "nodes_size", dl.nodes.size, "elapsed", common.PrettyDuration(time.Since(start))) + return nil +} + +func (im *incrManager) truncateExtraBlock(blockNumber uint64) error { + // always reload the incr chain freezer to + incrChainFreezer := im.incrDB.GetChainFreezer() + tail, err := incrChainFreezer.Tail() + if err != nil { + log.Error("Failed to get incr chain freezer tail", "error", err) + return err + } + if tail == 0 { + return nil + } + + // Only truncate if we have more blocks than the limit and there are actual blocks to truncate + if blockNumber-tail >= im.db.config.IncrKeptBlocks { + targetTail := blockNumber - im.db.config.IncrKeptBlocks + 1 + pruned, err := truncateIncrChainFreezerFromTail(incrChainFreezer, targetTail) + if err != nil { + log.Error("Failed to truncate chain freezer", "target_tail", targetTail, "current_tail", tail, + "blockNumber", blockNumber, "error", err) + return err + } + + if err = incrChainFreezer.SyncAncient(); err != nil { + log.Error("Failed to sync after incr chain freezer truncation", "error", err) + } else { + log.Debug("Successfully synced incr chain freezer after truncation") + } + log.Debug("Pruned incr chain history", "items", pruned, "target_tail", targetTail, "old_tail", tail) + } + return nil +} + +// updateStats updates operation statistics +func (im *incrManager) updateStats(err error) { + if err != nil { + atomic.AddUint64(&im.stats.failedTasks, 1) + } else { + atomic.AddUint64(&im.stats.completedTasks, 1) + } +} + +// drainQueue waits for all pending tasks to be processed +func (im *incrManager) drainQueue() { + for { + queueLen := im.GetQueueLength() + if queueLen == 0 { + break + } + log.Debug("Waiting for queue to drain", "remaining", queueLen) + time.Sleep(100 * time.Millisecond) + } +} + +// GetQueueLength returns the current number of pending tasks +func (im *incrManager) GetQueueLength() int { + return int(atomic.LoadInt32(&im.stats.queueLength)) +} + +// GetQueueCapacity returns the maximum queue capacity +func (im *incrManager) GetQueueCapacity() int { + return cap(im.writeQueue) +} + +// GetQueueUsageRate returns the queue usage rate as a percentage +func (im *incrManager) GetQueueUsageRate() float64 { + queueLen := im.GetQueueLength() + capacity := im.GetQueueCapacity() + if capacity == 0 { + return 0 + } + return float64(queueLen) / float64(capacity) * 100 +} + +// GetStats returns current statistics +func (im *incrManager) GetStats() (total, completed, failed uint64, queueLen int) { + return atomic.LoadUint64(&im.stats.totalTasks), + atomic.LoadUint64(&im.stats.completedTasks), + atomic.LoadUint64(&im.stats.failedTasks), + im.GetQueueLength() +} + +// LogStats logs current statistics +func (im *incrManager) LogStats() { + total := atomic.LoadUint64(&im.stats.totalTasks) + completed := atomic.LoadUint64(&im.stats.completedTasks) + failed := atomic.LoadUint64(&im.stats.failedTasks) + queueLen := im.GetQueueLength() + queueCapacity := im.GetQueueCapacity() + queueUsage := im.GetQueueUsageRate() + + avgProcessTime := atomic.LoadUint64(&im.stats.avgProcessTime) + maxProcessTime := atomic.LoadUint64(&im.stats.maxProcessTime) + + successRate := float64(0) + if total > 0 { + successRate = float64(completed) / float64(total) * 100 + } + + log.Info("Incremental store statistics", "total_tasks", total, "completed", completed, + "failed", failed, "pending", queueLen, "queue_capacity", queueCapacity, "queue_usage", fmt.Sprintf("%.1f%%", queueUsage), + "success_rate", fmt.Sprintf("%.2f%%", successRate), "avg_process_time", time.Duration(avgProcessTime), + "max_process_time", time.Duration(maxProcessTime), "switching", im.incrDB.IsSwitching(), + "uptime", time.Since(im.stats.lastResetTime).Round(time.Second)) +} + +// writeIncrBlock writes incremental block +func (im *incrManager) writeIncrBlock(reader ethdb.Reader, blockNumber, stateID uint64, isEmptyBlock bool) error { + blockHash := rawdb.ReadCanonicalHash(reader, blockNumber) + if blockHash == (common.Hash{}) { + return fmt.Errorf("canonical hash not found for block %d", blockNumber) + } + h, header := rawdb.ReadHeaderAndRaw(reader, blockHash, blockNumber) + if len(header) == 0 { + return fmt.Errorf("block header missing, can't freeze block %d", blockNumber) + } + body := rawdb.ReadBodyRLP(reader, blockHash, blockNumber) + if len(body) == 0 { + return fmt.Errorf("block body missing, can't freeze block %d", blockNumber) + } + receipts := rawdb.ReadReceiptsRLP(reader, blockHash, blockNumber) + if len(receipts) == 0 { + return fmt.Errorf("block receipts missing, can't freeze block %d", blockNumber) + } + td := rawdb.ReadTdRLP(reader, blockHash, blockNumber) + if len(td) == 0 { + return fmt.Errorf("total difficulty not found for block %d (hash: %s)", blockNumber, blockHash.Hex()) + } + + chainConfig, err := rawdb.GetChainConfig(reader) + if err != nil { + log.Error("Failed to get chain config", "error", err) + return err + } + // blobs is nil before cancun fork + var sidecars rlp.RawValue + isCancun := chainConfig.IsCancun(h.Number, h.Time) + if isCancun { + sidecars = rawdb.ReadBlobSidecarsRLP(reader, blockHash, blockNumber) + if len(sidecars) == 0 { + return fmt.Errorf("block blobs missing, can't freeze block %d", blockNumber) + } + } + + err = im.incrDB.WriteIncrBlockData(blockNumber, stateID, blockHash[:], header, body, receipts, td, sidecars, isEmptyBlock, isCancun) + if err != nil { + log.Error("Failed to write block data", "error", err) + return err + } + + if blockNumber%parliaSnapCheckpointInterval == 0 { + blob, err := reader.Get(append(rawdb.ParliaSnapshotPrefix, blockHash[:]...)) + if err != nil { + log.Error("Failed to get parlia snapshot", "error", err) + return err + } + im.incrDB.WriteParliaSnapshot(blockHash, blob) + log.Debug("Writing parlia snapshot into incremental", "blockNumber", blockNumber) + } + + log.Debug("Write one block data into incr chain freezer", "block", blockNumber, "hash", blockHash.Hex()) + return nil +} + +// ForceFlushStateBuffer forces all buffered data in asyncIncrStateBuffer to be flushed. +// This is called before directory switch to ensure data integrity +func (im *incrManager) ForceFlushStateBuffer() error { + if im.asyncBuffer == nil { + return nil + } + + // Check if there's any data to flush + if im.asyncBuffer.empty() { + log.Info("No buffered data to flush") + return nil + } + + // Force flush all data + if err := im.asyncBuffer.flush(im.incrDB, true); err != nil { + return fmt.Errorf("failed to force flush all buffered data: %v", err) + } + + im.asyncBuffer.waitAndStopFlushing() + + // Get the last stateID from the buffer + stateID := im.asyncBuffer.getFlushedStateID() + if stateID > 0 { + if err := im.truncateStateFreezer(stateID); err != nil { + log.Error("Failed to truncate state freezer", "stateID", stateID, "error", err) + return err + } + } + + return nil +} + +// truncateStateFreezer truncate state history by flushed stateID. +func (im *incrManager) truncateStateFreezer(stateID uint64) error { + tail, err := im.db.freezer.Tail() + if err != nil { + return nil + } + limit := im.db.config.StateHistory + if limit == 0 || stateID-tail <= limit { + log.Info("No truncation needed", "stateID", stateID, "tail", tail, "limit", limit) + return nil + } + + pruned, err := truncateFromTail(im.db.diskdb, im.db.freezer, stateID-limit) + if err != nil { + log.Error("Failed to truncate from tail", "error", err, "target", stateID) + return err + } + log.Info("Successfully truncated state history", "pruned_items", pruned, "target_tail", stateID) + return nil +} + +func readIncrTrieNodes(reader ethdb.AncientReader, id uint64) (*nodeSet, error) { + data, err := rawdb.ReadIncrStateTrieNodes(reader, id) + if err != nil { + log.Error("Failed to read incremental trie nodes", "id", id, "error", err) + return nil, err + } + + var decodedTrieNodes []journalNodes + if err = rlp.DecodeBytes(data, &decodedTrieNodes); err != nil { + log.Error("Failed to decode incremental trie nodes", "id", id, "error", err) + return nil, err + } + + return newNodeSet(flattenTrieNodes(decodedTrieNodes)), nil +} + +func readIncrStatesData(reader ethdb.AncientReader, id uint64) (*stateSet, error) { + data, err := rawdb.ReadIncrStatesData(reader, id) + if err != nil { + log.Error("Failed to read incr states data", "id", id, "error", err) + return nil, err + } + + var s statesData + if err = rlp.DecodeBytes(data, &s); err != nil { + log.Error("Failed to decode incr states data", "id", id, "error", err) + return nil, err + } + + accountSet := make(map[common.Hash][]byte) + for i := 0; i < len(s.Acc.AddrHashes); i++ { + accountSet[s.Acc.AddrHashes[i]] = s.Acc.Accounts[i] + } + + storageSet := make(map[common.Hash]map[common.Hash][]byte) + for _, entry := range s.Storages { + storageSet[entry.AddrHash] = make(map[common.Hash][]byte, len(entry.Keys)) + for i := 0; i < len(entry.Keys); i++ { + storageSet[entry.AddrHash][entry.Keys[i]] = entry.Vals[i] + } + } + + return newStates(accountSet, storageSet, s.RawStorageKey), nil +} + +// flattenTrieNodes returns a two-dimensional map for internal nodes. +func flattenTrieNodes(jn []journalNodes) map[common.Hash]map[string]*trienode.Node { + nodes := make(map[common.Hash]map[string]*trienode.Node) + for _, entry := range jn { + subset := make(map[string]*trienode.Node) + for _, n := range entry.Nodes { + if len(n.Blob) > 0 { + subset[string(n.Path)] = trienode.New(crypto.Keccak256Hash(n.Blob), n.Blob) + } else { + subset[string(n.Path)] = trienode.NewDeleted() + } + } + nodes[entry.Owner] = subset + } + return nodes +} diff --git a/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go index c0224dfed4..25cdca6b73 100644 --- a/triedb/pathdb/layertree.go +++ b/triedb/pathdb/layertree.go @@ -387,3 +387,28 @@ func (tree *layerTree) front() common.Hash { parent = children[0] } } + +// bottomDiffLayer returns the bottom-most diff layer in this tree. +// It returns the first diffLayer that is directly built on top of a diskLayer. +func (tree *layerTree) bottomDiffLayer() *diffLayer { + tree.lock.RLock() + defer tree.lock.RUnlock() + + bottomDisk := tree.bottom() + if bottomDisk == nil { + return nil + } + + // Find diffLayer that has bottomDisk as parent + for _, l := range tree.layers { + if dl, ok := l.(*diffLayer); ok { + if parent := dl.parentLayer(); parent != nil { + if parentDisk, ok := parent.(*diskLayer); ok && parentDisk.rootHash() == bottomDisk.rootHash() { + return dl + } + } + } + } + + return nil +} diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index 779f9d813f..b3a33fcdd5 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -81,6 +81,9 @@ var ( historicalAccountReadTimer = metrics.NewRegisteredResettingTimer("pathdb/history/account/reads", nil) historicalStorageReadTimer = metrics.NewRegisteredResettingTimer("pathdb/history/storage/reads", nil) + + incrProcessErrorMeter = metrics.NewRegisteredMeter("pathdb/incr/process/error", nil) + incrCommitErrorMeter = metrics.NewRegisteredMeter("pathdb/incr/commit/error", nil) ) // Metrics in generation diff --git a/triedb/pathdb/states.go b/triedb/pathdb/states.go index bc638a569e..567726f6dd 100644 --- a/triedb/pathdb/states.go +++ b/triedb/pathdb/states.go @@ -330,16 +330,29 @@ func (s *stateSet) updateSize(delta int) { s.size = 0 } +type statesData struct { + RawStorageKey bool + Acc accounts + Storages []storage +} + +type accounts struct { + AddrHashes []common.Hash + Accounts [][]byte +} + +type storage struct { + AddrHash common.Hash + Keys []common.Hash + Vals [][]byte +} + // encode serializes the content of state set into the provided writer. func (s *stateSet) encode(w io.Writer) error { // Encode accounts if err := rlp.Encode(w, s.rawStorageKey); err != nil { return err } - type accounts struct { - AddrHashes []common.Hash - Accounts [][]byte - } var enc accounts for addrHash, blob := range s.accountData { enc.AddrHashes = append(enc.AddrHashes, addrHash) @@ -349,12 +362,7 @@ func (s *stateSet) encode(w io.Writer) error { return err } // Encode storages - type Storage struct { - AddrHash common.Hash - Keys []common.Hash - Vals [][]byte - } - storages := make([]Storage, 0, len(s.storageData)) + storages := make([]storage, 0, len(s.storageData)) for addrHash, slots := range s.storageData { keys := make([]common.Hash, 0, len(slots)) vals := make([][]byte, 0, len(slots)) @@ -362,7 +370,7 @@ func (s *stateSet) encode(w io.Writer) error { keys = append(keys, key) vals = append(vals, val) } - storages = append(storages, Storage{ + storages = append(storages, storage{ AddrHash: addrHash, Keys: keys, Vals: vals, @@ -376,10 +384,6 @@ func (s *stateSet) decode(r *rlp.Stream) error { if err := r.Decode(&s.rawStorageKey); err != nil { return fmt.Errorf("load diff raw storage key flag: %v", err) } - type accounts struct { - AddrHashes []common.Hash - Accounts [][]byte - } var ( dec accounts accountSet = make(map[common.Hash][]byte) @@ -393,11 +397,6 @@ func (s *stateSet) decode(r *rlp.Stream) error { s.accountData = accountSet // Decode storages - type storage struct { - AddrHash common.Hash - Keys []common.Hash - Vals [][]byte - } var ( storages []storage storageSet = make(map[common.Hash]map[common.Hash][]byte) diff --git a/version/version.go b/version/version.go index d2b5fd119d..d0b4c648e1 100644 --- a/version/version.go +++ b/version/version.go @@ -19,6 +19,6 @@ package version const ( Major = 1 // Major version component of the current release Minor = 6 // Minor version component of the current release - Patch = 1 // Patch version component of the current release + Patch = 2 // Patch version component of the current release Meta = "" // Version metadata to append to the version string )