Merge master to qa (#813)

* Merge qa to master (#750)

* Added checks to RPC requests and introduced new flags to customise the parameters (#657)

* added a check to reject rpc requests with batch size > the one set using a newly added flag (rpcbatchlimit)

* added a check to reject rpc requests whose result size > the one set using a newly added flag (rpcreturndatalimit)

* updated the config files and docs

* chg : trieTimeout from 60 to 10 mins (#692)

* chg : trieTimeout from 60 to 10 mins

* chg : cache.timout to 10m from 1h in configs

* internal/cli/server : fix : added triesInMemory in config (#691)

* changed version from 0.3.0 to 0.3.4-beta (#693)

* fix nil state-sync issue, increase grpc limit (#695)

* Increase grpc message size limit in pprof

* consensus/bor/bor.go : stateSyncs init fixed [Fix #686]

* eth/filters: handle nil state-sync before notify

* eth/filters: update check

Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Daniil <daniil.melnik@chainstack.com>

* core, tests/bor: add more tests for state-sync validation (#710)

* core: add get state sync function for tests

* tests/bor: add validation for state sync events post consensus

* Arpit/temp bor sync (#701)

* Increase grpc message size limit in pprof

* ReadBorReceipts improvements

* use internal function

* fix tests

* fetch geth upstread for ReadBorReceiptRLP

* Only query bor receipt when the query index is equal to # tx in block body

This change reduces the frequency of calling ReadBorReceipt and
ReadBorTransaction, which are CPU and db intensive.

* Revert "fetch geth upstread for ReadBorReceiptRLP"

This reverts commit 2e838a6b1313d26674f3a8df4b044e35dcbf35a0.

* Restore ReadBorReceiptRLP

* fix bor receipts

* remove unused

* fix lints

---------

Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Manav Darji <manavdarji.india@gmail.com>
Co-authored-by: Evgeny Danienko <6655321@bk.ru>

* Revert "chg : trieTimeout from 60 to 10 mins (#692)" (#720)

This reverts commit 241843c7e7.

* Arpit/add execution pool 2 (#719)

* initial

* linters

* linters

* remove timeout

* update pool

* change pool size function

* check nil

* check nil

* fix tests

* Use execution pool from server in all handlers

* simplify things

* test fix

* add support for cli, config

* add to cli and config

* merge base branch

* debug statements

* fix bug

* atomic pointer timeout

* add apis

* update workerpool

* fix issues

* change params

* fix issues

* fix ipc issue

* remove execution pool from IPC

* revert

* fix tests

* mutex

* refactor flag and value names

* ordering fix

* refactor flag and value names

* update default ep size to 40

* fix bor start issues

* revert file changes

* debug statements

* fix bug

* update workerpool

* atomic pointer timeout

* add apis

* Merge branch 'add-execution-pool' of github.com:maticnetwork/bor into arpit/add-execution-pool

* fix issues

* change params

* fix issues

* fix ipc issue

* remove execution pool from IPC

* revert

* merge base branch

* Merge branch 'add-execution-pool' of github.com:maticnetwork/bor into arpit/add-execution-pool

* mutex

* fix tests

* Merge branch 'arpit/add-execution-pool' of github.com:maticnetwork/bor into arpit/add-execution-pool

* Change default size of execution pool to 40

* refactor flag and value names

* fix merge conflicts

* ordering fix

* refactor flag and value names

* update default ep size to 40

* fix bor start issues

* revert file changes

* fix linters

* fix go.mod

* change sec to ms

* change default value for ep timeout

* fix node api calls

* comment setter for ep timeout

---------

Co-authored-by: Evgeny Danienko <6655321@bk.ru>
Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Manav Darji <manavdarji.india@gmail.com>

* version change (#721)

* Event based pprof (#732)

* feature

* Save pprof to /tmp

---------

Co-authored-by: Jerry <jerrycgh@gmail.com>

* Cherry-pick changes from develop (#738)

* Check if block is nil to prevent panic (#736)

* miner: use env for tracing instead of block object (#728)

---------

Co-authored-by: Dmitry <46797839+dkeysil@users.noreply.github.com>

* add max code init size check in txpool (#739)

* Revert "Event based pprof" and update version (#742)

* Revert "Event based pprof (#732)"

This reverts commit 22fa4033e8.

* params: update version to 0.3.4-beta3

* packaging/templates: update bor version

* params, packaging/templates: update bor version

---------

Co-authored-by: SHIVAM SHARMA <shivam691999@gmail.com>
Co-authored-by: Pratik Patil <pratikspatil024@gmail.com>
Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Daniil <daniil.melnik@chainstack.com>
Co-authored-by: Arpit Temani <temaniarpit27@gmail.com>
Co-authored-by: Evgeny Danienko <6655321@bk.ru>
Co-authored-by: Dmitry <46797839+dkeysil@users.noreply.github.com>

* core, miner: add sub-spans for tracing (#753)

* core, miner: add sub-spans for tracing

* fix linters

* core: add logs for debugging

* core: add more logs to print tdd while reorg

* fix linters

* core: minor fix

* core: remove debug logs

* core: use different span for write block and set head

* core: use internal context for sending traces (#755)

* core: add : impossible reorg block dump (#754)

* add : impossible reorg block dump

* chg : 3 seperate files for impossoble reorg dump

* add : use exportBlocks method and RLP blocks before writing

* chg : small changes

* bump : go version from 1.19 to 1.20.1 (#761)

* Revert "bump : go version from 1.19 to 1.20.1 (#761)"

This reverts commit 4561012af9.

* core/vm: use optimized bigint (#26021)

* Add holiman/big

* Fix linter

* Bump version to v0.3.5

* fix lints from develop (few lints decided to appear from code that was untouched, weird)

* upgrade crypto lib version (#770)

* bump dep : github.com/Masterminds/goutils to v1.1.1 (#769)

* mardizzone/pos-1313: bump crypto dependency (#772)

* dev: chg: bumd net dependency

* dev: chg: bump crypto dependency

* dev: chg: bump crypto dependency

* bump dep : golang.org/x/net to v0.8.0 (#771)

* Verify validator set against local contract on receiving an end-of-sprint block (#768)

* Verify validator set against local contract on receiving an end-of-sprint block

* Fix tests

* Respect error returned by ParseValidators

* Keep going back until a parent block presents

* core/txpool: implement DoS defenses from geth (#778)

* Hotfixes and deps bump (#776)

* dev: chg: bump deps

* internal/cli/server, rpc: lower down http readtimeout to 10s

* dev: chg: get p2p adapter

* dev: chg: lower down jsonrpc readtimeout to 10s

* cherry-pick txpool optimisation changes

* add check for empty lists in txpool (#704)

* add check

* linters

* core, miner: add empty instrumentation name for tracing

---------

Co-authored-by: Raneet Debnath <raneetdebnath10@gmail.com>
Co-authored-by: SHIVAM SHARMA <shivam691999@gmail.com>
Co-authored-by: Evgeny Danilenko <6655321@bk.ru>
Co-authored-by: Manav Darji <manavdarji.india@gmail.com>

* packaging,params: bump to v0.3.6 (#782)

* v0.3.6 fix (#787)

* Fix get validator set in header verifier

* chg : commit tx logs from info to debug (#673)

* chg : commit tx logs from info to debug

* fix : minor changes

* chg : miner : commitTransactions-stats moved from info to debug

* lint : fix linters

* refactor logging

* miner : chg : UnauthorizedSignerError to debug

* lint : fix lint

* fix : log.Logger interface compatibility

---------

Co-authored-by: Evgeny Danienko <6655321@bk.ru>

* Remove unnecessary sorting of valset from header in verification

* dev: chg: version bump

---------

Co-authored-by: SHIVAM SHARMA <shivam691999@gmail.com>
Co-authored-by: Evgeny Danienko <6655321@bk.ru>
Co-authored-by: marcello33 <marcelloardizzone@hotmail.it>

* core: improve locks in txpool (#807)

* added a write lock to the txs.filter method and a read lock to the txs.reheap method - both of which are called by Filter during reorg adjustments to txpool

* txpool reorg locks

* more locks

* locks

* linters

* params, packaging: update version for v0.3.8-beta release

* core: add logs in reheap

---------

Co-authored-by: Alex <dalexwatts@gmail.com>
Co-authored-by: Evgeny Danienko <6655321@bk.ru>

* Merge qa to master (#808)

* Added checks to RPC requests and introduced new flags to customise the parameters (#657)

* added a check to reject rpc requests with batch size > the one set using a newly added flag (rpcbatchlimit)

* added a check to reject rpc requests whose result size > the one set using a newly added flag (rpcreturndatalimit)

* updated the config files and docs

* chg : trieTimeout from 60 to 10 mins (#692)

* chg : trieTimeout from 60 to 10 mins

* chg : cache.timout to 10m from 1h in configs

* internal/cli/server : fix : added triesInMemory in config (#691)

* changed version from 0.3.0 to 0.3.4-beta (#693)

* fix nil state-sync issue, increase grpc limit (#695)

* Increase grpc message size limit in pprof

* consensus/bor/bor.go : stateSyncs init fixed [Fix #686]

* eth/filters: handle nil state-sync before notify

* eth/filters: update check

Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Daniil <daniil.melnik@chainstack.com>

* core, tests/bor: add more tests for state-sync validation (#710)

* core: add get state sync function for tests

* tests/bor: add validation for state sync events post consensus

* Arpit/temp bor sync (#701)

* Increase grpc message size limit in pprof

* ReadBorReceipts improvements

* use internal function

* fix tests

* fetch geth upstread for ReadBorReceiptRLP

* Only query bor receipt when the query index is equal to # tx in block body

This change reduces the frequency of calling ReadBorReceipt and
ReadBorTransaction, which are CPU and db intensive.

* Revert "fetch geth upstread for ReadBorReceiptRLP"

This reverts commit 2e838a6b1313d26674f3a8df4b044e35dcbf35a0.

* Restore ReadBorReceiptRLP

* fix bor receipts

* remove unused

* fix lints

---------

Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Manav Darji <manavdarji.india@gmail.com>
Co-authored-by: Evgeny Danienko <6655321@bk.ru>

* Revert "chg : trieTimeout from 60 to 10 mins (#692)" (#720)

This reverts commit 241843c7e7.

* Arpit/add execution pool 2 (#719)

* initial

* linters

* linters

* remove timeout

* update pool

* change pool size function

* check nil

* check nil

* fix tests

* Use execution pool from server in all handlers

* simplify things

* test fix

* add support for cli, config

* add to cli and config

* merge base branch

* debug statements

* fix bug

* atomic pointer timeout

* add apis

* update workerpool

* fix issues

* change params

* fix issues

* fix ipc issue

* remove execution pool from IPC

* revert

* fix tests

* mutex

* refactor flag and value names

* ordering fix

* refactor flag and value names

* update default ep size to 40

* fix bor start issues

* revert file changes

* debug statements

* fix bug

* update workerpool

* atomic pointer timeout

* add apis

* Merge branch 'add-execution-pool' of github.com:maticnetwork/bor into arpit/add-execution-pool

* fix issues

* change params

* fix issues

* fix ipc issue

* remove execution pool from IPC

* revert

* merge base branch

* Merge branch 'add-execution-pool' of github.com:maticnetwork/bor into arpit/add-execution-pool

* mutex

* fix tests

* Merge branch 'arpit/add-execution-pool' of github.com:maticnetwork/bor into arpit/add-execution-pool

* Change default size of execution pool to 40

* refactor flag and value names

* fix merge conflicts

* ordering fix

* refactor flag and value names

* update default ep size to 40

* fix bor start issues

* revert file changes

* fix linters

* fix go.mod

* change sec to ms

* change default value for ep timeout

* fix node api calls

* comment setter for ep timeout

---------

Co-authored-by: Evgeny Danienko <6655321@bk.ru>
Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Manav Darji <manavdarji.india@gmail.com>

* version change (#721)

* Event based pprof (#732)

* feature

* Save pprof to /tmp

---------

Co-authored-by: Jerry <jerrycgh@gmail.com>

* Cherry-pick changes from develop (#738)

* Check if block is nil to prevent panic (#736)

* miner: use env for tracing instead of block object (#728)

---------

Co-authored-by: Dmitry <46797839+dkeysil@users.noreply.github.com>

* add max code init size check in txpool (#739)

* Revert "Event based pprof" and update version (#742)

* Revert "Event based pprof (#732)"

This reverts commit 22fa4033e8.

* params: update version to 0.3.4-beta3

* packaging/templates: update bor version

* internal/ethapi :: Fix : newRPCTransactionFromBlockIndex

* fix: remove assignment for bor receipt

---------

Co-authored-by: SHIVAM SHARMA <shivam691999@gmail.com>
Co-authored-by: Pratik Patil <pratikspatil024@gmail.com>
Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Daniil <daniil.melnik@chainstack.com>
Co-authored-by: Arpit Temani <temaniarpit27@gmail.com>
Co-authored-by: Evgeny Danienko <6655321@bk.ru>
Co-authored-by: Dmitry <46797839+dkeysil@users.noreply.github.com>

* Setting up bor to use hosted 18.04 runner as ubuntu provided 18.04 runner is end of life

---------

Co-authored-by: SHIVAM SHARMA <shivam691999@gmail.com>
Co-authored-by: Pratik Patil <pratikspatil024@gmail.com>
Co-authored-by: Jerry <jerrycgh@gmail.com>
Co-authored-by: Daniil <daniil.melnik@chainstack.com>
Co-authored-by: Arpit Temani <temaniarpit27@gmail.com>
Co-authored-by: Evgeny Danienko <6655321@bk.ru>
Co-authored-by: Dmitry <46797839+dkeysil@users.noreply.github.com>
Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: marcello33 <marcelloardizzone@hotmail.it>
Co-authored-by: Raneet Debnath <raneetdebnath10@gmail.com>
Co-authored-by: Raneet Debnath <35629432+Raneet10@users.noreply.github.com>
Co-authored-by: Alex <dalexwatts@gmail.com>
Co-authored-by: Daniel Jones <djones@polygon.technology>
This commit is contained in:
Manav Darji 2023-04-06 15:36:19 +05:30 committed by GitHub
parent a85370115e
commit ac353affa3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 4245 additions and 598 deletions

View file

@ -12,7 +12,9 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-18.04 runs-on:
group: ubuntu-runners
labels: 18.04RunnerT2Large
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2

View file

@ -59,7 +59,10 @@ ios:
@echo "Import \"$(GOBIN)/Geth.framework\" to use the library." @echo "Import \"$(GOBIN)/Geth.framework\" to use the library."
test: test:
$(GOTEST) --timeout 5m -shuffle=on -cover -coverprofile=cover.out $(TESTALL) $(GOTEST) --timeout 5m -shuffle=on -cover -short -coverprofile=cover.out -covermode=atomic $(TESTALL)
test-txpool-race:
$(GOTEST) -run=TestPoolMiningDataRaces --timeout 600m -race -v ./core/
test-race: test-race:
$(GOTEST) --timeout 15m -race -shuffle=on $(TESTALL) $(GOTEST) --timeout 15m -race -shuffle=on $(TESTALL)
@ -75,7 +78,7 @@ lint:
lintci-deps: lintci-deps:
rm -f ./build/bin/golangci-lint rm -f ./build/bin/golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./build/bin v1.48.0 curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b ./build/bin v1.50.1
goimports: goimports:
goimports -local "$(PACKAGE)" -w . goimports -local "$(PACKAGE)" -w .

View file

@ -94,7 +94,7 @@ syncmode = "full"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -24,6 +24,8 @@ import (
"os" "os"
"strings" "strings"
"gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -32,7 +34,6 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
"gopkg.in/urfave/cli.v1"
) )
type result struct { type result struct {

View file

@ -1,6 +1,7 @@
package debug package debug
import ( import (
"fmt"
"runtime" "runtime"
) )
@ -26,3 +27,26 @@ func Callers(show int) []string {
return callers return callers
} }
func CodeLine() (string, string, int) {
pc, filename, line, _ := runtime.Caller(1)
return runtime.FuncForPC(pc).Name(), filename, line
}
func CodeLineStr() string {
pc, filename, line, _ := runtime.Caller(1)
return fmt.Sprintf("%s:%d - %s", filename, line, runtime.FuncForPC(pc).Name())
}
func Stack(all bool) []byte {
buf := make([]byte, 4096)
for {
n := runtime.Stack(buf, all)
if n < len(buf) {
return buf[:n]
}
buf = make([]byte, 2*len(buf))
}
}

View file

@ -20,6 +20,8 @@ package math
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/holiman/uint256"
) )
// Various big integer limit values. // Various big integer limit values.
@ -132,6 +134,7 @@ func MustParseBig256(s string) *big.Int {
// BigPow returns a ** b as a big integer. // BigPow returns a ** b as a big integer.
func BigPow(a, b int64) *big.Int { func BigPow(a, b int64) *big.Int {
r := big.NewInt(a) r := big.NewInt(a)
return r.Exp(r, big.NewInt(b), nil) return r.Exp(r, big.NewInt(b), nil)
} }
@ -140,6 +143,15 @@ func BigMax(x, y *big.Int) *big.Int {
if x.Cmp(y) < 0 { if x.Cmp(y) < 0 {
return y return y
} }
return x
}
func BigMaxUint(x, y *uint256.Int) *uint256.Int {
if x.Lt(y) {
return y
}
return x return x
} }
@ -148,6 +160,15 @@ func BigMin(x, y *big.Int) *big.Int {
if x.Cmp(y) > 0 { if x.Cmp(y) > 0 {
return y return y
} }
return x
}
func BigMinUint256(x, y *uint256.Int) *uint256.Int {
if x.Gt(y) {
return y
}
return x return x
} }

23
common/math/uint.go Normal file
View file

@ -0,0 +1,23 @@
package math
import (
"math/big"
"github.com/holiman/uint256"
)
var (
U0 = uint256.NewInt(0)
U1 = uint256.NewInt(1)
U100 = uint256.NewInt(100)
)
func U256LTE(a, b *uint256.Int) bool {
return a.Lt(b) || a.Eq(b)
}
func FromBig(v *big.Int) *uint256.Int {
u, _ := uint256.FromBig(v)
return u
}

9
common/time.go Normal file
View file

@ -0,0 +1,9 @@
package common
import "time"
const TimeMilliseconds = "15:04:05.000"
func NowMilliseconds() string {
return time.Now().Format(TimeMilliseconds)
}

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"time" "time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
) )
@ -51,11 +52,16 @@ func Trace(ctx context.Context, spanName string) (context.Context, trace.Span) {
return tr.Start(ctx, spanName) return tr.Start(ctx, spanName)
} }
func Exec(ctx context.Context, spanName string, opts ...Option) { func Exec(ctx context.Context, instrumentationName, spanName string, opts ...Option) {
var span trace.Span var span trace.Span
tr := FromContext(ctx) tr := FromContext(ctx)
if tr == nil && len(instrumentationName) != 0 {
tr = otel.GetTracerProvider().Tracer(instrumentationName)
ctx = WithTracer(ctx, tr)
}
if tr != nil { if tr != nil {
ctx, span = tr.Start(ctx, spanName) ctx, span = tr.Start(ctx, spanName)
} }
@ -85,7 +91,7 @@ func ElapsedTime(ctx context.Context, span trace.Span, msg string, fn func(conte
fn(ctx, span) fn(ctx, span)
if span != nil { if span != nil {
span.SetAttributes(attribute.Int(msg, int(time.Since(now).Milliseconds()))) span.SetAttributes(attribute.Int(msg, int(time.Since(now).Microseconds())))
} }
} }

View file

@ -298,6 +298,14 @@ func (c *Bor) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Head
return c.verifyHeader(chain, header, nil) return c.verifyHeader(chain, header, nil)
} }
func (c *Bor) GetSpanner() Spanner {
return c.spanner
}
func (c *Bor) SetSpanner(spanner Spanner) {
c.spanner = spanner
}
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
// method returns a quit channel to abort the operations and a results channel to // method returns a quit channel to abort the operations and a results channel to
// retrieve the async verifications (the order is that of the input slice). // retrieve the async verifications (the order is that of the input slice).
@ -454,6 +462,33 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainHeaderReader, header *t
return err return err
} }
// Verify the validator list match the local contract
if IsSprintStart(number+1, c.config.CalculateSprint(number)) {
newValidators, err := c.spanner.GetCurrentValidatorsByBlockNrOrHash(context.Background(), rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber), number+1)
if err != nil {
return err
}
sort.Sort(valset.ValidatorsByAddress(newValidators))
headerVals, err := valset.ParseValidators(header.Extra[extraVanity : len(header.Extra)-extraSeal])
if err != nil {
return err
}
if len(newValidators) != len(headerVals) {
return errInvalidSpanValidators
}
for i, val := range newValidators {
if !bytes.Equal(val.HeaderBytes(), headerVals[i].HeaderBytes()) {
return errInvalidSpanValidators
}
}
}
// verify the validator list in the last sprint block // verify the validator list in the last sprint block
if IsSprintStart(number, c.config.CalculateSprint(number)) { if IsSprintStart(number, c.config.CalculateSprint(number)) {
parentValidatorBytes := parent.Extra[extraVanity : len(parent.Extra)-extraSeal] parentValidatorBytes := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
@ -518,7 +553,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co
hash := checkpoint.Hash() hash := checkpoint.Hash()
// get validators and current span // get validators and current span
validators, err := c.spanner.GetCurrentValidators(context.Background(), hash, number+1) validators, err := c.spanner.GetCurrentValidatorsByHash(context.Background(), hash, number+1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -688,7 +723,7 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
// get validator set if number // get validator set if number
if IsSprintStart(number+1, c.config.CalculateSprint(number)) { if IsSprintStart(number+1, c.config.CalculateSprint(number)) {
newValidators, err := c.spanner.GetCurrentValidators(context.Background(), header.ParentHash, number+1) newValidators, err := c.spanner.GetCurrentValidatorsByHash(context.Background(), header.ParentHash, number+1)
if err != nil { if err != nil {
return errUnknownValidators return errUnknownValidators
} }
@ -821,7 +856,7 @@ func (c *Bor) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHead
if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) { if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) {
cx := statefull.ChainContext{Chain: chain, Bor: c} cx := statefull.ChainContext{Chain: chain, Bor: c}
tracing.Exec(finalizeCtx, "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) { tracing.Exec(finalizeCtx, "", "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) {
// check and commit span // check and commit span
err = c.checkAndCommitSpan(finalizeCtx, state, header, cx) err = c.checkAndCommitSpan(finalizeCtx, state, header, cx)
}) })
@ -832,7 +867,7 @@ func (c *Bor) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHead
} }
if c.HeimdallClient != nil { if c.HeimdallClient != nil {
tracing.Exec(finalizeCtx, "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) { tracing.Exec(finalizeCtx, "", "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) {
// commit states // commit states
stateSyncData, err = c.CommitStates(finalizeCtx, state, header, cx) stateSyncData, err = c.CommitStates(finalizeCtx, state, header, cx)
}) })
@ -844,7 +879,7 @@ func (c *Bor) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHead
} }
} }
tracing.Exec(finalizeCtx, "bor.changeContractCodeIfNeeded", func(ctx context.Context, span trace.Span) { tracing.Exec(finalizeCtx, "", "bor.changeContractCodeIfNeeded", func(ctx context.Context, span trace.Span) {
err = c.changeContractCodeIfNeeded(headerNumber, state) err = c.changeContractCodeIfNeeded(headerNumber, state)
}) })
@ -854,7 +889,7 @@ func (c *Bor) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHead
} }
// No block rewards in PoA, so the state remains as it is // No block rewards in PoA, so the state remains as it is
tracing.Exec(finalizeCtx, "bor.IntermediateRoot", func(ctx context.Context, span trace.Span) { tracing.Exec(finalizeCtx, "", "bor.IntermediateRoot", func(ctx context.Context, span trace.Span) {
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
}) })
@ -1218,7 +1253,7 @@ func (c *Bor) SetHeimdallClient(h IHeimdallClient) {
} }
func (c *Bor) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) { func (c *Bor) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
return c.spanner.GetCurrentValidators(ctx, headerHash, blockNumber) return c.spanner.GetCurrentValidatorsByHash(ctx, headerHash, blockNumber)
} }
// //

View file

@ -89,7 +89,7 @@ func (c *ChainSpanner) GetCurrentSpan(ctx context.Context, headerHash common.Has
} }
// GetCurrentValidators get current validators // GetCurrentValidators get current validators
func (c *ChainSpanner) GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) { func (c *ChainSpanner) GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64) ([]*valset.Validator, error) {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
@ -107,16 +107,13 @@ func (c *ChainSpanner) GetCurrentValidators(ctx context.Context, headerHash comm
toAddress := c.validatorContractAddress toAddress := c.validatorContractAddress
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
// block
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{ result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{
Gas: &gas, Gas: &gas,
To: &toAddress, To: &toAddress,
Data: &msgData, Data: &msgData,
}, blockNr, nil) }, blockNrOrHash, nil)
if err != nil { if err != nil {
panic(err) return nil, err
} }
var ( var (
@ -144,6 +141,12 @@ func (c *ChainSpanner) GetCurrentValidators(ctx context.Context, headerHash comm
return valz, nil return valz, nil
} }
func (c *ChainSpanner) GetCurrentValidatorsByHash(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
blockNr := rpc.BlockNumberOrHashWithHash(headerHash, false)
return c.GetCurrentValidatorsByBlockNrOrHash(ctx, blockNr, blockNumber)
}
const method = "commitSpan" const method = "commitSpan"
func (c *ChainSpanner) CommitSpan(ctx context.Context, heimdallSpan HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error { func (c *ChainSpanner) CommitSpan(ctx context.Context, heimdallSpan HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error {

View file

@ -9,11 +9,13 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
) )
//go:generate mockgen -destination=./span_mock.go -package=bor . Spanner //go:generate mockgen -destination=./span_mock.go -package=bor . Spanner
type Spanner interface { type Spanner interface {
GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*span.Span, error) GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*span.Span, error)
GetCurrentValidators(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) GetCurrentValidatorsByHash(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64) ([]*valset.Validator, error)
CommitSpan(ctx context.Context, heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error CommitSpan(ctx context.Context, heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error
} }

View file

@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ethereum/go-ethereum/consensus/bor (interfaces: Spanner) // Source: consensus/bor/span.go
// Package bor is a generated GoMock package. // Package bor is a generated GoMock package.
package bor package bor
@ -14,6 +14,7 @@ import (
core "github.com/ethereum/go-ethereum/core" core "github.com/ethereum/go-ethereum/core"
state "github.com/ethereum/go-ethereum/core/state" state "github.com/ethereum/go-ethereum/core/state"
types "github.com/ethereum/go-ethereum/core/types" types "github.com/ethereum/go-ethereum/core/types"
rpc "github.com/ethereum/go-ethereum/rpc"
gomock "github.com/golang/mock/gomock" gomock "github.com/golang/mock/gomock"
) )
@ -41,45 +42,60 @@ func (m *MockSpanner) EXPECT() *MockSpannerMockRecorder {
} }
// CommitSpan mocks base method. // CommitSpan mocks base method.
func (m *MockSpanner) CommitSpan(arg0 context.Context, arg1 span.HeimdallSpan, arg2 *state.StateDB, arg3 *types.Header, arg4 core.ChainContext) error { func (m *MockSpanner) CommitSpan(ctx context.Context, heimdallSpan span.HeimdallSpan, state *state.StateDB, header *types.Header, chainContext core.ChainContext) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CommitSpan", arg0, arg1, arg2, arg3, arg4) ret := m.ctrl.Call(m, "CommitSpan", ctx, heimdallSpan, state, header, chainContext)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// CommitSpan indicates an expected call of CommitSpan. // CommitSpan indicates an expected call of CommitSpan.
func (mr *MockSpannerMockRecorder) CommitSpan(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { func (mr *MockSpannerMockRecorder) CommitSpan(ctx, heimdallSpan, state, header, chainContext interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), arg0, arg1, arg2, arg3, arg4) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CommitSpan", reflect.TypeOf((*MockSpanner)(nil).CommitSpan), ctx, heimdallSpan, state, header, chainContext)
} }
// GetCurrentSpan mocks base method. // GetCurrentSpan mocks base method.
func (m *MockSpanner) GetCurrentSpan(arg0 context.Context, arg1 common.Hash) (*span.Span, error) { func (m *MockSpanner) GetCurrentSpan(ctx context.Context, headerHash common.Hash) (*span.Span, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCurrentSpan", arg0, arg1) ret := m.ctrl.Call(m, "GetCurrentSpan", ctx, headerHash)
ret0, _ := ret[0].(*span.Span) ret0, _ := ret[0].(*span.Span)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// GetCurrentSpan indicates an expected call of GetCurrentSpan. // GetCurrentSpan indicates an expected call of GetCurrentSpan.
func (mr *MockSpannerMockRecorder) GetCurrentSpan(arg0, arg1 interface{}) *gomock.Call { func (mr *MockSpannerMockRecorder) GetCurrentSpan(ctx, headerHash interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentSpan", reflect.TypeOf((*MockSpanner)(nil).GetCurrentSpan), ctx, headerHash)
} }
// GetCurrentValidators mocks base method. // GetCurrentValidatorsByBlockNrOrHash mocks base method.
func (m *MockSpanner) GetCurrentValidators(arg0 context.Context, arg1 common.Hash, arg2 uint64) ([]*valset.Validator, error) { func (m *MockSpanner) GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64) ([]*valset.Validator, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1, arg2) ret := m.ctrl.Call(m, "GetCurrentValidatorsByBlockNrOrHash", ctx, blockNrOrHash, blockNumber)
ret0, _ := ret[0].([]*valset.Validator) ret0, _ := ret[0].([]*valset.Validator)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// GetCurrentValidators indicates an expected call of GetCurrentValidators. // GetCurrentValidatorsByBlockNrOrHash indicates an expected call of GetCurrentValidatorsByBlockNrOrHash.
func (mr *MockSpannerMockRecorder) GetCurrentValidators(arg0, arg1, arg2 interface{}) *gomock.Call { func (mr *MockSpannerMockRecorder) GetCurrentValidatorsByBlockNrOrHash(ctx, blockNrOrHash, blockNumber interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidators), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidatorsByBlockNrOrHash", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidatorsByBlockNrOrHash), ctx, blockNrOrHash, blockNumber)
}
// GetCurrentValidatorsByHash mocks base method.
func (m *MockSpanner) GetCurrentValidatorsByHash(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCurrentValidatorsByHash", ctx, headerHash, blockNumber)
ret0, _ := ret[0].([]*valset.Validator)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetCurrentValidatorsByHash indicates an expected call of GetCurrentValidatorsByHash.
func (mr *MockSpannerMockRecorder) GetCurrentValidatorsByHash(ctx, headerHash, blockNumber interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidatorsByHash", reflect.TypeOf((*MockSpanner)(nil).GetCurrentValidatorsByHash), ctx, headerHash, blockNumber)
} }

View file

@ -20,6 +20,8 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -92,3 +94,54 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
) )
} }
} }
// CalcBaseFee calculates the basefee of the header.
func CalcBaseFeeUint(config *params.ChainConfig, parent *types.Header) *uint256.Int {
var (
initialBaseFeeUint = uint256.NewInt(params.InitialBaseFee)
baseFeeChangeDenominatorUint64 = params.BaseFeeChangeDenominator(config.Bor, parent.Number)
baseFeeChangeDenominatorUint = uint256.NewInt(baseFeeChangeDenominatorUint64)
)
// If the current block is the first EIP-1559 block, return the InitialBaseFee.
if !config.IsLondon(parent.Number) {
return initialBaseFeeUint.Clone()
}
var (
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
parentGasTargetBig = uint256.NewInt(parentGasTarget)
)
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed == parentGasTarget {
return math.FromBig(parent.BaseFee)
}
if parent.GasUsed > parentGasTarget {
// If the parent block used more gas than its target, the baseFee should increase.
gasUsedDelta := uint256.NewInt(parent.GasUsed - parentGasTarget)
parentBaseFee := math.FromBig(parent.BaseFee)
x := gasUsedDelta.Mul(parentBaseFee, gasUsedDelta)
y := x.Div(x, parentGasTargetBig)
baseFeeDelta := math.BigMaxUint(
x.Div(y, baseFeeChangeDenominatorUint),
math.U1,
)
return x.Add(parentBaseFee, baseFeeDelta)
}
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
gasUsedDelta := uint256.NewInt(parentGasTarget - parent.GasUsed)
parentBaseFee := math.FromBig(parent.BaseFee)
x := gasUsedDelta.Mul(parentBaseFee, gasUsedDelta)
y := x.Div(x, parentGasTargetBig)
baseFeeDelta := x.Div(y, baseFeeChangeDenominatorUint)
return math.BigMaxUint(
x.Sub(parentBaseFee, baseFeeDelta),
math.U0.Clone(),
)
}

View file

@ -18,21 +18,29 @@
package core package core
import ( import (
"compress/gzip"
"context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
"os"
"path/filepath"
"sort" "sort"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/common/tracing"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
@ -1349,46 +1357,89 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
} }
// WriteBlockWithState writes the block and all associated state to the database. // WriteBlockWithState writes the block and all associated state to the database.
func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { func (bc *BlockChain) WriteBlockAndSetHead(ctx context.Context, block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
if !bc.chainmu.TryLock() { if !bc.chainmu.TryLock() {
return NonStatTy, errChainStopped return NonStatTy, errChainStopped
} }
defer bc.chainmu.Unlock() defer bc.chainmu.Unlock()
return bc.writeBlockAndSetHead(block, receipts, logs, state, emitHeadEvent) return bc.writeBlockAndSetHead(ctx, block, receipts, logs, state, emitHeadEvent)
} }
// writeBlockAndSetHead writes the block and all associated state to the database, // writeBlockAndSetHead writes the block and all associated state to the database,
// and also it applies the given block as the new chain head. This function expects // and also it applies the given block as the new chain head. This function expects
// the chain mutex to be held. // the chain mutex to be held.
func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) { func (bc *BlockChain) writeBlockAndSetHead(ctx context.Context, block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
writeBlockAndSetHeadCtx, span := tracing.StartSpan(ctx, "blockchain.writeBlockAndSetHead")
defer tracing.EndSpan(span)
var stateSyncLogs []*types.Log var stateSyncLogs []*types.Log
if stateSyncLogs, err = bc.writeBlockWithState(block, receipts, logs, state); err != nil { tracing.Exec(writeBlockAndSetHeadCtx, "", "blockchain.writeBlockWithState", func(_ context.Context, span trace.Span) {
return NonStatTy, err stateSyncLogs, err = bc.writeBlockWithState(block, receipts, logs, state)
} tracing.SetAttributes(
currentBlock := bc.CurrentBlock() span,
reorg, err := bc.forker.ReorgNeeded(currentBlock.Header(), block.Header()) attribute.Int("number", int(block.Number().Uint64())),
attribute.Bool("error", err != nil),
)
})
if err != nil { if err != nil {
return NonStatTy, err return NonStatTy, err
} }
currentBlock := bc.CurrentBlock()
var reorg bool
tracing.Exec(writeBlockAndSetHeadCtx, "", "blockchain.ReorgNeeded", func(_ context.Context, span trace.Span) {
reorg, err = bc.forker.ReorgNeeded(currentBlock.Header(), block.Header())
tracing.SetAttributes(
span,
attribute.Int("number", int(block.Number().Uint64())),
attribute.Int("current block", int(currentBlock.Number().Uint64())),
attribute.Bool("reorg needed", reorg),
attribute.Bool("error", err != nil),
)
})
if err != nil {
return NonStatTy, err
}
tracing.Exec(writeBlockAndSetHeadCtx, "", "blockchain.reorg", func(_ context.Context, span trace.Span) {
if reorg { if reorg {
// Reorganise the chain if the parent is not the head block // Reorganise the chain if the parent is not the head block
if block.ParentHash() != currentBlock.Hash() { if block.ParentHash() != currentBlock.Hash() {
if err := bc.reorg(currentBlock, block); err != nil { if err = bc.reorg(currentBlock, block); err != nil {
return NonStatTy, err status = NonStatTy
} }
} }
status = CanonStatTy status = CanonStatTy
} else { } else {
status = SideStatTy status = SideStatTy
} }
tracing.SetAttributes(
span,
attribute.Int("number", int(block.Number().Uint64())),
attribute.Int("current block", int(currentBlock.Number().Uint64())),
attribute.Bool("reorg needed", reorg),
attribute.Bool("error", err != nil),
attribute.String("status", string(status)),
)
})
if status == NonStatTy {
return
}
// Set new head. // Set new head.
if status == CanonStatTy { if status == CanonStatTy {
tracing.Exec(writeBlockAndSetHeadCtx, "", "blockchain.writeHeadBlock", func(_ context.Context, _ trace.Span) {
bc.writeHeadBlock(block) bc.writeHeadBlock(block)
})
} }
bc.futureBlocks.Remove(block.Hash()) bc.futureBlocks.Remove(block.Hash())
if status == CanonStatTy { if status == CanonStatTy {
@ -1785,7 +1836,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool)
// Don't set the head, only insert the block // Don't set the head, only insert the block
_, err = bc.writeBlockWithState(block, receipts, logs, statedb) _, err = bc.writeBlockWithState(block, receipts, logs, statedb)
} else { } else {
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false) status, err = bc.writeBlockAndSetHead(context.Background(), block, receipts, logs, statedb, false)
} }
atomic.StoreUint32(&followupInterrupt, 1) atomic.StoreUint32(&followupInterrupt, 1)
if err != nil { if err != nil {
@ -2194,6 +2245,35 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
} else { } else {
// len(newChain) == 0 && len(oldChain) > 0 // len(newChain) == 0 && len(oldChain) > 0
// rewind the canonical chain to a lower point. // rewind the canonical chain to a lower point.
home, err := os.UserHomeDir()
if err != nil {
fmt.Println("Impossible reorg : Unable to get user home dir", "Error", err)
}
outPath := filepath.Join(home, "impossible-reorgs", fmt.Sprintf("%v-impossibleReorg", time.Now().Format(time.RFC3339)))
if _, err := os.Stat(outPath); errors.Is(err, os.ErrNotExist) {
err := os.MkdirAll(outPath, os.ModePerm)
if err != nil {
log.Error("Impossible reorg : Unable to create Dir", "Error", err)
}
} else {
err = ExportBlocks(oldChain, filepath.Join(outPath, "oldChain.gz"))
if err != nil {
log.Error("Impossible reorg : Unable to export oldChain", "Error", err)
}
err = ExportBlocks([]*types.Block{oldBlock}, filepath.Join(outPath, "oldBlock.gz"))
if err != nil {
log.Error("Impossible reorg : Unable to export oldBlock", "Error", err)
}
err = ExportBlocks([]*types.Block{newBlock}, filepath.Join(outPath, "newBlock.gz"))
if err != nil {
log.Error("Impossible reorg : Unable to export newBlock", "Error", err)
}
}
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain)) log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "oldblocks", len(oldChain), "newnum", newBlock.Number(), "newhash", newBlock.Hash(), "newblocks", len(newChain))
} }
// Insert the new chain(except the head block(reverse order)), // Insert the new chain(except the head block(reverse order)),
@ -2246,6 +2326,44 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
return nil return nil
} }
// ExportBlocks exports blocks into the specified file, truncating any data
// already present in the file.
func ExportBlocks(blocks []*types.Block, fn string) error {
log.Info("Exporting blockchain", "file", fn)
// Open the file handle and potentially wrap with a gzip stream
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
}
defer fh.Close()
var writer io.Writer = fh
if strings.HasSuffix(fn, ".gz") {
writer = gzip.NewWriter(writer)
defer writer.(*gzip.Writer).Close()
}
// Iterate over the blocks and export them
if err := ExportN(writer, blocks); err != nil {
return err
}
log.Info("Exported blocks", "file", fn)
return nil
}
// ExportBlock writes a block to the given writer.
func ExportN(w io.Writer, blocks []*types.Block) error {
for _, block := range blocks {
if err := block.EncodeRLP(w); err != nil {
return err
}
}
return nil
}
// InsertBlockWithoutSetHead executes the block, runs the necessary verification // InsertBlockWithoutSetHead executes the block, runs the necessary verification
// upon it and then persist the block and the associate state into the database. // upon it and then persist the block and the associate state into the database.
// The key difference between the InsertChain is it won't do the canonical chain // The key difference between the InsertChain is it won't do the canonical chain

View file

@ -1796,7 +1796,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
spanner := bor.NewMockSpanner(ctrl) spanner := bor.NewMockSpanner(ctrl)
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{ spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{
{ {
ID: 0, ID: 0,
Address: miner.TestBankAddress, Address: miner.TestBankAddress,

View file

@ -61,11 +61,13 @@ func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
if _, err := os.Stat(journal.path); os.IsNotExist(err) { if _, err := os.Stat(journal.path); os.IsNotExist(err) {
return nil return nil
} }
// Open the journal for loading any past transactions // Open the journal for loading any past transactions
input, err := os.Open(journal.path) input, err := os.Open(journal.path)
if err != nil { if err != nil {
return err return err
} }
defer input.Close() defer input.Close()
// Temporarily discard any journal additions (don't double add on load) // Temporarily discard any journal additions (don't double add on load)
@ -80,29 +82,35 @@ func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
// appropriate progress counters. Then use this method to load all the // appropriate progress counters. Then use this method to load all the
// journaled transactions in small-ish batches. // journaled transactions in small-ish batches.
loadBatch := func(txs types.Transactions) { loadBatch := func(txs types.Transactions) {
errs := add(txs)
dropped = len(errs)
for _, err := range add(txs) { for _, err := range add(txs) {
if err != nil {
log.Debug("Failed to add journaled transaction", "err", err) log.Debug("Failed to add journaled transaction", "err", err)
dropped++
}
} }
} }
var ( var (
failure error failure error
batch types.Transactions batch types.Transactions
) )
for { for {
// Parse the next transaction and terminate on error // Parse the next transaction and terminate on error
tx := new(types.Transaction) tx := new(types.Transaction)
if err = stream.Decode(tx); err != nil { if err = stream.Decode(tx); err != nil {
if err != io.EOF { if err != io.EOF {
failure = err failure = err
} }
if batch.Len() > 0 { if batch.Len() > 0 {
loadBatch(batch) loadBatch(batch)
} }
break break
} }
// New transaction parsed, queue up for later, import if threshold is reached // New transaction parsed, queue up for later, import if threshold is reached
total++ total++
@ -111,6 +119,7 @@ func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
batch = batch[:0] batch = batch[:0]
} }
} }
log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped) log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped)
return failure return failure

View file

@ -25,8 +25,12 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
) )
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
@ -54,7 +58,11 @@ func (h *nonceHeap) Pop() interface{} {
type txSortedMap struct { type txSortedMap struct {
items map[uint64]*types.Transaction // Hash map storing the transaction data items map[uint64]*types.Transaction // Hash map storing the transaction data
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode) index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
m sync.RWMutex
cache types.Transactions // Cache of the transactions already sorted cache types.Transactions // Cache of the transactions already sorted
isEmpty bool
cacheMu sync.RWMutex
} }
// newTxSortedMap creates a new nonce-sorted transaction map. // newTxSortedMap creates a new nonce-sorted transaction map.
@ -62,28 +70,55 @@ func newTxSortedMap() *txSortedMap {
return &txSortedMap{ return &txSortedMap{
items: make(map[uint64]*types.Transaction), items: make(map[uint64]*types.Transaction),
index: new(nonceHeap), index: new(nonceHeap),
isEmpty: true,
} }
} }
// Get retrieves the current transactions associated with the given nonce. // Get retrieves the current transactions associated with the given nonce.
func (m *txSortedMap) Get(nonce uint64) *types.Transaction { func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
m.m.RLock()
defer m.m.RUnlock()
return m.items[nonce] return m.items[nonce]
} }
func (m *txSortedMap) Has(nonce uint64) bool {
if m == nil {
return false
}
m.m.RLock()
defer m.m.RUnlock()
return m.items[nonce] != nil
}
// Put inserts a new transaction into the map, also updating the map's nonce // Put inserts a new transaction into the map, also updating the map's nonce
// index. If a transaction already exists with the same nonce, it's overwritten. // index. If a transaction already exists with the same nonce, it's overwritten.
func (m *txSortedMap) Put(tx *types.Transaction) { func (m *txSortedMap) Put(tx *types.Transaction) {
m.m.Lock()
defer m.m.Unlock()
nonce := tx.Nonce() nonce := tx.Nonce()
if m.items[nonce] == nil { if m.items[nonce] == nil {
heap.Push(m.index, nonce) heap.Push(m.index, nonce)
} }
m.items[nonce], m.cache = tx, nil
m.items[nonce] = tx
m.cacheMu.Lock()
m.isEmpty = true
m.cache = nil
m.cacheMu.Unlock()
} }
// Forward removes all transactions from the map with a nonce lower than the // Forward removes all transactions from the map with a nonce lower than the
// provided threshold. Every removed transaction is returned for any post-removal // provided threshold. Every removed transaction is returned for any post-removal
// maintenance. // maintenance.
func (m *txSortedMap) Forward(threshold uint64) types.Transactions { func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
m.m.Lock()
defer m.m.Unlock()
var removed types.Transactions var removed types.Transactions
// Pop off heap items until the threshold is reached // Pop off heap items until the threshold is reached
@ -92,10 +127,15 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
removed = append(removed, m.items[nonce]) removed = append(removed, m.items[nonce])
delete(m.items, nonce) delete(m.items, nonce)
} }
// If we had a cached order, shift the front // If we had a cached order, shift the front
m.cacheMu.Lock()
if m.cache != nil { if m.cache != nil {
hitCacheCounter.Inc(1)
m.cache = m.cache[len(removed):] m.cache = m.cache[len(removed):]
} }
m.cacheMu.Unlock()
return removed return removed
} }
@ -105,21 +145,51 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
// If you want to do several consecutive filterings, it's therefore better to first // If you want to do several consecutive filterings, it's therefore better to first
// do a .filter(func1) followed by .Filter(func2) or reheap() // do a .filter(func1) followed by .Filter(func2) or reheap()
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
m.m.Lock()
defer m.m.Unlock()
removed := m.filter(filter) removed := m.filter(filter)
// If transactions were removed, the heap and cache are ruined // If transactions were removed, the heap and cache are ruined
if len(removed) > 0 { if len(removed) > 0 {
m.reheap() m.reheap(false)
} }
return removed return removed
} }
func (m *txSortedMap) reheap() { func (m *txSortedMap) reheap(withRlock bool) {
*m.index = make([]uint64, 0, len(m.items)) index := make(nonceHeap, 0, len(m.items))
for nonce := range m.items {
*m.index = append(*m.index, nonce) if withRlock {
m.m.RLock()
log.Info("[DEBUG] Acquired lock over txpool map while performing reheap")
} }
heap.Init(m.index)
for nonce := range m.items {
index = append(index, nonce)
}
if withRlock {
m.m.RUnlock()
}
heap.Init(&index)
if withRlock {
m.m.Lock()
}
m.index = &index
if withRlock {
m.m.Unlock()
}
m.cacheMu.Lock()
m.cache = nil m.cache = nil
m.isEmpty = true
m.cacheMu.Unlock()
resetCacheGauge.Inc(1)
} }
// filter is identical to Filter, but **does not** regenerate the heap. This method // filter is identical to Filter, but **does not** regenerate the heap. This method
@ -135,7 +205,12 @@ func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transac
} }
} }
if len(removed) > 0 { if len(removed) > 0 {
m.cacheMu.Lock()
m.cache = nil m.cache = nil
m.isEmpty = true
m.cacheMu.Unlock()
resetCacheGauge.Inc(1)
} }
return removed return removed
} }
@ -143,45 +218,66 @@ func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transac
// Cap places a hard limit on the number of items, returning all transactions // Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit. // exceeding that limit.
func (m *txSortedMap) Cap(threshold int) types.Transactions { func (m *txSortedMap) Cap(threshold int) types.Transactions {
m.m.Lock()
defer m.m.Unlock()
// Short circuit if the number of items is under the limit // Short circuit if the number of items is under the limit
if len(m.items) <= threshold { if len(m.items) <= threshold {
return nil return nil
} }
// Otherwise gather and drop the highest nonce'd transactions // Otherwise gather and drop the highest nonce'd transactions
var drops types.Transactions var drops types.Transactions
sort.Sort(*m.index) sort.Sort(*m.index)
for size := len(m.items); size > threshold; size-- { for size := len(m.items); size > threshold; size-- {
drops = append(drops, m.items[(*m.index)[size-1]]) drops = append(drops, m.items[(*m.index)[size-1]])
delete(m.items, (*m.index)[size-1]) delete(m.items, (*m.index)[size-1])
} }
*m.index = (*m.index)[:threshold] *m.index = (*m.index)[:threshold]
heap.Init(m.index) heap.Init(m.index)
// If we had a cache, shift the back // If we had a cache, shift the back
m.cacheMu.Lock()
if m.cache != nil { if m.cache != nil {
m.cache = m.cache[:len(m.cache)-len(drops)] m.cache = m.cache[:len(m.cache)-len(drops)]
} }
m.cacheMu.Unlock()
return drops return drops
} }
// Remove deletes a transaction from the maintained map, returning whether the // Remove deletes a transaction from the maintained map, returning whether the
// transaction was found. // transaction was found.
func (m *txSortedMap) Remove(nonce uint64) bool { func (m *txSortedMap) Remove(nonce uint64) bool {
m.m.Lock()
defer m.m.Unlock()
// Short circuit if no transaction is present // Short circuit if no transaction is present
_, ok := m.items[nonce] _, ok := m.items[nonce]
if !ok { if !ok {
return false return false
} }
// Otherwise delete the transaction and fix the heap index // Otherwise delete the transaction and fix the heap index
for i := 0; i < m.index.Len(); i++ { for i := 0; i < m.index.Len(); i++ {
if (*m.index)[i] == nonce { if (*m.index)[i] == nonce {
heap.Remove(m.index, i) heap.Remove(m.index, i)
break break
} }
} }
delete(m.items, nonce) delete(m.items, nonce)
m.cacheMu.Lock()
m.cache = nil m.cache = nil
m.isEmpty = true
m.cacheMu.Unlock()
resetCacheGauge.Inc(1)
return true return true
} }
@ -194,55 +290,129 @@ func (m *txSortedMap) Remove(nonce uint64) bool {
// prevent getting into and invalid state. This is not something that should ever // prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing! // happen but better to be self correcting than failing!
func (m *txSortedMap) Ready(start uint64) types.Transactions { func (m *txSortedMap) Ready(start uint64) types.Transactions {
m.m.Lock()
defer m.m.Unlock()
// Short circuit if no transactions are available // Short circuit if no transactions are available
if m.index.Len() == 0 || (*m.index)[0] > start { if m.index.Len() == 0 || (*m.index)[0] > start {
return nil return nil
} }
// Otherwise start accumulating incremental transactions // Otherwise start accumulating incremental transactions
var ready types.Transactions var ready types.Transactions
for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ {
ready = append(ready, m.items[next]) ready = append(ready, m.items[next])
delete(m.items, next) delete(m.items, next)
heap.Pop(m.index) heap.Pop(m.index)
} }
m.cacheMu.Lock()
m.cache = nil m.cache = nil
m.isEmpty = true
m.cacheMu.Unlock()
resetCacheGauge.Inc(1)
return ready return ready
} }
// Len returns the length of the transaction map. // Len returns the length of the transaction map.
func (m *txSortedMap) Len() int { func (m *txSortedMap) Len() int {
m.m.RLock()
defer m.m.RUnlock()
return len(m.items) return len(m.items)
} }
func (m *txSortedMap) flatten() types.Transactions { func (m *txSortedMap) flatten() types.Transactions {
// If the sorting was not cached yet, create and cache it // If the sorting was not cached yet, create and cache it
if m.cache == nil { m.cacheMu.Lock()
m.cache = make(types.Transactions, 0, len(m.items)) defer m.cacheMu.Unlock()
if m.isEmpty {
m.isEmpty = false // to simulate sync.Once
m.cacheMu.Unlock()
m.m.RLock()
cache := make(types.Transactions, 0, len(m.items))
for _, tx := range m.items { for _, tx := range m.items {
m.cache = append(m.cache, tx) cache = append(cache, tx)
} }
sort.Sort(types.TxByNonce(m.cache))
m.m.RUnlock()
// exclude sorting from locks
sort.Sort(types.TxByNonce(cache))
m.cacheMu.Lock()
m.cache = cache
reinitCacheGauge.Inc(1)
missCacheCounter.Inc(1)
} else {
hitCacheCounter.Inc(1)
} }
return m.cache return m.cache
} }
func (m *txSortedMap) lastElement() *types.Transaction {
// If the sorting was not cached yet, create and cache it
m.cacheMu.Lock()
defer m.cacheMu.Unlock()
cache := m.cache
if m.isEmpty {
m.isEmpty = false // to simulate sync.Once
m.cacheMu.Unlock()
m.m.RLock()
cache = make(types.Transactions, 0, len(m.items))
for _, tx := range m.items {
cache = append(cache, tx)
}
m.m.RUnlock()
// exclude sorting from locks
sort.Sort(types.TxByNonce(cache))
m.cacheMu.Lock()
m.cache = cache
reinitCacheGauge.Inc(1)
missCacheCounter.Inc(1)
} else {
hitCacheCounter.Inc(1)
}
ln := len(cache)
if ln == 0 {
return nil
}
return cache[len(cache)-1]
}
// Flatten creates a nonce-sorted slice of transactions based on the loosely // Flatten creates a nonce-sorted slice of transactions based on the loosely
// sorted internal representation. The result of the sorting is cached in case // sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents. // it's requested again before any modifications are made to the contents.
func (m *txSortedMap) Flatten() types.Transactions { func (m *txSortedMap) Flatten() types.Transactions {
// Copy the cache to prevent accidental modifications // Copy the cache to prevent accidental modifications
cache := m.flatten() return m.flatten()
txs := make(types.Transactions, len(cache))
copy(txs, cache)
return txs
} }
// LastElement returns the last element of a flattened list, thus, the // LastElement returns the last element of a flattened list, thus, the
// transaction with the highest nonce // transaction with the highest nonce
func (m *txSortedMap) LastElement() *types.Transaction { func (m *txSortedMap) LastElement() *types.Transaction {
cache := m.flatten() return m.lastElement()
return cache[len(cache)-1]
} }
// txList is a "list" of transactions belonging to an account, sorted by account // txList is a "list" of transactions belonging to an account, sorted by account
@ -253,8 +423,9 @@ type txList struct {
strict bool // Whether nonces are strictly continuous or not strict bool // Whether nonces are strictly continuous or not
txs *txSortedMap // Heap indexed sorted hash map of the transactions txs *txSortedMap // Heap indexed sorted hash map of the transactions
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) costcap *uint256.Int // Price of the highest costing transaction (reset only if exceeds balance)
gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
totalcost *big.Int // Total cost of all transactions in the list
} }
// newTxList create a new transaction list for maintaining nonce-indexable fast, // newTxList create a new transaction list for maintaining nonce-indexable fast,
@ -263,7 +434,7 @@ func newTxList(strict bool) *txList {
return &txList{ return &txList{
strict: strict, strict: strict,
txs: newTxSortedMap(), txs: newTxSortedMap(),
costcap: new(big.Int), totalcost: new(big.Int),
} }
} }
@ -285,31 +456,41 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 { if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 {
return false, nil return false, nil
} }
// thresholdFeeCap = oldFC * (100 + priceBump) / 100 // thresholdFeeCap = oldFC * (100 + priceBump) / 100
a := big.NewInt(100 + int64(priceBump)) a := uint256.NewInt(100 + priceBump)
aFeeCap := new(big.Int).Mul(a, old.GasFeeCap()) aFeeCap := uint256.NewInt(0).Mul(a, old.GasFeeCapUint())
aTip := a.Mul(a, old.GasTipCap()) aTip := a.Mul(a, old.GasTipCapUint())
// thresholdTip = oldTip * (100 + priceBump) / 100 // thresholdTip = oldTip * (100 + priceBump) / 100
b := big.NewInt(100) b := cmath.U100
thresholdFeeCap := aFeeCap.Div(aFeeCap, b) thresholdFeeCap := aFeeCap.Div(aFeeCap, b)
thresholdTip := aTip.Div(aTip, b) thresholdTip := aTip.Div(aTip, b)
// We have to ensure that both the new fee cap and tip are higher than the // We have to ensure that both the new fee cap and tip are higher than the
// old ones as well as checking the percentage threshold to ensure that // old ones as well as checking the percentage threshold to ensure that
// this is accurate for low (Wei-level) gas price replacements. // this is accurate for low (Wei-level) gas price replacements.
if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 { if tx.GasFeeCapUIntLt(thresholdFeeCap) || tx.GasTipCapUIntLt(thresholdTip) {
return false, nil return false, nil
} }
// Old is being replaced, subtract old cost
l.subTotalCost([]*types.Transaction{old})
} }
// Add new tx cost to totalcost
l.totalcost.Add(l.totalcost, tx.Cost())
// Otherwise overwrite the old transaction with the current one // Otherwise overwrite the old transaction with the current one
l.txs.Put(tx) l.txs.Put(tx)
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
if cost := tx.CostUint(); l.costcap == nil || l.costcap.Lt(cost) {
l.costcap = cost l.costcap = cost
} }
if gas := tx.Gas(); l.gascap < gas { if gas := tx.Gas(); l.gascap < gas {
l.gascap = gas l.gascap = gas
} }
return true, old return true, old
} }
@ -317,7 +498,10 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
// provided threshold. Every removed transaction is returned for any post-removal // provided threshold. Every removed transaction is returned for any post-removal
// maintenance. // maintenance.
func (l *txList) Forward(threshold uint64) types.Transactions { func (l *txList) Forward(threshold uint64) types.Transactions {
return l.txs.Forward(threshold) txs := l.txs.Forward(threshold)
l.subTotalCost(txs)
return txs
} }
// Filter removes all transactions from the list with a cost or gas limit higher // Filter removes all transactions from the list with a cost or gas limit higher
@ -329,17 +513,20 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
// a point in calculating all the costs or if the balance covers all. If the threshold // a point in calculating all the costs or if the balance covers all. If the threshold
// is lower than the costgas cap, the caps will be reset to a new high after removing // is lower than the costgas cap, the caps will be reset to a new high after removing
// the newly invalidated transactions. // the newly invalidated transactions.
func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) { func (l *txList) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
// If all transactions are below the threshold, short circuit // If all transactions are below the threshold, short circuit
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { if cmath.U256LTE(l.costcap, costLimit) && l.gascap <= gasLimit {
return nil, nil return nil, nil
} }
l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
l.costcap = costLimit.Clone() // Lower the caps to the thresholds
l.gascap = gasLimit l.gascap = gasLimit
// Filter out all the transactions above the account's funds // Filter out all the transactions above the account's funds
cost := uint256.NewInt(0)
removed := l.txs.Filter(func(tx *types.Transaction) bool { removed := l.txs.Filter(func(tx *types.Transaction) bool {
return tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit) > 0 cost.SetFromBig(tx.Cost())
return tx.Gas() > gasLimit || cost.Gt(costLimit)
}) })
if len(removed) == 0 { if len(removed) == 0 {
@ -354,16 +541,27 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions
lowest = nonce lowest = nonce
} }
} }
l.txs.m.Lock()
invalids = l.txs.filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) invalids = l.txs.filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
l.txs.m.Unlock()
} }
l.txs.reheap() // Reset total cost
l.subTotalCost(removed)
l.subTotalCost(invalids)
l.txs.reheap(true)
return removed, invalids return removed, invalids
} }
// Cap places a hard limit on the number of items, returning all transactions // Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit. // exceeding that limit.
func (l *txList) Cap(threshold int) types.Transactions { func (l *txList) Cap(threshold int) types.Transactions {
return l.txs.Cap(threshold) txs := l.txs.Cap(threshold)
l.subTotalCost(txs)
return txs
} }
// Remove deletes a transaction from the maintained list, returning whether the // Remove deletes a transaction from the maintained list, returning whether the
@ -375,9 +573,14 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
if removed := l.txs.Remove(nonce); !removed { if removed := l.txs.Remove(nonce); !removed {
return false, nil return false, nil
} }
l.subTotalCost([]*types.Transaction{tx})
// In strict mode, filter out non-executable transactions // In strict mode, filter out non-executable transactions
if l.strict { if l.strict {
return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce }) txs := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce })
l.subTotalCost(txs)
return true, txs
} }
return true, nil return true, nil
} }
@ -390,7 +593,10 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
// prevent getting into and invalid state. This is not something that should ever // prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing! // happen but better to be self correcting than failing!
func (l *txList) Ready(start uint64) types.Transactions { func (l *txList) Ready(start uint64) types.Transactions {
return l.txs.Ready(start) txs := l.txs.Ready(start)
l.subTotalCost(txs)
return txs
} }
// Len returns the length of the transaction list. // Len returns the length of the transaction list.
@ -416,13 +622,26 @@ func (l *txList) LastElement() *types.Transaction {
return l.txs.LastElement() return l.txs.LastElement()
} }
func (l *txList) Has(nonce uint64) bool {
return l != nil && l.txs.items[nonce] != nil
}
// subTotalCost subtracts the cost of the given transactions from the
// total cost of all transactions.
func (l *txList) subTotalCost(txs []*types.Transaction) {
for _, tx := range txs {
l.totalcost.Sub(l.totalcost, tx.Cost())
}
}
// priceHeap is a heap.Interface implementation over transactions for retrieving // priceHeap is a heap.Interface implementation over transactions for retrieving
// price-sorted transactions to discard when the pool fills up. If baseFee is set // price-sorted transactions to discard when the pool fills up. If baseFee is set
// then the heap is sorted based on the effective tip based on the given base fee. // then the heap is sorted based on the effective tip based on the given base fee.
// If baseFee is nil then the sorting is based on gasFeeCap. // If baseFee is nil then the sorting is based on gasFeeCap.
type priceHeap struct { type priceHeap struct {
baseFee *big.Int // heap should always be re-sorted after baseFee is changed baseFee *uint256.Int // heap should always be re-sorted after baseFee is changed
list []*types.Transaction list []*types.Transaction
baseFeeMu sync.RWMutex
} }
func (h *priceHeap) Len() int { return len(h.list) } func (h *priceHeap) Len() int { return len(h.list) }
@ -440,16 +659,24 @@ func (h *priceHeap) Less(i, j int) bool {
} }
func (h *priceHeap) cmp(a, b *types.Transaction) int { func (h *priceHeap) cmp(a, b *types.Transaction) int {
h.baseFeeMu.RLock()
if h.baseFee != nil { if h.baseFee != nil {
// Compare effective tips if baseFee is specified // Compare effective tips if baseFee is specified
if c := a.EffectiveGasTipCmp(b, h.baseFee); c != 0 { if c := a.EffectiveGasTipTxUintCmp(b, h.baseFee); c != 0 {
h.baseFeeMu.RUnlock()
return c return c
} }
} }
h.baseFeeMu.RUnlock()
// Compare fee caps if baseFee is not specified or effective tips are equal // Compare fee caps if baseFee is not specified or effective tips are equal
if c := a.GasFeeCapCmp(b); c != 0 { if c := a.GasFeeCapCmp(b); c != 0 {
return c return c
} }
// Compare tips if effective tips and fee caps are equal // Compare tips if effective tips and fee caps are equal
return a.GasTipCapCmp(b) return a.GasTipCapCmp(b)
} }
@ -629,7 +856,10 @@ func (l *txPricedList) Reheap() {
// SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not // SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not
// necessary to call right before SetBaseFee when processing a new block. // necessary to call right before SetBaseFee when processing a new block.
func (l *txPricedList) SetBaseFee(baseFee *big.Int) { func (l *txPricedList) SetBaseFee(baseFee *uint256.Int) {
l.urgent.baseFeeMu.Lock()
l.urgent.baseFee = baseFee l.urgent.baseFee = baseFee
l.urgent.baseFeeMu.Unlock()
l.Reheap() l.Reheap()
} }

View file

@ -17,10 +17,11 @@
package core package core
import ( import (
"math/big"
"math/rand" "math/rand"
"testing" "testing"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
@ -59,11 +60,15 @@ func BenchmarkTxListAdd(b *testing.B) {
for i := 0; i < len(txs); i++ { for i := 0; i < len(txs); i++ {
txs[i] = transaction(uint64(i), 0, key) txs[i] = transaction(uint64(i), 0, key)
} }
// Insert the transactions in a random order // Insert the transactions in a random order
priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit)) priceLimit := uint256.NewInt(DefaultTxPoolConfig.PriceLimit)
b.ResetTimer() b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
list := newTxList(true) list := newTxList(true)
for _, v := range rand.Perm(len(txs)) { for _, v := range rand.Perm(len(txs)) {
list.Add(txs[v], DefaultTxPoolConfig.PriceBump) list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump) list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

229
core/txpool2_test.go Normal file
View file

@ -0,0 +1,229 @@
// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"crypto/ecdsa"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
)
func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(value), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
return tx
}
func count(t *testing.T, pool *TxPool) (pending int, queued int) {
t.Helper()
pending, queued = pool.stats()
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
return pending, queued
}
func fillPool(t *testing.T, pool *TxPool) {
t.Helper()
// Create a number of test accounts, fund them and make transactions
executableTxs := types.Transactions{}
nonExecutableTxs := types.Transactions{}
for i := 0; i < 384; i++ {
key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000))
// Add executable ones
for j := 0; j < int(pool.config.AccountSlots); j++ {
executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key))
}
}
// Import the batch and verify that limits have been enforced
pool.AddRemotesSync(executableTxs)
pool.AddRemotesSync(nonExecutableTxs)
pending, queued := pool.Stats()
slots := pool.all.Slots()
// sanity-check that the test prerequisites are ok (pending full)
if have, want := pending, slots; have != want {
t.Fatalf("have %d, want %d", have, want)
}
if have, want := queued, 0; have != want {
t.Fatalf("have %d, want %d", have, want)
}
t.Logf("pool.config: GlobalSlots=%d, GlobalQueue=%d\n", pool.config.GlobalSlots, pool.config.GlobalQueue)
t.Logf("pending: %d queued: %d, all: %d\n", pending, queued, slots)
}
// Tests that if a batch high-priced of non-executables arrive, they do not kick out
// executable transactions
func TestTransactionFutureAttack(t *testing.T) {
t.Parallel()
// Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.GlobalQueue = 100
config.GlobalSlots = 100
pool := NewTxPool(config, eip1559Config, blockchain)
defer pool.Stop()
fillPool(t, pool)
pending, _ := pool.Stats()
// Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops
{
key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000))
futureTxs := types.Transactions{}
for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ {
futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 100000, big.NewInt(500), key))
}
for i := 0; i < 5; i++ {
pool.AddRemotesSync(futureTxs)
newPending, newQueued := count(t, pool)
t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots())
}
}
newPending, _ := pool.Stats()
// Pending should not have been touched
if have, want := newPending, pending; have < want {
t.Errorf("wrong pending-count, have %d, want %d (GlobalSlots: %d)",
have, want, pool.config.GlobalSlots)
}
}
// Tests that if a batch high-priced of non-executables arrive, they do not kick out
// executable transactions
func TestTransactionFuture1559(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain)
defer pool.Stop()
// Create a number of test accounts, fund them and make transactions
fillPool(t, pool)
pending, _ := pool.Stats()
// Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops
{
key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000))
futureTxs := types.Transactions{}
for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ {
futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key))
}
pool.AddRemotesSync(futureTxs)
}
newPending, _ := pool.Stats()
// Pending should not have been touched
if have, want := newPending, pending; have != want {
t.Errorf("Wrong pending-count, have %d, want %d (GlobalSlots: %d)",
have, want, pool.config.GlobalSlots)
}
}
// Tests that if a batch of balance-overdraft txs arrive, they do not kick out
// executable transactions
func TestTransactionZAttack(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain)
defer pool.Stop()
// Create a number of test accounts, fund them and make transactions
fillPool(t, pool)
countInvalidPending := func() int {
t.Helper()
var ivpendingNum int
pendingtxs, _ := pool.Content()
for account, txs := range pendingtxs {
cur_balance := new(big.Int).Set(pool.currentState.GetBalance(account))
for _, tx := range txs {
if cur_balance.Cmp(tx.Value()) <= 0 {
ivpendingNum++
} else {
cur_balance.Sub(cur_balance, tx.Value())
}
}
}
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
return ivpendingNum
}
ivPending := countInvalidPending()
t.Logf("invalid pending: %d\n", ivPending)
// Now, DETER-Z attack starts, let's add a bunch of expensive non-executables (from N accounts) along with balance-overdraft txs (from one account), and see if the pending-count drops
for j := 0; j < int(pool.config.GlobalQueue); j++ {
futureTxs := types.Transactions{}
key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000))
futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 21000, big.NewInt(500), key))
pool.AddRemotesSync(futureTxs)
}
overDraftTxs := types.Transactions{}
{
key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000))
for j := 0; j < int(pool.config.GlobalSlots); j++ {
overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 60000000000, 21000, big.NewInt(500), key))
}
}
pool.AddRemotesSync(overDraftTxs)
pool.AddRemotesSync(overDraftTxs)
pool.AddRemotesSync(overDraftTxs)
pool.AddRemotesSync(overDraftTxs)
pool.AddRemotesSync(overDraftTxs)
newPending, newQueued := count(t, pool)
newIvPending := countInvalidPending()
t.Logf("pool.all.Slots(): %d\n", pool.all.Slots())
t.Logf("pending: %d queued: %d, all: %d\n", newPending, newQueued, pool.all.Slots())
t.Logf("invalid pending: %d\n", newIvPending)
// Pending should not have been touched
if newIvPending != ivPending {
t.Errorf("Wrong invalid pending-count, have %d, want %d (GlobalSlots: %d, queued: %d)",
newIvPending, ivPending, pool.config.GlobalSlots, newQueued)
}
}

View file

@ -19,6 +19,8 @@ package types
import ( import (
"math/big" "math/big"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
@ -47,6 +49,7 @@ type AccessListTx struct {
ChainID *big.Int // destination chain ID ChainID *big.Int // destination chain ID
Nonce uint64 // nonce of sender account Nonce uint64 // nonce of sender account
GasPrice *big.Int // wei per gas GasPrice *big.Int // wei per gas
gasPriceUint256 *uint256.Int // wei per gas
Gas uint64 // gas limit Gas uint64 // gas limit
To *common.Address `rlp:"nil"` // nil means contract creation To *common.Address `rlp:"nil"` // nil means contract creation
Value *big.Int // wei amount Value *big.Int // wei amount
@ -80,6 +83,12 @@ func (tx *AccessListTx) copy() TxData {
} }
if tx.GasPrice != nil { if tx.GasPrice != nil {
cpy.GasPrice.Set(tx.GasPrice) cpy.GasPrice.Set(tx.GasPrice)
if cpy.gasPriceUint256 != nil {
cpy.gasPriceUint256.Set(tx.gasPriceUint256)
} else {
cpy.gasPriceUint256, _ = uint256.FromBig(tx.GasPrice)
}
} }
if tx.V != nil { if tx.V != nil {
cpy.V.Set(tx.V) cpy.V.Set(tx.V)
@ -100,8 +109,36 @@ func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
func (tx *AccessListTx) data() []byte { return tx.Data } func (tx *AccessListTx) data() []byte { return tx.Data }
func (tx *AccessListTx) gas() uint64 { return tx.Gas } func (tx *AccessListTx) gas() uint64 { return tx.Gas }
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice } func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
func (tx *AccessListTx) gasPriceU256() *uint256.Int {
if tx.gasPriceUint256 != nil {
return tx.gasPriceUint256
}
tx.gasPriceUint256, _ = uint256.FromBig(tx.GasPrice)
return tx.gasPriceUint256
}
func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice } func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice }
func (tx *AccessListTx) gasTipCapU256() *uint256.Int {
if tx.gasPriceUint256 != nil {
return tx.gasPriceUint256
}
tx.gasPriceUint256, _ = uint256.FromBig(tx.GasPrice)
return tx.gasPriceUint256
}
func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *AccessListTx) gasFeeCapU256() *uint256.Int {
if tx.gasPriceUint256 != nil {
return tx.gasPriceUint256
}
tx.gasPriceUint256, _ = uint256.FromBig(tx.GasPrice)
return tx.gasPriceUint256
}
func (tx *AccessListTx) value() *big.Int { return tx.Value } func (tx *AccessListTx) value() *big.Int { return tx.Value }
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
func (tx *AccessListTx) to() *common.Address { return tx.To } func (tx *AccessListTx) to() *common.Address { return tx.To }

View file

@ -19,6 +19,8 @@ package types
import ( import (
"math/big" "math/big"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
@ -26,7 +28,9 @@ type DynamicFeeTx struct {
ChainID *big.Int ChainID *big.Int
Nonce uint64 Nonce uint64
GasTipCap *big.Int // a.k.a. maxPriorityFeePerGas GasTipCap *big.Int // a.k.a. maxPriorityFeePerGas
gasTipCapUint256 *uint256.Int // a.k.a. maxPriorityFeePerGas
GasFeeCap *big.Int // a.k.a. maxFeePerGas GasFeeCap *big.Int // a.k.a. maxFeePerGas
gasFeeCapUint256 *uint256.Int // a.k.a. maxFeePerGas
Gas uint64 Gas uint64
To *common.Address `rlp:"nil"` // nil means contract creation To *common.Address `rlp:"nil"` // nil means contract creation
Value *big.Int Value *big.Int
@ -65,9 +69,21 @@ func (tx *DynamicFeeTx) copy() TxData {
} }
if tx.GasTipCap != nil { if tx.GasTipCap != nil {
cpy.GasTipCap.Set(tx.GasTipCap) cpy.GasTipCap.Set(tx.GasTipCap)
if cpy.gasTipCapUint256 != nil {
cpy.gasTipCapUint256.Set(tx.gasTipCapUint256)
} else {
cpy.gasTipCapUint256, _ = uint256.FromBig(tx.GasTipCap)
}
} }
if tx.GasFeeCap != nil { if tx.GasFeeCap != nil {
cpy.GasFeeCap.Set(tx.GasFeeCap) cpy.GasFeeCap.Set(tx.GasFeeCap)
if cpy.gasFeeCapUint256 != nil {
cpy.gasFeeCapUint256.Set(tx.gasFeeCapUint256)
} else {
cpy.gasFeeCapUint256, _ = uint256.FromBig(tx.GasFeeCap)
}
} }
if tx.V != nil { if tx.V != nil {
cpy.V.Set(tx.V) cpy.V.Set(tx.V)
@ -88,8 +104,35 @@ func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
func (tx *DynamicFeeTx) data() []byte { return tx.Data } func (tx *DynamicFeeTx) data() []byte { return tx.Data }
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas } func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap } func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
func (tx *DynamicFeeTx) gasFeeCapU256() *uint256.Int {
if tx.gasFeeCapUint256 != nil {
return tx.gasFeeCapUint256
}
tx.gasFeeCapUint256, _ = uint256.FromBig(tx.GasFeeCap)
return tx.gasFeeCapUint256
}
func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap } func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap }
func (tx *DynamicFeeTx) gasTipCapU256() *uint256.Int {
if tx.gasTipCapUint256 != nil {
return tx.gasTipCapUint256
}
tx.gasTipCapUint256, _ = uint256.FromBig(tx.GasTipCap)
return tx.gasTipCapUint256
}
func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap }
func (tx *DynamicFeeTx) gasPriceU256() *uint256.Int {
if tx.gasFeeCapUint256 != nil {
return tx.gasTipCapUint256
}
tx.gasFeeCapUint256, _ = uint256.FromBig(tx.GasFeeCap)
return tx.gasFeeCapUint256
}
func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
func (tx *DynamicFeeTx) to() *common.Address { return tx.To } func (tx *DynamicFeeTx) to() *common.Address { return tx.To }

View file

@ -19,6 +19,8 @@ package types
import ( import (
"math/big" "math/big"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
@ -26,6 +28,7 @@ import (
type LegacyTx struct { type LegacyTx struct {
Nonce uint64 // nonce of sender account Nonce uint64 // nonce of sender account
GasPrice *big.Int // wei per gas GasPrice *big.Int // wei per gas
gasPriceUint256 *uint256.Int // wei per gas
Gas uint64 // gas limit Gas uint64 // gas limit
To *common.Address `rlp:"nil"` // nil means contract creation To *common.Address `rlp:"nil"` // nil means contract creation
Value *big.Int // wei amount Value *big.Int // wei amount
@ -77,6 +80,12 @@ func (tx *LegacyTx) copy() TxData {
} }
if tx.GasPrice != nil { if tx.GasPrice != nil {
cpy.GasPrice.Set(tx.GasPrice) cpy.GasPrice.Set(tx.GasPrice)
if cpy.gasPriceUint256 != nil {
cpy.gasPriceUint256.Set(tx.gasPriceUint256)
} else {
cpy.gasPriceUint256, _ = uint256.FromBig(tx.GasPrice)
}
} }
if tx.V != nil { if tx.V != nil {
cpy.V.Set(tx.V) cpy.V.Set(tx.V)
@ -97,8 +106,35 @@ func (tx *LegacyTx) accessList() AccessList { return nil }
func (tx *LegacyTx) data() []byte { return tx.Data } func (tx *LegacyTx) data() []byte { return tx.Data }
func (tx *LegacyTx) gas() uint64 { return tx.Gas } func (tx *LegacyTx) gas() uint64 { return tx.Gas }
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice } func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
func (tx *LegacyTx) gasPriceU256() *uint256.Int {
if tx.gasPriceUint256 != nil {
return tx.gasPriceUint256
}
tx.gasPriceUint256, _ = uint256.FromBig(tx.GasPrice)
return tx.gasPriceUint256
}
func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice } func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice }
func (tx *LegacyTx) gasTipCapU256() *uint256.Int {
if tx.gasPriceUint256 != nil {
return tx.gasPriceUint256
}
tx.gasPriceUint256, _ = uint256.FromBig(tx.GasPrice)
return tx.gasPriceUint256
}
func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *LegacyTx) gasFeeCapU256() *uint256.Int {
if tx.gasPriceUint256 != nil {
return tx.gasPriceUint256
}
tx.gasPriceUint256, _ = uint256.FromBig(tx.GasPrice)
return tx.gasPriceUint256
}
func (tx *LegacyTx) value() *big.Int { return tx.Value } func (tx *LegacyTx) value() *big.Int { return tx.Value }
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
func (tx *LegacyTx) to() *common.Address { return tx.To } func (tx *LegacyTx) to() *common.Address { return tx.To }

View file

@ -25,6 +25,8 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -53,9 +55,9 @@ type Transaction struct {
time time.Time // Time first seen locally (spam avoidance) time time.Time // Time first seen locally (spam avoidance)
// caches // caches
hash atomic.Value hash atomic.Pointer[common.Hash]
size atomic.Value size atomic.Pointer[common.StorageSize]
from atomic.Value from atomic.Pointer[sigCache]
} }
// NewTx creates a new transaction. // NewTx creates a new transaction.
@ -77,8 +79,11 @@ type TxData interface {
data() []byte data() []byte
gas() uint64 gas() uint64
gasPrice() *big.Int gasPrice() *big.Int
gasPriceU256() *uint256.Int
gasTipCap() *big.Int gasTipCap() *big.Int
gasTipCapU256() *uint256.Int
gasFeeCap() *big.Int gasFeeCap() *big.Int
gasFeeCapU256() *uint256.Int
value() *big.Int value() *big.Int
nonce() uint64 nonce() uint64
to() *common.Address to() *common.Address
@ -194,7 +199,8 @@ func (tx *Transaction) setDecoded(inner TxData, size int) {
tx.inner = inner tx.inner = inner
tx.time = time.Now() tx.time = time.Now()
if size > 0 { if size > 0 {
tx.size.Store(common.StorageSize(size)) v := float64(size)
tx.size.Store((*common.StorageSize)(&v))
} }
} }
@ -266,15 +272,22 @@ func (tx *Transaction) Gas() uint64 { return tx.inner.gas() }
// GasPrice returns the gas price of the transaction. // GasPrice returns the gas price of the transaction.
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) } func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
func (tx *Transaction) GasPriceRef() *big.Int { return tx.inner.gasPrice() }
func (tx *Transaction) GasPriceUint() *uint256.Int { return tx.inner.gasPriceU256() }
// GasTipCap returns the gasTipCap per gas of the transaction. // GasTipCap returns the gasTipCap per gas of the transaction.
func (tx *Transaction) GasTipCap() *big.Int { return new(big.Int).Set(tx.inner.gasTipCap()) } func (tx *Transaction) GasTipCap() *big.Int { return new(big.Int).Set(tx.inner.gasTipCap()) }
func (tx *Transaction) GasTipCapRef() *big.Int { return tx.inner.gasTipCap() }
func (tx *Transaction) GasTipCapUint() *uint256.Int { return tx.inner.gasTipCapU256() }
// GasFeeCap returns the fee cap per gas of the transaction. // GasFeeCap returns the fee cap per gas of the transaction.
func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) } func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) }
func (tx *Transaction) GasFeeCapRef() *big.Int { return tx.inner.gasFeeCap() }
func (tx *Transaction) GasFeeCapUint() *uint256.Int { return tx.inner.gasFeeCapU256() }
// Value returns the ether amount of the transaction. // Value returns the ether amount of the transaction.
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) } func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
func (tx *Transaction) ValueRef() *big.Int { return tx.inner.value() }
// Nonce returns the sender account nonce of the transaction. // Nonce returns the sender account nonce of the transaction.
func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() } func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
@ -287,9 +300,19 @@ func (tx *Transaction) To() *common.Address {
// Cost returns gas * gasPrice + value. // Cost returns gas * gasPrice + value.
func (tx *Transaction) Cost() *big.Int { func (tx *Transaction) Cost() *big.Int {
total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())) gasPrice, _ := uint256.FromBig(tx.GasPriceRef())
total.Add(total, tx.Value()) gasPrice.Mul(gasPrice, uint256.NewInt(tx.Gas()))
return total value, _ := uint256.FromBig(tx.ValueRef())
return gasPrice.Add(gasPrice, value).ToBig()
}
func (tx *Transaction) CostUint() *uint256.Int {
gasPrice, _ := uint256.FromBig(tx.GasPriceRef())
gasPrice.Mul(gasPrice, uint256.NewInt(tx.Gas()))
value, _ := uint256.FromBig(tx.ValueRef())
return gasPrice.Add(gasPrice, value)
} }
// RawSignatureValues returns the V, R, S signature values of the transaction. // RawSignatureValues returns the V, R, S signature values of the transaction.
@ -303,11 +326,18 @@ func (tx *Transaction) GasFeeCapCmp(other *Transaction) int {
return tx.inner.gasFeeCap().Cmp(other.inner.gasFeeCap()) return tx.inner.gasFeeCap().Cmp(other.inner.gasFeeCap())
} }
// GasFeeCapIntCmp compares the fee cap of the transaction against the given fee cap.
func (tx *Transaction) GasFeeCapIntCmp(other *big.Int) int { func (tx *Transaction) GasFeeCapIntCmp(other *big.Int) int {
return tx.inner.gasFeeCap().Cmp(other) return tx.inner.gasFeeCap().Cmp(other)
} }
func (tx *Transaction) GasFeeCapUIntCmp(other *uint256.Int) int {
return tx.inner.gasFeeCapU256().Cmp(other)
}
func (tx *Transaction) GasFeeCapUIntLt(other *uint256.Int) bool {
return tx.inner.gasFeeCapU256().Lt(other)
}
// GasTipCapCmp compares the gasTipCap of two transactions. // GasTipCapCmp compares the gasTipCap of two transactions.
func (tx *Transaction) GasTipCapCmp(other *Transaction) int { func (tx *Transaction) GasTipCapCmp(other *Transaction) int {
return tx.inner.gasTipCap().Cmp(other.inner.gasTipCap()) return tx.inner.gasTipCap().Cmp(other.inner.gasTipCap())
@ -318,6 +348,14 @@ func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int {
return tx.inner.gasTipCap().Cmp(other) return tx.inner.gasTipCap().Cmp(other)
} }
func (tx *Transaction) GasTipCapUIntCmp(other *uint256.Int) int {
return tx.inner.gasTipCapU256().Cmp(other)
}
func (tx *Transaction) GasTipCapUIntLt(other *uint256.Int) bool {
return tx.inner.gasTipCapU256().Lt(other)
}
// EffectiveGasTip returns the effective miner gasTipCap for the given base fee. // EffectiveGasTip returns the effective miner gasTipCap for the given base fee.
// Note: if the effective gasTipCap is negative, this method returns both error // Note: if the effective gasTipCap is negative, this method returns both error
// the actual negative value, _and_ ErrGasFeeCapTooLow // the actual negative value, _and_ ErrGasFeeCapTooLow
@ -356,10 +394,73 @@ func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) i
return tx.EffectiveGasTipValue(baseFee).Cmp(other) return tx.EffectiveGasTipValue(baseFee).Cmp(other)
} }
func (tx *Transaction) EffectiveGasTipUintCmp(other *uint256.Int, baseFee *uint256.Int) int {
if baseFee == nil {
return tx.GasTipCapUIntCmp(other)
}
return tx.EffectiveGasTipValueUint(baseFee).Cmp(other)
}
func (tx *Transaction) EffectiveGasTipUintLt(other *uint256.Int, baseFee *uint256.Int) bool {
if baseFee == nil {
return tx.GasTipCapUIntLt(other)
}
return tx.EffectiveGasTipValueUint(baseFee).Lt(other)
}
func (tx *Transaction) EffectiveGasTipTxUintCmp(other *Transaction, baseFee *uint256.Int) int {
if baseFee == nil {
return tx.inner.gasTipCapU256().Cmp(other.inner.gasTipCapU256())
}
return tx.EffectiveGasTipValueUint(baseFee).Cmp(other.EffectiveGasTipValueUint(baseFee))
}
func (tx *Transaction) EffectiveGasTipValueUint(baseFee *uint256.Int) *uint256.Int {
effectiveTip, _ := tx.EffectiveGasTipUnit(baseFee)
return effectiveTip
}
func (tx *Transaction) EffectiveGasTipUnit(baseFee *uint256.Int) (*uint256.Int, error) {
if baseFee == nil {
return tx.GasFeeCapUint(), nil
}
var err error
gasFeeCap := tx.GasFeeCapUint().Clone()
if gasFeeCap.Lt(baseFee) {
err = ErrGasFeeCapTooLow
}
gasTipCapUint := tx.GasTipCapUint()
if gasFeeCap.Lt(gasTipCapUint) {
return gasFeeCap, err
}
if gasFeeCap.Lt(gasTipCapUint) && baseFee.IsZero() {
return gasFeeCap, err
}
gasFeeCap.Sub(gasFeeCap, baseFee)
if gasFeeCap.Gt(gasTipCapUint) || gasFeeCap.Eq(gasTipCapUint) {
gasFeeCap.Add(gasFeeCap, baseFee)
return gasTipCapUint, err
}
return gasFeeCap, err
}
// Hash returns the transaction hash. // Hash returns the transaction hash.
func (tx *Transaction) Hash() common.Hash { func (tx *Transaction) Hash() common.Hash {
if hash := tx.hash.Load(); hash != nil { if hash := tx.hash.Load(); hash != nil {
return hash.(common.Hash) return *hash
} }
var h common.Hash var h common.Hash
@ -368,7 +469,9 @@ func (tx *Transaction) Hash() common.Hash {
} else { } else {
h = prefixedRlpHash(tx.Type(), tx.inner) h = prefixedRlpHash(tx.Type(), tx.inner)
} }
tx.hash.Store(h)
tx.hash.Store(&h)
return h return h
} }
@ -376,11 +479,14 @@ func (tx *Transaction) Hash() common.Hash {
// encoding and returning it, or returning a previously cached value. // encoding and returning it, or returning a previously cached value.
func (tx *Transaction) Size() common.StorageSize { func (tx *Transaction) Size() common.StorageSize {
if size := tx.size.Load(); size != nil { if size := tx.size.Load(); size != nil {
return size.(common.StorageSize) return *size
} }
c := writeCounter(0) c := writeCounter(0)
rlp.Encode(&c, &tx.inner) rlp.Encode(&c, &tx.inner)
tx.size.Store(common.StorageSize(c)) tx.size.Store((*common.StorageSize)(&c))
return common.StorageSize(c) return common.StorageSize(c)
} }
@ -444,14 +550,14 @@ func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// TxWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap // TxWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap
type TxWithMinerFee struct { type TxWithMinerFee struct {
tx *Transaction tx *Transaction
minerFee *big.Int minerFee *uint256.Int
} }
// NewTxWithMinerFee creates a wrapped transaction, calculating the effective // NewTxWithMinerFee creates a wrapped transaction, calculating the effective
// miner gasTipCap if a base fee is provided. // miner gasTipCap if a base fee is provided.
// Returns error in case of a negative effective miner gasTipCap. // Returns error in case of a negative effective miner gasTipCap.
func NewTxWithMinerFee(tx *Transaction, baseFee *big.Int) (*TxWithMinerFee, error) { func NewTxWithMinerFee(tx *Transaction, baseFee *uint256.Int) (*TxWithMinerFee, error) {
minerFee, err := tx.EffectiveGasTip(baseFee) minerFee, err := tx.EffectiveGasTipUnit(baseFee)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -496,7 +602,7 @@ type TransactionsByPriceAndNonce struct {
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
heads TxByPriceAndTime // Next transaction for each unique account (price heap) heads TxByPriceAndTime // Next transaction for each unique account (price heap)
signer Signer // Signer for the set of transactions signer Signer // Signer for the set of transactions
baseFee *big.Int // Current base fee baseFee *uint256.Int // Current base fee
} }
// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve // NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
@ -504,6 +610,7 @@ type TransactionsByPriceAndNonce struct {
// //
// Note, the input map is reowned so the caller should not interact any more with // Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor. // if after providing it to the constructor.
/*
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, baseFee *big.Int) *TransactionsByPriceAndNonce { func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, baseFee *big.Int) *TransactionsByPriceAndNonce {
// Initialize a price and received time based heap with the head transactions // Initialize a price and received time based heap with the head transactions
heads := make(TxByPriceAndTime, 0, len(txs)) heads := make(TxByPriceAndTime, 0, len(txs))
@ -524,6 +631,39 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa
} }
heap.Init(&heads) heap.Init(&heads)
// Assemble and return the transaction set
return &TransactionsByPriceAndNonce{
txs: txs,
heads: heads,
signer: signer,
baseFee: baseFee,
}
}*/
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions, baseFee *uint256.Int) *TransactionsByPriceAndNonce {
// Initialize a price and received time based heap with the head transactions
heads := make(TxByPriceAndTime, 0, len(txs))
for from, accTxs := range txs {
if len(accTxs) == 0 {
continue
}
acc, _ := Sender(signer, accTxs[0])
wrapped, err := NewTxWithMinerFee(accTxs[0], baseFee)
// Remove transaction if sender doesn't match from, or if wrapping fails.
if acc != from || err != nil {
delete(txs, from)
continue
}
heads = append(heads, wrapped)
txs[from] = accTxs[1:]
}
heap.Init(&heads)
// Assemble and return the transaction set // Assemble and return the transaction set
return &TransactionsByPriceAndNonce{ return &TransactionsByPriceAndNonce{
txs: txs, txs: txs,

View file

@ -130,12 +130,11 @@ func MustSignNewTx(prv *ecdsa.PrivateKey, s Signer, txdata TxData) *Transaction
// not match the signer used in the current call. // not match the signer used in the current call.
func Sender(signer Signer, tx *Transaction) (common.Address, error) { func Sender(signer Signer, tx *Transaction) (common.Address, error) {
if sc := tx.from.Load(); sc != nil { if sc := tx.from.Load(); sc != nil {
sigCache := sc.(sigCache)
// If the signer used to derive from in a previous // If the signer used to derive from in a previous
// call is not the same as used current, invalidate // call is not the same as used current, invalidate
// the cache. // the cache.
if sigCache.signer.Equal(signer) { if sc.signer.Equal(signer) {
return sigCache.from, nil return sc.from, nil
} }
} }
@ -143,7 +142,9 @@ func Sender(signer Signer, tx *Transaction) (common.Address, error) {
if err != nil { if err != nil {
return common.Address{}, err return common.Address{}, err
} }
tx.from.Store(sigCache{signer: signer, from: addr})
tx.from.Store(&sigCache{signer: signer, from: addr})
return addr, nil return addr, nil
} }
@ -461,10 +462,10 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{ return rlpHash([]interface{}{
tx.Nonce(), tx.Nonce(),
tx.GasPrice(), tx.GasPriceRef(),
tx.Gas(), tx.Gas(),
tx.To(), tx.To(),
tx.Value(), tx.ValueRef(),
tx.Data(), tx.Data(),
}) })
} }

View file

@ -27,7 +27,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -272,14 +275,22 @@ func TestTransactionPriceNonceSort1559(t *testing.T) {
// Tests that transactions can be correctly sorted according to their price in // Tests that transactions can be correctly sorted according to their price in
// decreasing order, but at the same time with increasing nonces when issued by // decreasing order, but at the same time with increasing nonces when issued by
// the same account. // the same account.
func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) { //
//nolint:gocognit,thelper
func testTransactionPriceNonceSort(t *testing.T, baseFeeBig *big.Int) {
// Generate a batch of accounts to start with // Generate a batch of accounts to start with
keys := make([]*ecdsa.PrivateKey, 25) keys := make([]*ecdsa.PrivateKey, 25)
for i := 0; i < len(keys); i++ { for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey() keys[i], _ = crypto.GenerateKey()
} }
signer := LatestSignerForChainID(common.Big1) signer := LatestSignerForChainID(common.Big1)
var baseFee *uint256.Int
if baseFeeBig != nil {
baseFee = cmath.FromBig(baseFeeBig)
}
// Generate a batch of transactions with overlapping values, but shifted nonces // Generate a batch of transactions with overlapping values, but shifted nonces
groups := map[common.Address]Transactions{} groups := map[common.Address]Transactions{}
expectedCount := 0 expectedCount := 0
@ -308,7 +319,7 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
GasTipCap: big.NewInt(int64(rand.Intn(gasFeeCap + 1))), GasTipCap: big.NewInt(int64(rand.Intn(gasFeeCap + 1))),
Data: nil, Data: nil,
}) })
if count == 25 && int64(gasFeeCap) < baseFee.Int64() { if count == 25 && uint64(gasFeeCap) < baseFee.Uint64() {
count = i count = i
} }
} }
@ -341,12 +352,25 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce()) t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce())
} }
} }
// If the next tx has different from account, the price must be lower than the current one // If the next tx has different from account, the price must be lower than the current one
if i+1 < len(txs) { if i+1 < len(txs) {
next := txs[i+1] next := txs[i+1]
fromNext, _ := Sender(signer, next) fromNext, _ := Sender(signer, next)
tip, err := txi.EffectiveGasTip(baseFee) tip, err := txi.EffectiveGasTipUnit(baseFee)
nextTip, nextErr := next.EffectiveGasTip(baseFee) nextTip, nextErr := next.EffectiveGasTipUnit(baseFee)
tipBig, _ := txi.EffectiveGasTip(baseFeeBig)
nextTipBig, _ := next.EffectiveGasTip(baseFeeBig)
if tip.Cmp(cmath.FromBig(tipBig)) != 0 {
t.Fatalf("EffectiveGasTip incorrect. uint256 %q, big.Int %q, baseFee %q, baseFeeBig %q", tip.String(), tipBig.String(), baseFee.String(), baseFeeBig.String())
}
if nextTip.Cmp(cmath.FromBig(nextTipBig)) != 0 {
t.Fatalf("EffectiveGasTip next incorrect. uint256 %q, big.Int %q, baseFee %q, baseFeeBig %q", nextTip.String(), nextTipBig.String(), baseFee.String(), baseFeeBig.String())
}
if err != nil || nextErr != nil { if err != nil || nextErr != nil {
t.Errorf("error calculating effective tip") t.Errorf("error calculating effective tip")
} }

View file

@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/crypto/bn256" "github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
//lint:ignore SA1019 Needed for precompile big2 "github.com/holiman/big"
"golang.org/x/crypto/ripemd160" "golang.org/x/crypto/ripemd160"
) )
@ -266,6 +266,7 @@ var (
// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198 // modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198
// //
// def mult_complexity(x): // def mult_complexity(x):
//
// if x <= 64: return x ** 2 // if x <= 64: return x ** 2
// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 // elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072
// else: return x ** 2 // 16 + 480 * x - 199680 // else: return x ** 2 // 16 + 480 * x - 199680
@ -379,15 +380,24 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
} }
// Retrieve the operands and execute the exponentiation // Retrieve the operands and execute the exponentiation
var ( var (
base = new(big.Int).SetBytes(getData(input, 0, baseLen)) base = new(big2.Int).SetBytes(getData(input, 0, baseLen))
exp = new(big.Int).SetBytes(getData(input, baseLen, expLen)) exp = new(big2.Int).SetBytes(getData(input, baseLen, expLen))
mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen)) mod = new(big2.Int).SetBytes(getData(input, baseLen+expLen, modLen))
v []byte
) )
if mod.BitLen() == 0 {
switch {
case mod.BitLen() == 0:
// Modulo 0 is undefined, return zero // Modulo 0 is undefined, return zero
return common.LeftPadBytes([]byte{}, int(modLen)), nil return common.LeftPadBytes([]byte{}, int(modLen)), nil
case base.BitLen() == 1: // a bit length of 1 means it's 1 (or -1).
//If base == 1, then we can just return base % mod (if mod >= 1, which it is)
v = base.Mod(base, mod).Bytes()
default:
v = base.Exp(base, exp, mod).Bytes()
} }
return common.LeftPadBytes(base.Exp(base, exp, mod).Bytes(), int(modLen)), nil
return common.LeftPadBytes(v, int(modLen)), nil
} }
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point, // newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,

View file

@ -93,7 +93,7 @@ ethstats = "" # Reporting URL of a ethstats service (nodename:sec
vhosts = ["localhost"] # Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. vhosts = ["localhost"] # Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.
corsdomain = ["localhost"] # Comma separated list of domains from which to accept cross origin requests (browser enforced) corsdomain = ["localhost"] # Comma separated list of domains from which to accept cross origin requests (browser enforced)
[jsonrpc.timeouts] [jsonrpc.timeouts]
read = "30s" read = "10s"
write = "30s" write = "30s"
idle = "2m0s" idle = "2m0s"

View file

@ -236,11 +236,18 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
} }
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
return b.eth.txPool.AddLocal(signedTx) err := b.eth.txPool.AddLocal(signedTx)
if err != nil {
if unwrapped := errors.Unwrap(err); unwrapped != nil {
return unwrapped
}
}
return err
} }
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
pending := b.eth.txPool.Pending(false) pending := b.eth.txPool.Pending(context.Background(), false)
var txs types.Transactions var txs types.Transactions
for _, batch := range pending { for _, batch := range pending {
txs = append(txs, batch...) txs = append(txs, batch...)

View file

@ -26,6 +26,7 @@ func newCheckpointVerifier(verifyFn func(ctx context.Context, handler *ethHandle
) )
// check if we have the checkpoint blocks // check if we have the checkpoint blocks
//nolint:contextcheck
head := handler.ethAPI.BlockNumber() head := handler.ethAPI.BlockNumber()
if head < hexutil.Uint64(endBlock) { if head < hexutil.Uint64(endBlock) {
log.Debug("Head block behind checkpoint block", "head", head, "checkpoint end block", endBlock) log.Debug("Head block behind checkpoint block", "head", head, "checkpoint end block", endBlock)

View file

@ -17,6 +17,7 @@
package eth package eth
import ( import (
"context"
"errors" "errors"
"math" "math"
"math/big" "math/big"
@ -69,7 +70,7 @@ type txPool interface {
// Pending should return pending transactions. // Pending should return pending transactions.
// The slice should be modifiable by the caller. // The slice should be modifiable by the caller.
Pending(enforceTips bool) map[common.Address]types.Transactions Pending(ctx context.Context, enforceTips bool) map[common.Address]types.Transactions
// SubscribeNewTxsEvent should return an event subscription of // SubscribeNewTxsEvent should return an event subscription of
// NewTxsEvent and send events to the given channel. // NewTxsEvent and send events to the given channel.

View file

@ -17,6 +17,7 @@
package eth package eth
import ( import (
"context"
"math/big" "math/big"
"sort" "sort"
"sync" "sync"
@ -92,7 +93,7 @@ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error {
} }
// Pending returns all the transactions known to the pool // Pending returns all the transactions known to the pool
func (p *testTxPool) Pending(enforceTips bool) map[common.Address]types.Transactions { func (p *testTxPool) Pending(ctx context.Context, enforceTips bool) map[common.Address]types.Transactions {
p.lock.RLock() p.lock.RLock()
defer p.lock.RUnlock() defer p.lock.RUnlock()

View file

@ -17,6 +17,7 @@
package eth package eth
import ( import (
"context"
"errors" "errors"
"math/big" "math/big"
"sync/atomic" "sync/atomic"
@ -44,20 +45,24 @@ func (h *handler) syncTransactions(p *eth.Peer) {
// //
// TODO(karalabe): Figure out if we could get away with random order somehow // TODO(karalabe): Figure out if we could get away with random order somehow
var txs types.Transactions var txs types.Transactions
pending := h.txpool.Pending(false)
pending := h.txpool.Pending(context.Background(), false)
for _, batch := range pending { for _, batch := range pending {
txs = append(txs, batch...) txs = append(txs, batch...)
} }
if len(txs) == 0 { if len(txs) == 0 {
return return
} }
// The eth/65 protocol introduces proper transaction announcements, so instead // The eth/65 protocol introduces proper transaction announcements, so instead
// of dripping transactions across multiple peers, just send the entire list as // of dripping transactions across multiple peers, just send the entire list as
// an announcement and let the remote side decide what they need (likely nothing). // an announcement and let the remote side decide what they need (likely nothing).
hashes := make([]common.Hash, len(txs)) hashes := make([]common.Hash, len(txs))
for i, tx := range txs { for i, tx := range txs {
hashes[i] = tx.Hash() hashes[i] = tx.Hash()
} }
p.AsyncSendPooledTransactionHashes(hashes) p.AsyncSendPooledTransactionHashes(hashes)
} }

23
go.mod
View file

@ -12,13 +12,13 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.1.1 github.com/aws/aws-sdk-go-v2/config v1.1.1
github.com/aws/aws-sdk-go-v2/credentials v1.1.1 github.com/aws/aws-sdk-go-v2/credentials v1.1.1
github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1 github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1
github.com/btcsuite/btcd/btcec/v2 v2.1.2 github.com/btcsuite/btcd/btcec/v2 v2.1.3
github.com/cespare/cp v0.1.0 github.com/cespare/cp v0.1.0
github.com/cloudflare/cloudflare-go v0.14.0 github.com/cloudflare/cloudflare-go v0.14.0
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v1.8.0 github.com/deckarep/golang-set v1.8.0
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf github.com/docker/docker v1.6.1
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48
github.com/edsrzf/mmap-go v1.0.0 github.com/edsrzf/mmap-go v1.0.0
github.com/fatih/color v1.7.0 github.com/fatih/color v1.7.0
@ -37,6 +37,7 @@ require (
github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/go-bexpr v0.1.10
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/hashicorp/hcl/v2 v2.10.1 github.com/hashicorp/hcl/v2 v2.10.1
github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e
github.com/holiman/bloomfilter/v2 v2.0.3 github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.2.0 github.com/holiman/uint256 v1.2.0
github.com/huin/goupnp v1.0.3-0.20220313090229-ca81a64b4204 github.com/huin/goupnp v1.0.3-0.20220313090229-ca81a64b4204
@ -69,12 +70,12 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.2.0
go.opentelemetry.io/otel/sdk v1.2.0 go.opentelemetry.io/otel/sdk v1.2.0
go.uber.org/goleak v1.1.12 go.uber.org/goleak v1.1.12
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 golang.org/x/crypto v0.1.0
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 golang.org/x/sync v0.1.0
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 golang.org/x/sys v0.6.0
golang.org/x/text v0.3.7 golang.org/x/text v0.8.0
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
golang.org/x/tools v0.1.12 golang.org/x/tools v0.6.0
gonum.org/v1/gonum v0.11.0 gonum.org/v1/gonum v0.11.0
google.golang.org/grpc v1.48.0 google.golang.org/grpc v1.48.0
google.golang.org/protobuf v1.28.0 google.golang.org/protobuf v1.28.0
@ -90,7 +91,7 @@ require github.com/gammazero/deque v0.2.1 // indirect
require ( require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3 // indirect
github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
@ -136,9 +137,9 @@ require (
go.opentelemetry.io/otel/trace v1.2.0 go.opentelemetry.io/otel/trace v1.2.0
go.opentelemetry.io/proto/otlp v0.10.0 // indirect go.opentelemetry.io/proto/otlp v0.10.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.0.0-20220728030405-41545e8bf201 // indirect golang.org/x/net v0.8.0 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/term v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b // indirect google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect

44
go.sum
View file

@ -33,8 +33,9 @@ github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d h1:RO27lg
github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d/go.mod h1:romz7UPgSYhfJkKOalzEEyV6sWtt/eAEm0nX2aOrod0= github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d/go.mod h1:romz7UPgSYhfJkKOalzEEyV6sWtt/eAEm0nX2aOrod0=
github.com/JekaMas/workerpool v1.1.5 h1:xmrx2Zyft95CEGiEqzDxiawptCIRZQ0zZDhTGDFOCaw= github.com/JekaMas/workerpool v1.1.5 h1:xmrx2Zyft95CEGiEqzDxiawptCIRZQ0zZDhTGDFOCaw=
github.com/JekaMas/workerpool v1.1.5/go.mod h1:IoDWPpwMcA27qbuugZKeBslDrgX09lVmksuh9sjzbhc= github.com/JekaMas/workerpool v1.1.5/go.mod h1:IoDWPpwMcA27qbuugZKeBslDrgX09lVmksuh9sjzbhc=
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
@ -84,8 +85,8 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/btcsuite/btcd/btcec/v2 v2.1.2 h1:YoYoC9J0jwfukodSBMzZYUVQ8PTiYg4BnOWiJVzTmLs= github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE=
github.com/btcsuite/btcd/btcec/v2 v2.1.2/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0 h1:MSskdM4/xJYcFzy0altH/C/xHopifpWzHUi1JeVI34Q= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0 h1:MSskdM4/xJYcFzy0altH/C/xHopifpWzHUi1JeVI34Q=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ=
@ -136,8 +137,8 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.6.1 h1:4xYASHy5cScPkLD7PO0uTmnVc860m9NarPN1X8zeMe8=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.6.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 h1:iZOop7pqsg+56twTopWgwCGxdB5SI2yDO8Ti7eTRliQ= github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 h1:iZOop7pqsg+56twTopWgwCGxdB5SI2yDO8Ti7eTRliQ=
github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
@ -275,6 +276,8 @@ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuW
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl/v2 v2.10.1 h1:h4Xx4fsrRE26ohAk/1iGF/JBqRQbyUqu5Lvj60U54ys= github.com/hashicorp/hcl/v2 v2.10.1 h1:h4Xx4fsrRE26ohAk/1iGF/JBqRQbyUqu5Lvj60U54ys=
github.com/hashicorp/hcl/v2 v2.10.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/hcl/v2 v2.10.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw=
github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
@ -535,8 +538,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -569,8 +572,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -600,8 +603,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220728030405-41545e8bf201 h1:bvOltf3SADAfG05iRml8lAB3qjoEX5RCyN4K6G5v3N0= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.0.0-20220728030405-41545e8bf201/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -615,8 +618,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -658,12 +661,12 @@ golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -671,8 +674,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -709,8 +713,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -540,7 +540,7 @@ func DefaultConfig() *Config {
VHost: []string{"localhost"}, VHost: []string{"localhost"},
}, },
HttpTimeout: &HttpTimeouts{ HttpTimeout: &HttpTimeouts{
ReadTimeout: 30 * time.Second, ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second, IdleTimeout: 120 * time.Second,
}, },

View file

@ -61,6 +61,28 @@ func CPUProfile(ctx context.Context, sec int) ([]byte, map[string]string, error)
}, nil }, nil
} }
// CPUProfile generates a CPU Profile for a given duration
func CPUProfileWithChannel(done chan bool) ([]byte, map[string]string, error) {
var buf bytes.Buffer
if err := pprof.StartCPUProfile(&buf); err != nil {
return nil, nil, err
}
select {
case <-done:
case <-time.After(30 * time.Second):
}
pprof.StopCPUProfile()
return buf.Bytes(),
map[string]string{
"X-Content-Type-Options": "nosniff",
"Content-Type": "application/octet-stream",
"Content-Disposition": `attachment; filename="profile"`,
}, nil
}
// Trace runs a trace profile for a given duration // Trace runs a trace profile for a given duration
func Trace(ctx context.Context, sec int) ([]byte, map[string]string, error) { func Trace(ctx context.Context, sec int) ([]byte, map[string]string, error) {
if sec <= 0 { if sec <= 0 {

View file

@ -21,6 +21,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"runtime"
"strings" "strings"
"time" "time"
@ -1457,7 +1458,15 @@ func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, conf
func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *params.ChainConfig, db ethdb.Database) *RPCTransaction { func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *params.ChainConfig, db ethdb.Database) *RPCTransaction {
txs := b.Transactions() txs := b.Transactions()
borReceipt := rawdb.ReadBorReceipt(db, b.Hash(), b.NumberU64(), config) if index >= uint64(len(txs)+1) {
return nil
}
var borReceipt *types.Receipt
// Read bor receipts if a state-sync transaction is requested
if index == uint64(len(txs)) {
borReceipt = rawdb.ReadBorReceipt(db, b.Hash(), b.NumberU64(), config)
if borReceipt != nil { if borReceipt != nil {
if borReceipt.TxHash != (common.Hash{}) { if borReceipt.TxHash != (common.Hash{}) {
borTx, _, _, _ := rawdb.ReadBorTransactionWithBlockHash(db, borReceipt.TxHash, b.Hash()) borTx, _, _, _ := rawdb.ReadBorTransactionWithBlockHash(db, borReceipt.TxHash, b.Hash())
@ -1466,7 +1475,9 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *param
} }
} }
} }
}
// If the index is still out of the range after checking bor state sync transaction, it means that the transaction index is invalid
if index >= uint64(len(txs)) { if index >= uint64(len(txs)) {
return nil return nil
} }
@ -1474,7 +1485,7 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *param
rpcTx := newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee(), config) rpcTx := newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee(), config)
// If the transaction is a bor transaction, we need to set the hash to the derived bor tx hash. BorTx is always the last index. // If the transaction is a bor transaction, we need to set the hash to the derived bor tx hash. BorTx is always the last index.
if borReceipt != nil && int(index) == len(txs)-1 { if borReceipt != nil && index == uint64(len(txs)-1) {
rpcTx.Hash = borReceipt.TxHash rpcTx.Hash = borReceipt.TxHash
} }
@ -2222,6 +2233,21 @@ func (api *PrivateDebugAPI) PurgeCheckpointWhitelist() {
api.b.PurgeCheckpointWhitelist() api.b.PurgeCheckpointWhitelist()
} }
// GetTraceStack returns the current trace stack
func (api *PrivateDebugAPI) GetTraceStack() string {
buf := make([]byte, 1024)
for {
n := runtime.Stack(buf, true)
if n < len(buf) {
return string(buf)
}
buf = make([]byte, 2*len(buf))
}
}
// PublicNetAPI offers network related RPC methods // PublicNetAPI offers network related RPC methods
type PublicNetAPI struct { type PublicNetAPI struct {
net *p2p.Server net *p2p.Server

View file

@ -148,3 +148,35 @@ func (l *logger) flush() {
} }
l.h.buf = nil l.h.buf = nil
} }
func (l *logger) OnTrace(fn func(l log.Logging)) {
if l.GetHandler().Level() >= log.LvlTrace {
fn(l.Trace)
}
}
func (l *logger) OnDebug(fn func(l log.Logging)) {
if l.GetHandler().Level() >= log.LvlDebug {
fn(l.Debug)
}
}
func (l *logger) OnInfo(fn func(l log.Logging)) {
if l.GetHandler().Level() >= log.LvlInfo {
fn(l.Info)
}
}
func (l *logger) OnWarn(fn func(l log.Logging)) {
if l.GetHandler().Level() >= log.LvlWarn {
fn(l.Warn)
}
}
func (l *logger) OnError(fn func(l log.Logging)) {
if l.GetHandler().Level() >= log.LvlError {
fn(l.Error)
}
}
func (l *logger) OnCrit(fn func(l log.Logging)) {
if l.GetHandler().Level() >= log.LvlCrit {
fn(l.Crit)
}
}

View file

@ -512,6 +512,11 @@ web3._extend({
call: 'debug_purgeCheckpointWhitelist', call: 'debug_purgeCheckpointWhitelist',
params: 0, params: 0,
}), }),
new web3._extend.Method({
name: 'getTraceStack',
call: 'debug_getTraceStack',
params: 0,
}),
], ],
properties: [] properties: []
}); });

View file

@ -617,7 +617,7 @@ func testTransactionStatus(t *testing.T, protocol int) {
sendRequest(rawPeer.app, GetTxStatusMsg, reqID, []common.Hash{tx.Hash()}) sendRequest(rawPeer.app, GetTxStatusMsg, reqID, []common.Hash{tx.Hash()})
} }
if err := expectResponse(rawPeer.app, TxStatusMsg, reqID, testBufLimit, []light.TxStatus{expStatus}); err != nil { if err := expectResponse(rawPeer.app, TxStatusMsg, reqID, testBufLimit, []light.TxStatus{expStatus}); err != nil {
t.Errorf("transaction status mismatch") t.Error("transaction status mismatch", err)
} }
} }
signer := types.HomesteadSigner{} signer := types.HomesteadSigner{}

View file

@ -507,25 +507,39 @@ func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) {
if err := msg.Decode(&r); err != nil { if err := msg.Decode(&r); err != nil {
return nil, 0, 0, err return nil, 0, 0, err
} }
amount := uint64(len(r.Txs)) amount := uint64(len(r.Txs))
return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply { return func(backend serverBackend, p *clientPeer, waitOrStop func() bool) *reply {
stats := make([]light.TxStatus, len(r.Txs)) stats := make([]light.TxStatus, len(r.Txs))
var (
err error
addFn func(transaction *types.Transaction) error
)
for i, tx := range r.Txs { for i, tx := range r.Txs {
if i != 0 && !waitOrStop() { if i != 0 && !waitOrStop() {
return nil return nil
} }
hash := tx.Hash() hash := tx.Hash()
stats[i] = txStatus(backend, hash) stats[i] = txStatus(backend, hash)
if stats[i].Status == core.TxStatusUnknown { if stats[i].Status == core.TxStatusUnknown {
addFn := backend.TxPool().AddRemotes addFn = backend.TxPool().AddRemote
// Add txs synchronously for testing purpose // Add txs synchronously for testing purpose
if backend.AddTxsSync() { if backend.AddTxsSync() {
addFn = backend.TxPool().AddRemotesSync addFn = backend.TxPool().AddRemoteSync
} }
if errs := addFn([]*types.Transaction{tx}); errs[0] != nil {
stats[i].Error = errs[0].Error() if err = addFn(tx); err != nil {
stats[i].Error = err.Error()
continue continue
} }
stats[i] = txStatus(backend, hash) stats[i] = txStatus(backend, hash)
} }
} }

View file

@ -106,6 +106,8 @@ type RecordKeyNames struct {
Ctx string Ctx string
} }
type Logging func(msg string, ctx ...interface{})
// A Logger writes key/value pairs to a Handler // A Logger writes key/value pairs to a Handler
type Logger interface { type Logger interface {
// New returns a new Logger that has this logger's context plus the given context // New returns a new Logger that has this logger's context plus the given context
@ -124,6 +126,13 @@ type Logger interface {
Warn(msg string, ctx ...interface{}) Warn(msg string, ctx ...interface{})
Error(msg string, ctx ...interface{}) Error(msg string, ctx ...interface{})
Crit(msg string, ctx ...interface{}) Crit(msg string, ctx ...interface{})
OnTrace(func(l Logging))
OnDebug(func(l Logging))
OnInfo(func(l Logging))
OnWarn(func(l Logging))
OnError(func(l Logging))
OnCrit(func(l Logging))
} }
type logger struct { type logger struct {
@ -198,6 +207,38 @@ func (l *logger) SetHandler(h Handler) {
l.h.Swap(h) l.h.Swap(h)
} }
func (l *logger) OnTrace(fn func(l Logging)) {
if l.GetHandler().Level() >= LvlTrace {
fn(l.Trace)
}
}
func (l *logger) OnDebug(fn func(l Logging)) {
if l.GetHandler().Level() >= LvlDebug {
fn(l.Debug)
}
}
func (l *logger) OnInfo(fn func(l Logging)) {
if l.GetHandler().Level() >= LvlInfo {
fn(l.Info)
}
}
func (l *logger) OnWarn(fn func(l Logging)) {
if l.GetHandler().Level() >= LvlWarn {
fn(l.Warn)
}
}
func (l *logger) OnError(fn func(l Logging)) {
if l.GetHandler().Level() >= LvlError {
fn(l.Error)
}
}
func (l *logger) OnCrit(fn func(l Logging)) {
if l.GetHandler().Level() >= LvlCrit {
fn(l.Crit)
}
}
func normalize(ctx []interface{}) []interface{} { func normalize(ctx []interface{}) []interface{} {
// if the caller passed a Ctx object, then expand it // if the caller passed a Ctx object, then expand it
if len(ctx) == 1 { if len(ctx) == 1 {

View file

@ -60,6 +60,38 @@ func Crit(msg string, ctx ...interface{}) {
os.Exit(1) os.Exit(1)
} }
func OnTrace(fn func(l Logging)) {
if root.GetHandler().Level() >= LvlTrace {
fn(root.Trace)
}
}
func OnDebug(fn func(l Logging)) {
if root.GetHandler().Level() >= LvlDebug {
fn(root.Debug)
}
}
func OnInfo(fn func(l Logging)) {
if root.GetHandler().Level() >= LvlInfo {
fn(root.Info)
}
}
func OnWarn(fn func(l Logging)) {
if root.GetHandler().Level() >= LvlWarn {
fn(root.Warn)
}
}
func OnError(fn func(l Logging)) {
if root.GetHandler().Level() >= LvlError {
fn(root.Error)
}
}
func OnCrit(fn func(l Logging)) {
if root.GetHandler().Level() >= LvlCrit {
fn(root.Crit)
}
}
// Output is a convenient alias for write, allowing for the modification of // Output is a convenient alias for write, allowing for the modification of
// the calldepth (number of stack frames to skip). // the calldepth (number of stack frames to skip).
// calldepth influences the reported line number of the log message. // calldepth influences the reported line number of the log message.

View file

@ -47,7 +47,7 @@ func NewBorDefaultMiner(t *testing.T) *DefaultBorMiner {
ethAPI.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() ethAPI.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
spanner := bor.NewMockSpanner(ctrl) spanner := bor.NewMockSpanner(ctrl)
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{ spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{
{ {
ID: 0, ID: 0,
Address: common.Address{0x1}, Address: common.Address{0x1},

View file

@ -17,10 +17,15 @@
package miner package miner
import ( import (
"bytes"
"context" "context"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"os"
"runtime"
"runtime/pprof"
ptrace "runtime/trace"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -31,14 +36,17 @@ import (
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/common/tracing" "github.com/ethereum/go-ethereum/common/tracing"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -83,6 +91,12 @@ const (
staleThreshold = 7 staleThreshold = 7
) )
// metrics gauge to track total and empty blocks sealed by a miner
var (
sealedBlocksCounter = metrics.NewRegisteredCounter("worker/sealedBlocks", nil)
sealedEmptyBlocksCounter = metrics.NewRegisteredCounter("worker/sealedEmptyBlocks", nil)
)
// environment is the worker's current environment and holds all // environment is the worker's current environment and holds all
// information of the sealing block generation. // information of the sealing block generation.
type environment struct { type environment struct {
@ -257,6 +271,8 @@ type worker struct {
skipSealHook func(*task) bool // Method to decide whether skipping the sealing. skipSealHook func(*task) bool // Method to decide whether skipping the sealing.
fullTaskHook func() // Method to call before pushing the full sealing task. fullTaskHook func() // Method to call before pushing the full sealing task.
resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval.
profileCount *int32 // Global count for profiling
} }
//nolint:staticcheck //nolint:staticcheck
@ -285,6 +301,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
resubmitIntervalCh: make(chan time.Duration), resubmitIntervalCh: make(chan time.Duration),
resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
} }
worker.profileCount = new(int32)
// Subscribe NewTxsEvent for tx pool // Subscribe NewTxsEvent for tx pool
worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh) worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)
// Subscribe events for blockchain // Subscribe events for blockchain
@ -560,9 +577,11 @@ func (w *worker) mainLoop(ctx context.Context) {
for { for {
select { select {
case req := <-w.newWorkCh: case req := <-w.newWorkCh:
//nolint:contextcheck
w.commitWork(req.ctx, req.interrupt, req.noempty, req.timestamp) w.commitWork(req.ctx, req.interrupt, req.noempty, req.timestamp)
case req := <-w.getWorkCh: case req := <-w.getWorkCh:
//nolint:contextcheck
block, err := w.generateWork(req.ctx, req.params) block, err := w.generateWork(req.ctx, req.params)
if err != nil { if err != nil {
req.err = err req.err = err
@ -622,13 +641,17 @@ func (w *worker) mainLoop(ctx context.Context) {
if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas { if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
continue continue
} }
txs := make(map[common.Address]types.Transactions) txs := make(map[common.Address]types.Transactions)
for _, tx := range ev.Txs { for _, tx := range ev.Txs {
acc, _ := types.Sender(w.current.signer, tx) acc, _ := types.Sender(w.current.signer, tx)
txs[acc] = append(txs[acc], tx) txs[acc] = append(txs[acc], tx)
} }
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, w.current.header.BaseFee)
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
tcount := w.current.tcount tcount := w.current.tcount
w.commitTransactions(w.current, txset, nil) w.commitTransactions(w.current, txset, nil)
// Only update the snapshot if any new transactions were added // Only update the snapshot if any new transactions were added
@ -758,7 +781,7 @@ func (w *worker) resultLoop() {
err error err error
) )
tracing.Exec(task.ctx, "resultLoop", func(ctx context.Context, span trace.Span) { tracing.Exec(task.ctx, "", "resultLoop", func(ctx context.Context, span trace.Span) {
for i, taskReceipt := range task.receipts { for i, taskReceipt := range task.receipts {
receipt := new(types.Receipt) receipt := new(types.Receipt)
receipts[i] = receipt receipts[i] = receipt
@ -782,8 +805,8 @@ func (w *worker) resultLoop() {
} }
// Commit block and state to database. // Commit block and state to database.
tracing.ElapsedTime(ctx, span, "WriteBlockAndSetHead time taken", func(_ context.Context, _ trace.Span) { tracing.Exec(ctx, "", "resultLoop.WriteBlockAndSetHead", func(ctx context.Context, span trace.Span) {
_, err = w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true) _, err = w.chain.WriteBlockAndSetHead(ctx, block, receipts, logs, task.state, true)
}) })
tracing.SetAttributes( tracing.SetAttributes(
@ -808,6 +831,12 @@ func (w *worker) resultLoop() {
// Broadcast the block and announce chain insertion event // Broadcast the block and announce chain insertion event
w.mux.Post(core.NewMinedBlockEvent{Block: block}) w.mux.Post(core.NewMinedBlockEvent{Block: block})
sealedBlocksCounter.Inc(1)
if block.Transactions().Len() == 0 {
sealedEmptyBlocksCounter.Inc(1)
}
// Insert the block into the set of pending ones to resultLoop for confirmations // Insert the block into the set of pending ones to resultLoop for confirmations
w.unconfirmed.Insert(block.NumberU64(), block.Hash()) w.unconfirmed.Insert(block.NumberU64(), block.Hash())
case <-w.exitCh: case <-w.exitCh:
@ -918,6 +947,22 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
} }
var coalescedLogs []*types.Log var coalescedLogs []*types.Log
initialGasLimit := env.gasPool.Gas()
initialTxs := txs.GetTxs()
var breakCause string
defer func() {
log.OnDebug(func(lg log.Logging) {
lg("commitTransactions-stats",
"initialTxsCount", initialTxs,
"initialGasLimit", initialGasLimit,
"resultTxsCount", txs.GetTxs(),
"resultGapPool", env.gasPool.Gas(),
"exitCause", breakCause)
})
}()
for { for {
// In the following three cases, we will interrupt the execution of the transaction. // In the following three cases, we will interrupt the execution of the transaction.
// (1) new head block event arrival, the interrupt signal is 1 // (1) new head block event arrival, the interrupt signal is 1
@ -965,7 +1010,14 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
// Start executing the transaction // Start executing the transaction
env.state.Prepare(tx.Hash(), env.tcount) env.state.Prepare(tx.Hash(), env.tcount)
var start time.Time
log.OnDebug(func(log.Logging) {
start = time.Now()
})
logs, err := w.commitTransaction(env, tx) logs, err := w.commitTransaction(env, tx)
switch { switch {
case errors.Is(err, core.ErrGasLimitReached): case errors.Is(err, core.ErrGasLimitReached):
// Pop the current out-of-gas transaction without shifting in the next from the account // Pop the current out-of-gas transaction without shifting in the next from the account
@ -988,6 +1040,10 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
env.tcount++ env.tcount++
txs.Shift() txs.Shift()
log.OnDebug(func(lg log.Logging) {
lg("Committed new tx", "tx hash", tx.Hash(), "from", from, "to", tx.To(), "nonce", tx.Nonce(), "gas", tx.Gas(), "gasPrice", tx.GasPrice(), "value", tx.Value(), "time spent", time.Since(start))
})
case errors.Is(err, core.ErrTxTypeNotSupported): case errors.Is(err, core.ErrTxTypeNotSupported):
// Pop the unsupported transaction without shifting in the next from the account // Pop the unsupported transaction without shifting in the next from the account
log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
@ -1077,7 +1133,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
} }
// Set baseFee and GasLimit if we are on an EIP-1559 chain // Set baseFee and GasLimit if we are on an EIP-1559 chain
if w.chainConfig.IsLondon(header.Number) { if w.chainConfig.IsLondon(header.Number) {
header.BaseFee = misc.CalcBaseFee(w.chainConfig, parent.Header()) header.BaseFee = misc.CalcBaseFeeUint(w.chainConfig, parent.Header()).ToBig()
if !w.chainConfig.IsLondon(parent.Number()) { if !w.chainConfig.IsLondon(parent.Number()) {
parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier
header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil) header.GasLimit = core.CalcGasLimit(parentGasLimit, w.config.GasCeil)
@ -1085,7 +1141,12 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
} }
// Run the consensus preparation with the default or customized consensus engine. // Run the consensus preparation with the default or customized consensus engine.
if err := w.engine.Prepare(w.chain, header); err != nil { if err := w.engine.Prepare(w.chain, header); err != nil {
switch err.(type) {
case *bor.UnauthorizedSignerError:
log.Debug("Failed to prepare header for sealing", "err", err)
default:
log.Error("Failed to prepare header for sealing", "err", err) log.Error("Failed to prepare header for sealing", "err", err)
}
return nil, err return nil, err
} }
// Could potentially happen if starting to mine in an odd state. // Could potentially happen if starting to mine in an odd state.
@ -1117,9 +1178,75 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
return env, nil return env, nil
} }
func startProfiler(profile string, filepath string, number uint64) (func() error, error) {
var (
buf bytes.Buffer
err error
)
closeFn := func() {}
switch profile {
case "cpu":
err = pprof.StartCPUProfile(&buf)
if err == nil {
closeFn = func() {
pprof.StopCPUProfile()
}
}
case "trace":
err = ptrace.Start(&buf)
if err == nil {
closeFn = func() {
ptrace.Stop()
}
}
case "heap":
runtime.GC()
err = pprof.WriteHeapProfile(&buf)
default:
log.Info("Incorrect profile name")
}
if err != nil {
return func() error {
closeFn()
return nil
}, err
}
closeFnNew := func() error {
var err error
closeFn()
if buf.Len() == 0 {
return nil
}
f, err := os.Create(filepath + "/" + profile + "-" + fmt.Sprint(number) + ".prof")
if err != nil {
return err
}
defer f.Close()
_, err = f.Write(buf.Bytes())
return err
}
return closeFnNew, nil
}
// fillTransactions retrieves the pending transactions from the txpool and fills them // fillTransactions retrieves the pending transactions from the txpool and fills them
// into the given sealing block. The transaction selection and ordering strategy can // into the given sealing block. The transaction selection and ordering strategy can
// be customized with the plugin in the future. // be customized with the plugin in the future.
//
//nolint:gocognit
func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *environment) { func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *environment) {
ctx, span := tracing.StartSpan(ctx, "fillTransactions") ctx, span := tracing.StartSpan(ctx, "fillTransactions")
defer tracing.EndSpan(span) defer tracing.EndSpan(span)
@ -1134,10 +1261,76 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
remoteTxs map[common.Address]types.Transactions remoteTxs map[common.Address]types.Transactions
) )
tracing.Exec(ctx, "worker.SplittingTransactions", func(ctx context.Context, span trace.Span) { // TODO: move to config or RPC
pending := w.eth.TxPool().Pending(true) const profiling = false
if profiling {
doneCh := make(chan struct{})
defer func() {
close(doneCh)
}()
go func(number uint64) {
closeFn := func() error {
return nil
}
for {
select {
case <-time.After(150 * time.Millisecond):
// Check if we've not crossed limit
if attempt := atomic.AddInt32(w.profileCount, 1); attempt >= 10 {
log.Info("Completed profiling", "attempt", attempt)
return
}
log.Info("Starting profiling in fill transactions", "number", number)
dir, err := os.MkdirTemp("", fmt.Sprintf("bor-traces-%s-", time.Now().UTC().Format("2006-01-02-150405Z")))
if err != nil {
log.Error("Error in profiling", "path", dir, "number", number, "err", err)
return
}
// grab the cpu profile
closeFnInternal, err := startProfiler("cpu", dir, number)
if err != nil {
log.Error("Error in profiling", "path", dir, "number", number, "err", err)
return
}
closeFn = func() error {
err := closeFnInternal()
log.Info("Completed profiling", "path", dir, "number", number, "error", err)
return nil
}
case <-doneCh:
err := closeFn()
if err != nil {
log.Info("closing fillTransactions", "number", number, "error", err)
}
return
}
}
}(env.header.Number.Uint64())
}
tracing.Exec(ctx, "", "worker.SplittingTransactions", func(ctx context.Context, span trace.Span) {
prePendingTime := time.Now()
pending := w.eth.TxPool().Pending(ctx, true)
remoteTxs = pending remoteTxs = pending
postPendingTime := time.Now()
for _, account := range w.eth.TxPool().Locals() { for _, account := range w.eth.TxPool().Locals() {
if txs := remoteTxs[account]; len(txs) > 0 { if txs := remoteTxs[account]; len(txs) > 0 {
delete(remoteTxs, account) delete(remoteTxs, account)
@ -1145,6 +1338,8 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
} }
} }
postLocalsTime := time.Now()
localTxsCount = len(localTxs) localTxsCount = len(localTxs)
remoteTxsCount = len(remoteTxs) remoteTxsCount = len(remoteTxs)
@ -1152,6 +1347,8 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
span, span,
attribute.Int("len of local txs", localTxsCount), attribute.Int("len of local txs", localTxsCount),
attribute.Int("len of remote txs", remoteTxsCount), attribute.Int("len of remote txs", remoteTxsCount),
attribute.String("time taken by Pending()", fmt.Sprintf("%v", postPendingTime.Sub(prePendingTime))),
attribute.String("time taken by Locals()", fmt.Sprintf("%v", postLocalsTime.Sub(postPendingTime))),
) )
}) })
@ -1164,8 +1361,8 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
if localTxsCount > 0 { if localTxsCount > 0 {
var txs *types.TransactionsByPriceAndNonce var txs *types.TransactionsByPriceAndNonce
tracing.Exec(ctx, "worker.LocalTransactionsByPriceAndNonce", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.LocalTransactionsByPriceAndNonce", func(ctx context.Context, span trace.Span) {
txs = types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee) txs = types.NewTransactionsByPriceAndNonce(env.signer, localTxs, cmath.FromBig(env.header.BaseFee))
tracing.SetAttributes( tracing.SetAttributes(
span, span,
@ -1173,7 +1370,7 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
) )
}) })
tracing.Exec(ctx, "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
committed = w.commitTransactions(env, txs, interrupt) committed = w.commitTransactions(env, txs, interrupt)
}) })
@ -1187,8 +1384,8 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
if remoteTxsCount > 0 { if remoteTxsCount > 0 {
var txs *types.TransactionsByPriceAndNonce var txs *types.TransactionsByPriceAndNonce
tracing.Exec(ctx, "worker.RemoteTransactionsByPriceAndNonce", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.RemoteTransactionsByPriceAndNonce", func(ctx context.Context, span trace.Span) {
txs = types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee) txs = types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, cmath.FromBig(env.header.BaseFee))
tracing.SetAttributes( tracing.SetAttributes(
span, span,
@ -1196,7 +1393,7 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
) )
}) })
tracing.Exec(ctx, "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
committed = w.commitTransactions(env, txs, interrupt) committed = w.commitTransactions(env, txs, interrupt)
}) })
@ -1237,7 +1434,7 @@ func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool,
err error err error
) )
tracing.Exec(ctx, "worker.prepareWork", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) {
// Set the coinbase if the worker is running or it's required // Set the coinbase if the worker is running or it's required
var coinbase common.Address var coinbase common.Address
if w.isRunning() { if w.isRunning() {

View file

@ -75,7 +75,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool, isBor bool) {
ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
spanner := bor.NewMockSpanner(ctrl) spanner := bor.NewMockSpanner(ctrl)
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{ spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{
{ {
ID: 0, ID: 0,
Address: TestBankAddress, Address: TestBankAddress,
@ -622,7 +622,7 @@ func BenchmarkBorMining(b *testing.B) {
ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() ethAPIMock.EXPECT().Call(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
spanner := bor.NewMockSpanner(ctrl) spanner := bor.NewMockSpanner(ctrl)
spanner.EXPECT().GetCurrentValidators(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{ spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*valset.Validator{
{ {
ID: 0, ID: 0,
Address: TestBankAddress, Address: TestBankAddress,

View file

@ -57,15 +57,19 @@ func TestClientSyncTree(t *testing.T) {
c := NewClient(Config{Resolver: r, Logger: testlog.Logger(t, log.LvlTrace)}) c := NewClient(Config{Resolver: r, Logger: testlog.Logger(t, log.LvlTrace)})
stree, err := c.SyncTree("enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n") stree, err := c.SyncTree("enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n")
if err != nil { if err != nil {
t.Fatal("sync error:", err) t.Fatal("sync error:", err)
} }
if !reflect.DeepEqual(sortByID(stree.Nodes()), sortByID(wantNodes)) { if !reflect.DeepEqual(sortByID(stree.Nodes()), sortByID(wantNodes)) {
t.Errorf("wrong nodes in synced tree:\nhave %v\nwant %v", spew.Sdump(stree.Nodes()), spew.Sdump(wantNodes)) t.Errorf("wrong nodes in synced tree:\nhave %v\nwant %v", spew.Sdump(stree.Nodes()), spew.Sdump(wantNodes))
} }
if !reflect.DeepEqual(stree.Links(), wantLinks) { if !reflect.DeepEqual(stree.Links(), wantLinks) {
t.Errorf("wrong links in synced tree: %v", stree.Links()) t.Errorf("wrong links in synced tree: %v", stree.Links())
} }
if stree.Seq() != wantSeq { if stree.Seq() != wantSeq {
t.Errorf("synced tree has wrong seq: %d", stree.Seq()) t.Errorf("synced tree has wrong seq: %d", stree.Seq())
} }
@ -295,7 +299,7 @@ func TestIteratorEmptyTree(t *testing.T) {
// updateSomeNodes applies ENR updates to some of the given nodes. // updateSomeNodes applies ENR updates to some of the given nodes.
func updateSomeNodes(keySeed int64, nodes []*enode.Node) { func updateSomeNodes(keySeed int64, nodes []*enode.Node) {
keys := testKeys(nodesSeed1, len(nodes)) keys := testKeys(keySeed, len(nodes))
for i, n := range nodes[:len(nodes)/2] { for i, n := range nodes[:len(nodes)/2] {
r := n.Record() r := n.Record()
r.Set(enr.IP{127, 0, 0, 1}) r.Set(enr.IP{127, 0, 0, 1})
@ -384,10 +388,12 @@ func makeTestTree(domain string, nodes []*enode.Node, links []string) (*Tree, st
if err != nil { if err != nil {
panic(err) panic(err)
} }
url, err := tree.Sign(testKey(signingKeySeed), domain) url, err := tree.Sign(testKey(signingKeySeed), domain)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return tree, url return tree, url
} }

View file

@ -86,7 +86,7 @@ gcmode = "archive"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -86,7 +86,7 @@ syncmode = "full"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -88,7 +88,7 @@ syncmode = "full"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -88,7 +88,7 @@ syncmode = "full"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -1,5 +1,5 @@
Source: bor Source: bor
Version: 0.3.4-beta3 Version: 0.3.8-beta
Section: develop Section: develop
Priority: standard Priority: standard
Maintainer: Polygon <release-team@polygon.technology> Maintainer: Polygon <release-team@polygon.technology>

View file

@ -1,5 +1,5 @@
Source: bor Source: bor
Version: 0.3.4-beta3 Version: 0.3.8-beta
Section: develop Section: develop
Priority: standard Priority: standard
Maintainer: Polygon <release-team@polygon.technology> Maintainer: Polygon <release-team@polygon.technology>

View file

@ -1,5 +1,5 @@
Source: bor-profile Source: bor-profile
Version: 0.3.4-beta3 Version: 0.3.8-beta
Section: develop Section: develop
Priority: standard Priority: standard
Maintainer: Polygon <release-team@polygon.technology> Maintainer: Polygon <release-team@polygon.technology>

View file

@ -1,5 +1,5 @@
Source: bor-profile Source: bor-profile
Version: 0.3.4-beta3 Version: 0.3.8-beta
Section: develop Section: develop
Priority: standard Priority: standard
Maintainer: Polygon <release-team@polygon.technology> Maintainer: Polygon <release-team@polygon.technology>

View file

@ -1,5 +1,5 @@
Source: bor-profile Source: bor-profile
Version: 0.3.4-beta3 Version: 0.3.8-beta
Section: develop Section: develop
Priority: standard Priority: standard
Maintainer: Polygon <release-team@polygon.technology> Maintainer: Polygon <release-team@polygon.technology>

View file

@ -1,5 +1,5 @@
Source: bor-profile Source: bor-profile
Version: 0.3.4-beta3 Version: 0.3.8-beta
Section: develop Section: develop
Priority: standard Priority: standard
Maintainer: Polygon <release-team@polygon.technology> Maintainer: Polygon <release-team@polygon.technology>

View file

@ -86,7 +86,7 @@ gcmode = "archive"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -86,7 +86,7 @@ syncmode = "full"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -88,7 +88,7 @@ syncmode = "full"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -88,7 +88,7 @@ syncmode = "full"
# vhosts = ["*"] # vhosts = ["*"]
# corsdomain = ["*"] # corsdomain = ["*"]
# [jsonrpc.timeouts] # [jsonrpc.timeouts]
# read = "30s" # read = "10s"
# write = "30s" # write = "30s"
# idle = "2m0s" # idle = "2m0s"

View file

@ -23,8 +23,8 @@ import (
const ( const (
VersionMajor = 0 // Major version component of the current release VersionMajor = 0 // Major version component of the current release
VersionMinor = 3 // Minor version component of the current release VersionMinor = 3 // Minor version component of the current release
VersionPatch = 4 // Patch version component of the current release VersionPatch = 8 // Patch version component of the current release
VersionMeta = "beta3" // Version metadata to append to the version string VersionMeta = "beta" // Version metadata to append to the version string
) )
// Version holds the textual version string. // Version holds the textual version string.

View file

@ -104,7 +104,7 @@ type HTTPTimeouts struct {
// DefaultHTTPTimeouts represents the default timeout values used if further // DefaultHTTPTimeouts represents the default timeout values used if further
// configuration is not provided. // configuration is not provided.
var DefaultHTTPTimeouts = HTTPTimeouts{ var DefaultHTTPTimeouts = HTTPTimeouts{
ReadTimeout: 30 * time.Second, ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second, IdleTimeout: 120 * time.Second,
} }

View file

@ -392,12 +392,18 @@ func TestInsertingSpanSizeBlocks(t *testing.T) {
currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)} currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)}
spanner := getMockedSpanner(t, currentValidators)
_bor.SetSpanner(spanner)
// Insert sprintSize # of blocks so that span is fetched at the start of a new sprint // Insert sprintSize # of blocks so that span is fetched at the start of a new sprint
for i := uint64(1); i <= spanSize; i++ { for i := uint64(1); i <= spanSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
spanner = getMockedSpanner(t, currentSpan.ValidatorSet.Validators)
_bor.SetSpanner(spanner)
validators, err := _bor.GetCurrentValidators(context.Background(), block.Hash(), spanSize) // check validator set at the first block of new span validators, err := _bor.GetCurrentValidators(context.Background(), block.Hash(), spanSize) // check validator set at the first block of new span
if err != nil { if err != nil {
t.Fatalf("%s", err) t.Fatalf("%s", err)
@ -427,6 +433,9 @@ func TestFetchStateSyncEvents(t *testing.T) {
currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)} currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)}
spanner := getMockedSpanner(t, currentValidators)
_bor.SetSpanner(spanner)
// Insert sprintSize # of blocks so that span is fetched at the start of a new sprint // Insert sprintSize # of blocks so that span is fetched at the start of a new sprint
for i := uint64(1); i < sprintSize; i++ { for i := uint64(1); i < sprintSize; i++ {
if IsSpanEnd(i) { if IsSpanEnd(i) {
@ -528,6 +537,9 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
currentValidators = []*valset.Validator{valset.NewValidator(addr, 10)} currentValidators = []*valset.Validator{valset.NewValidator(addr, 10)}
} }
spanner := getMockedSpanner(t, currentValidators)
_bor.SetSpanner(spanner)
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
@ -554,6 +566,9 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
currentValidators = []*valset.Validator{valset.NewValidator(addr, 10)} currentValidators = []*valset.Validator{valset.NewValidator(addr, 10)}
} }
spanner := getMockedSpanner(t, currentValidators)
_bor.SetSpanner(spanner)
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
} }
@ -580,6 +595,8 @@ func TestOutOfTurnSigning(t *testing.T) {
h.EXPECT().Close().AnyTimes() h.EXPECT().Close().AnyTimes()
spanner := getMockedSpanner(t, heimdallSpan.ValidatorSet.Validators)
_bor.SetSpanner(spanner)
_bor.SetHeimdallClient(h) _bor.SetHeimdallClient(h)
db := init.ethereum.ChainDb() db := init.ethereum.ChainDb()
@ -1082,6 +1099,9 @@ func TestJaipurFork(t *testing.T) {
res, _ := loadSpanFromFile(t) res, _ := loadSpanFromFile(t)
spanner := getMockedSpanner(t, res.Result.ValidatorSet.Validators)
_bor.SetSpanner(spanner)
for i := uint64(1); i < sprintSize; i++ { for i := uint64(1); i < sprintSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators) block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)

View file

@ -352,6 +352,17 @@ func getMockedHeimdallClient(t *testing.T, heimdallSpan *span.HeimdallSpan) (*mo
return h, ctrl return h, ctrl
} }
func getMockedSpanner(t *testing.T, validators []*valset.Validator) *bor.MockSpanner {
t.Helper()
spanner := bor.NewMockSpanner(gomock.NewController(t))
spanner.EXPECT().GetCurrentValidatorsByHash(gomock.Any(), gomock.Any(), gomock.Any()).Return(validators, nil).AnyTimes()
spanner.EXPECT().GetCurrentValidatorsByBlockNrOrHash(gomock.Any(), gomock.Any(), gomock.Any()).Return(validators, nil).AnyTimes()
spanner.EXPECT().GetCurrentSpan(gomock.Any(), gomock.Any()).Return(&span.Span{0, 0, 0}, nil).AnyTimes()
spanner.EXPECT().CommitSpan(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
return spanner
}
func generateFakeStateSyncEvents(sample *clerk.EventRecordWithTime, count int) []*clerk.EventRecordWithTime { func generateFakeStateSyncEvents(sample *clerk.EventRecordWithTime, count int) []*clerk.EventRecordWithTime {
events := make([]*clerk.EventRecordWithTime, count) events := make([]*clerk.EventRecordWithTime, count)
event := *sample event := *sample

View file

@ -141,9 +141,6 @@ func (tm *testMatcher) findSkip(name string) (reason string, skipload bool) {
isWin32 := runtime.GOARCH == "386" && runtime.GOOS == "windows" isWin32 := runtime.GOARCH == "386" && runtime.GOOS == "windows"
for _, re := range tm.slowpat { for _, re := range tm.slowpat {
if re.MatchString(name) { if re.MatchString(name) {
if testing.Short() {
return "skipped in -short mode", false
}
if isWin32 { if isWin32 {
return "skipped on 32bit windows", false return "skipped on 32bit windows", false
} }