Merge branch 'develop' of https://github.com/maticnetwork/bor into block-stm

This commit is contained in:
Pratik Patil 2022-11-21 10:49:18 +05:30
commit 96e66e5256
86 changed files with 3109 additions and 871 deletions

23
.github/CODEOWNERS vendored
View file

@ -1,23 +0,0 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners.
accounts/usbwallet @karalabe
accounts/scwallet @gballet
accounts/abi @gballet @MariusVanDerWijden
cmd/clef @holiman
cmd/puppeth @karalabe
consensus @karalabe
core/ @karalabe @holiman @rjl493456442
eth/ @karalabe @holiman @rjl493456442
eth/catalyst/ @gballet
graphql/ @gballet
les/ @zsfelfoldi @rjl493456442
light/ @zsfelfoldi @rjl493456442
mobile/ @karalabe @ligi
node/ @fjl @renaynay
p2p/ @fjl @zsfelfoldi
rpc/ @fjl @holiman
p2p/simulations @fjl
p2p/protocols @fjl
p2p/testing @fjl
signer/ @holiman

View file

@ -3,9 +3,13 @@ defaultFee: 2000
borChainId: "15001"
heimdallChainId: heimdall-15001
contractsBranch: jc/v0.3.1-backport
sprintSize: 64
blockNumber: '0'
blockTime: '2'
numOfValidators: 3
numOfNonValidators: 0
ethURL: http://ganache:9545
ethHostUser: ubuntu
devnetType: docker
borDockerBuildContext: "../../bor"
heimdallDockerBuildContext: "https://github.com/maticnetwork/heimdall.git#develop"
heimdallDockerBuildContext: "https://github.com/maticnetwork/heimdall.git#develop"

44
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,44 @@
# Description
Please provide a detailed description of what was done in this PR
# Changes
- [ ] Bugfix (non-breaking change that solves an issue)
- [ ] Hotfix (change that solves an urgent issue, and requires immediate attention)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (change that is not backwards-compatible and/or changes current functionality)
# Breaking changes
Please complete this section if any breaking changes have been made, otherwise delete it
# Checklist
- [ ] I have added at least 2 reviewer or the whole pos-v1 team
- [ ] I have added sufficient documentation in code
- [ ] I will be resolving comments - if any - by pushing each fix in a separate commit and linking the commit hash in the comment reply
# Cross repository changes
- [ ] This PR requires changes to heimdall
- In case link the PR here:
- [ ] This PR requires changes to matic-cli
- In case link the PR here:
## Testing
- [ ] I have added unit tests
- [ ] I have added tests to CI
- [ ] I have tested this code manually on local environment
- [ ] I have tested this code manually on remote devnet using express-cli
- [ ] I have tested this code manually on mumbai
- [ ] I have created new e2e tests into express-cli
### Manual tests
Please complete this section with the steps you performed if you ran manual tests for this functionality, otherwise delete it
# Additional comments
Please post additional comments in this section if you have them, otherwise delete it

View file

@ -104,7 +104,7 @@ jobs:
uses: actions/checkout@v3
with:
repository: maticnetwork/matic-cli
ref: v0.3.0-dev
ref: arpit/pos-655-2
path: matic-cli
- name: Install dependencies on Linux
@ -154,7 +154,7 @@ jobs:
cd matic-cli/devnet/code/contracts
npm run truffle exec scripts/deposit.js -- --network development $(jq -r .root.tokens.MaticToken contractAddresses.json) 100000000000000000000
cd -
bash bor/integration-tests/smoke_test.sh
timeout 20m bash bor/integration-tests/smoke_test.sh
- name: Upload logs
if: always()

2
.gitignore vendored
View file

@ -53,3 +53,5 @@ profile.cov
./bor-debug-*
dist
*.csv

View file

@ -65,7 +65,7 @@ test-race:
$(GOTEST) --timeout 15m -race -shuffle=on $(TESTALL)
test-integration:
$(GOTEST) --timeout 30m -tags integration $(TESTE2E)
$(GOTEST) --timeout 60m -tags integration $(TESTE2E)
escape:
cd $(path) && go test -gcflags "-m -m" -run none -bench=BenchmarkJumpdest* -benchmem -memprofile mem.out

View file

@ -10,6 +10,7 @@ datadir = "/var/lib/bor/data"
syncmode = "full"
# gcmode = "full"
# snapshot = true
# "bor.logs" = false
# ethstats = ""
# ["eth.requiredblocks"]
@ -77,8 +78,7 @@ syncmode = "full"
# prefix = ""
# host = "localhost"
# api = ["web3", "net"]
# vhosts = ["*"]
# corsdomain = ["*"]
# origins = ["*"]
# [jsonrpc.graphql]
# enabled = false
# port = 0
@ -121,6 +121,7 @@ syncmode = "full"
# noprefetch = false
# preimages = false
# txlookuplimit = 2350000
# triesinmemory = 128
[accounts]
# allow-insecure-unlock = true
@ -134,4 +135,4 @@ syncmode = "full"
# [developer]
# dev = false
# period = 0
# period = 0

View file

@ -18,8 +18,12 @@
"period": {
"0": 2
},
"producerDelay": 6,
"sprint": 64,
"producerDelay": {
"0": 6
},
"sprint": {
"0": 64
},
"backupMultiplier": {
"0": 2
},

View file

@ -19,8 +19,12 @@
"0": 2,
"25275000": 5
},
"producerDelay": 6,
"sprint": 64,
"producerDelay": {
"0": 6
},
"sprint": {
"0": 64
},
"backupMultiplier": {
"0": 2,
"25275000": 5

View file

@ -17,6 +17,7 @@
package t8ntool
import (
"context"
"crypto/ecdsa"
"encoding/json"
"errors"
@ -188,7 +189,7 @@ func (i *bbInput) sealEthash(block *types.Block) (*types.Block, error) {
// If the testmode is used, the sealer will return quickly, and complain
// "Sealing result is not read by miner" if it cannot write the result.
results := make(chan *types.Block, 1)
if err := engine.Seal(nil, block, results, nil); err != nil {
if err := engine.Seal(context.Background(), nil, block, results, nil); err != nil {
panic(fmt.Sprintf("failed to seal block: %v", err))
}
found := <-results

View file

@ -223,6 +223,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
txIndex++
}
statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
// Add mining reward?
if miningReward > 0 {

View file

@ -327,7 +327,7 @@ func setDefaultMumbaiGethConfig(ctx *cli.Context, config *gethConfig) {
config.Eth.TxPool.AccountQueue = 64
config.Eth.TxPool.GlobalQueue = 131072
config.Eth.TxPool.Lifetime = 90 * time.Minute
config.Node.P2P.MaxPeers = 200
config.Node.P2P.MaxPeers = 50
config.Metrics.Enabled = true
// --pprof is enabled in 'internal/debug/flags.go'
}
@ -350,7 +350,7 @@ func setDefaultBorMainnetGethConfig(ctx *cli.Context, config *gethConfig) {
config.Eth.TxPool.AccountQueue = 64
config.Eth.TxPool.GlobalQueue = 131072
config.Eth.TxPool.Lifetime = 90 * time.Minute
config.Node.P2P.MaxPeers = 200
config.Node.P2P.MaxPeers = 50
config.Metrics.Enabled = true
// --pprof is enabled in 'internal/debug/flags.go'
}

96
common/tracing/context.go Normal file
View file

@ -0,0 +1,96 @@
package tracing
import (
"context"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
type tracerKey struct{}
type Option func(context.Context, trace.Span)
func WithTracer(ctx context.Context, tr trace.Tracer) context.Context {
return context.WithValue(ctx, tracerKey{}, tr)
}
func FromContext(ctx context.Context) trace.Tracer {
tr, _ := ctx.Value(tracerKey{}).(trace.Tracer)
return tr
}
func StartSpan(ctx context.Context, snapName string) (context.Context, trace.Span) {
tr := FromContext(ctx)
if tr == nil {
return ctx, nil
}
ctx, span := tr.Start(ctx, snapName)
ctx = WithTracer(ctx, tr)
return ctx, span
}
func EndSpan(span trace.Span) {
if span != nil {
span.End()
}
}
func Trace(ctx context.Context, spanName string) (context.Context, trace.Span) {
tr := FromContext(ctx)
if tr == nil {
return ctx, nil
}
return tr.Start(ctx, spanName)
}
func Exec(ctx context.Context, spanName string, opts ...Option) {
var span trace.Span
tr := FromContext(ctx)
if tr != nil {
ctx, span = tr.Start(ctx, spanName)
}
for _, optFn := range opts {
optFn(ctx, span)
}
if tr != nil {
span.End()
}
}
func WithTime(fn func(context.Context, trace.Span)) Option {
return func(ctx context.Context, span trace.Span) {
ElapsedTime(ctx, span, "elapsed", fn)
}
}
func ElapsedTime(ctx context.Context, span trace.Span, msg string, fn func(context.Context, trace.Span)) {
var now time.Time
if span != nil {
now = time.Now()
}
fn(ctx, span)
if span != nil {
span.SetAttributes(attribute.Int(msg, int(time.Since(now).Milliseconds())))
}
}
func SetAttributes(span trace.Span, kvs ...attribute.KeyValue) {
if span != nil {
span.SetAttributes(kvs...)
}
}

View file

@ -17,6 +17,7 @@
package beacon
import (
"context"
"errors"
"fmt"
"math/big"
@ -170,10 +171,11 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo
// verifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum consensus engine. The difference between the beacon and classic is
// (a) The following fields are expected to be constants:
// - difficulty is expected to be 0
// - nonce is expected to be 0
// - unclehash is expected to be Hash(emptyHeader)
// - difficulty is expected to be 0
// - nonce is expected to be 0
// - unclehash is expected to be Hash(emptyHeader)
// to be the desired constants
//
// (b) the timestamp is not verified anymore
// (c) the extradata is limited to 32 bytes
func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error {
@ -278,11 +280,11 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
// assembling the block.
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
func (beacon *Beacon) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// FinalizeAndAssemble is different with Prepare, it can be used in both block
// generation and verification. So determine the consensus rules by header type.
if !beacon.IsPoSHeader(header) {
return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts)
return beacon.ethone.FinalizeAndAssemble(ctx, chain, header, state, txs, uncles, receipts)
}
// Finalize and assemble the block
beacon.Finalize(chain, header, state, txs, uncles)
@ -294,9 +296,9 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
//
// Note, the method returns immediately and will send the result async. More
// than one result may also be returned depending on the consensus algorithm.
func (beacon *Beacon) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
func (beacon *Beacon) Seal(ctx context.Context, chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
if !beacon.IsPoSHeader(block.Header()) {
return beacon.ethone.Seal(chain, block, results, stop)
return beacon.ethone.Seal(ctx, chain, block, results, stop)
}
// The seal verification is done by the external consensus engine,
// return directly without pushing any block back. In another word

View file

@ -16,10 +16,13 @@ import (
"time"
lru "github.com/hashicorp/golang-lru"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/tracing"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/bor/api"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
@ -47,7 +50,9 @@ const (
// Bor protocol constants.
var (
defaultSprintLength = uint64(64) // Default number of blocks after which to checkpoint and reset the pending votes
defaultSprintLength = map[string]uint64{
"0": 64,
} // Default number of blocks after which to checkpoint and reset the pending votes
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
@ -164,7 +169,7 @@ func encodeSigHeader(w io.Writer, header *types.Header, c *params.BorConfig) {
header.Nonce,
}
if c.IsJaipur(header.Number.Uint64()) {
if c.IsJaipur(header.Number) {
if header.BaseFee != nil {
enc = append(enc, header.BaseFee)
}
@ -180,8 +185,8 @@ func CalcProducerDelay(number uint64, succession int, c *params.BorConfig) uint6
// When the block is the first block of the sprint, it is expected to be delayed by `producerDelay`.
// That is to allow time for block propagation in the last sprint
delay := c.CalculatePeriod(number)
if number%c.Sprint == 0 {
delay = c.ProducerDelay
if number%c.CalculateSprint(number) == 0 {
delay = c.CalculateProducerDelay(number)
}
if succession > 0 {
@ -245,7 +250,7 @@ func New(
borConfig := chainConfig.Bor
// Set any missing consensus parameters to their defaults
if borConfig != nil && borConfig.Sprint == 0 {
if borConfig != nil && borConfig.CalculateSprint(0) == 0 {
borConfig.Sprint = defaultSprintLength
}
// Allocate the snapshot caches and create the engine
@ -336,7 +341,7 @@ func (c *Bor) verifyHeader(chain consensus.ChainHeaderReader, header *types.Head
}
// check extr adata
isSprintEnd := IsSprintStart(number+1, c.config.Sprint)
isSprintEnd := IsSprintStart(number+1, c.config.CalculateSprint(number))
// Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
signersBytes := len(header.Extra) - extraVanity - extraSeal
@ -450,7 +455,7 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainHeaderReader, header *t
}
// verify the validator list in the last sprint block
if IsSprintStart(number, c.config.Sprint) {
if IsSprintStart(number, c.config.CalculateSprint(number)) {
parentValidatorBytes := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
validatorsBytes := make([]byte, len(snap.ValidatorSet.Validators)*validatorHeaderBytesLength)
@ -682,7 +687,7 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
header.Extra = header.Extra[:extraVanity]
// get validator set if number
if IsSprintStart(number+1, c.config.Sprint) {
if IsSprintStart(number+1, c.config.CalculateSprint(number)) {
newValidators, err := c.spanner.GetCurrentValidators(context.Background(), header.ParentHash, number+1)
if err != nil {
return errUnknownValidators
@ -735,9 +740,8 @@ func (c *Bor) Finalize(chain consensus.ChainHeaderReader, header *types.Header,
headerNumber := header.Number.Uint64()
if IsSprintStart(headerNumber, c.config.Sprint) {
if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) {
ctx := context.Background()
cx := statefull.ChainContext{Chain: chain, Bor: c}
// check and commit span
if err := c.checkAndCommitSpan(ctx, state, header, cx); err != nil {
@ -804,26 +808,35 @@ func (c *Bor) changeContractCodeIfNeeded(headerNumber uint64, state *state.State
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, _ []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
var stateSyncData []*types.StateSyncData
func (c *Bor) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
finalizeCtx, finalizeSpan := tracing.StartSpan(ctx, "bor.FinalizeAndAssemble")
defer tracing.EndSpan(finalizeSpan)
stateSyncData := []*types.StateSyncData{}
headerNumber := header.Number.Uint64()
if IsSprintStart(headerNumber, c.config.Sprint) {
ctx := context.Background()
var err error
if IsSprintStart(headerNumber, c.config.CalculateSprint(headerNumber)) {
cx := statefull.ChainContext{Chain: chain, Bor: c}
// check and commit span
err := c.checkAndCommitSpan(ctx, state, header, cx)
tracing.Exec(finalizeCtx, "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) {
// check and commit span
err = c.checkAndCommitSpan(finalizeCtx, state, header, cx)
})
if err != nil {
log.Error("Error while committing span", "error", err)
return nil, err
}
if c.HeimdallClient != nil {
// commit states
stateSyncData, err = c.CommitStates(ctx, state, header, cx)
tracing.Exec(finalizeCtx, "bor.checkAndCommitSpan", func(ctx context.Context, span trace.Span) {
// commit states
stateSyncData, err = c.CommitStates(finalizeCtx, state, header, cx)
})
if err != nil {
log.Error("Error while committing states", "error", err)
return nil, err
@ -831,13 +844,21 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
}
}
if err := c.changeContractCodeIfNeeded(headerNumber, state); err != nil {
tracing.Exec(finalizeCtx, "bor.changeContractCodeIfNeeded", func(ctx context.Context, span trace.Span) {
err = c.changeContractCodeIfNeeded(headerNumber, state)
})
if err != nil {
log.Error("Error changing contract code", "error", err)
return nil, err
}
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// No block rewards in PoA, so the state remains as it is
tracing.Exec(finalizeCtx, "bor.IntermediateRoot", func(ctx context.Context, span trace.Span) {
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
})
// Uncles are dropped
header.UncleHash = types.CalcUncleHash(nil)
// Assemble block
@ -847,6 +868,14 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *typ
bc := chain.(core.BorStateSyncer)
bc.SetStateSync(stateSyncData)
tracing.SetAttributes(
finalizeSpan,
attribute.Int("number", int(header.Number.Int64())),
attribute.String("hash", header.Hash().String()),
attribute.Int("number of txs", len(txs)),
attribute.Int("gas used", int(block.GasUsed())),
)
// return the final block for sealing
return block, nil
}
@ -862,7 +891,18 @@ func (c *Bor) Authorize(currentSigner common.Address, signFn SignerFn) {
// Seal implements consensus.Engine, attempting to create a sealed block using
// the local signing credentials.
func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
func (c *Bor) Seal(ctx context.Context, chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
_, sealSpan := tracing.StartSpan(ctx, "bor.Seal")
var endSpan bool = true
defer func() {
// Only end span in case of early-returns/errors
if endSpan {
tracing.EndSpan(sealSpan)
}
}()
header := block.Header()
// Sealing the genesis block is not supported
number := header.Number.Uint64()
@ -908,7 +948,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
// Wait until sealing is terminated or delay timeout.
log.Info("Waiting for slot to sign and propagate", "number", number, "hash", header.Hash, "delay-in-sec", uint(delay), "delay", common.PrettyDuration(delay))
go func() {
go func(sealSpan trace.Span) {
select {
case <-stop:
log.Debug("Discarding sealing operation for block", "number", number)
@ -931,13 +971,27 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
"delay", delay,
"headerDifficulty", header.Difficulty,
)
tracing.SetAttributes(
sealSpan,
attribute.Int("number", int(number)),
attribute.String("hash", header.Hash().String()),
attribute.Int("delay", int(delay.Milliseconds())),
attribute.Int("wiggle", int(wiggle.Milliseconds())),
attribute.Bool("out-of-turn", wiggle > 0),
)
tracing.EndSpan(sealSpan)
}
select {
case results <- block.WithSeal(header):
default:
log.Warn("Sealing result was not read by miner", "number", number, "sealhash", SealHash(header, c.config))
}
}()
}(sealSpan)
// Set the endSpan flag to false, as the go routine will handle it
endSpan = false
return nil
}
@ -1000,13 +1054,13 @@ func (c *Bor) checkAndCommitSpan(
) error {
headerNumber := header.Number.Uint64()
currentSpan, err := c.spanner.GetCurrentSpan(ctx, header.ParentHash)
span, err := c.spanner.GetCurrentSpan(ctx, header.ParentHash)
if err != nil {
return err
}
if c.needToCommitSpan(currentSpan, headerNumber) {
return c.FetchAndCommitSpan(ctx, currentSpan.ID+1, state, header, chain)
if c.needToCommitSpan(span, headerNumber) {
return c.FetchAndCommitSpan(ctx, span.ID+1, state, header, chain)
}
return nil
@ -1024,7 +1078,7 @@ func (c *Bor) needToCommitSpan(currentSpan *span.Span, headerNumber uint64) bool
}
// if current block is first block of last sprint in current span
if currentSpan.EndBlock > c.config.Sprint && currentSpan.EndBlock-c.config.Sprint+1 == headerNumber {
if currentSpan.EndBlock > c.config.CalculateSprint(headerNumber) && currentSpan.EndBlock-c.config.CalculateSprint(headerNumber)+1 == headerNumber {
return true
}
@ -1076,6 +1130,7 @@ func (c *Bor) CommitStates(
header *types.Header,
chain statefull.ChainContext,
) ([]*types.StateSyncData, error) {
fetchStart := time.Now()
number := header.Number.Uint64()
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
@ -1083,7 +1138,7 @@ func (c *Bor) CommitStates(
return nil, err
}
to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.Sprint).Time), 0)
to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.CalculateSprint(number)).Time), 0)
lastStateID := _lastStateID.Uint64()
log.Info(
@ -1102,6 +1157,8 @@ func (c *Bor) CommitStates(
}
}
fetchTime := time.Since(fetchStart)
processStart := time.Now()
totalGas := 0 /// limit on gas for state sync per block
chainID := c.chainConfig.ChainID.String()
stateSyncs := make([]*types.StateSyncData, len(eventRecords))
@ -1140,7 +1197,9 @@ func (c *Bor) CommitStates(
lastStateID++
}
log.Info("StateSyncData", "Gas", totalGas, "Block-number", number, "LastStateID", lastStateID, "TotalRecords", len(eventRecords))
processTime := time.Since(processStart)
log.Info("StateSyncData", "gas", totalGas, "number", number, "lastStateID", lastStateID, "total records", len(eventRecords), "fetch time", int(fetchTime.Milliseconds()), "process time", int(processTime.Milliseconds()))
return stateSyncs, nil
}
@ -1195,7 +1254,7 @@ func (c *Bor) getNextHeimdallSpanForTest(
spanBor.StartBlock = spanBor.EndBlock + 1
}
spanBor.EndBlock = spanBor.StartBlock + (100 * c.config.Sprint) - 1
spanBor.EndBlock = spanBor.StartBlock + (100 * c.config.CalculateSprint(headerNumber)) - 1
selectedProducers := make([]valset.Validator, len(snap.ValidatorSet.Validators))
for i, v := range snap.ValidatorSet.Validators {

View file

@ -23,7 +23,9 @@ func TestGenesisContractChange(t *testing.T) {
b := &Bor{
config: &params.BorConfig{
Sprint: 10, // skip sprint transactions in sprint
Sprint: map[string]uint64{
"0": 10,
}, // skip sprint transactions in sprint
BlockAlloc: map[string]interface{}{
// write as interface since that is how it is decoded in genesis
"2": map[string]interface{}{
@ -123,20 +125,20 @@ func TestEncodeSigHeaderJaipur(t *testing.T) {
)
// Jaipur NOT enabled and BaseFee not set
hash := SealHash(h, &params.BorConfig{JaipurBlock: 10})
hash := SealHash(h, &params.BorConfig{JaipurBlock: big.NewInt(10)})
require.Equal(t, hash, hashWithoutBaseFee)
// Jaipur enabled (Jaipur=0) and BaseFee not set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 0})
hash = SealHash(h, &params.BorConfig{JaipurBlock: common.Big0})
require.Equal(t, hash, hashWithoutBaseFee)
h.BaseFee = big.NewInt(2)
// Jaipur enabled (Jaipur=Header block) and BaseFee set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 1})
hash = SealHash(h, &params.BorConfig{JaipurBlock: common.Big1})
require.Equal(t, hash, hashWithBaseFee)
// Jaipur NOT enabled and BaseFee set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 10})
hash = SealHash(h, &params.BorConfig{JaipurBlock: big.NewInt(10)})
require.Equal(t, hash, hashWithoutBaseFee)
}

View file

@ -122,8 +122,8 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
number := header.Number.Uint64()
// Delete the oldest signer from the recent list to allow it signing again
if number >= s.config.Sprint {
delete(snap.Recents, number-s.config.Sprint)
if number >= s.config.CalculateSprint(number) {
delete(snap.Recents, number-s.config.CalculateSprint(number))
}
// Resolve the authorization key and check against signers
@ -145,7 +145,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
snap.Recents[number] = signer
// change validator set and change proposer
if number > 0 && (number+1)%s.config.Sprint == 0 {
if number > 0 && (number+1)%s.config.CalculateSprint(number) == 0 {
if err := validateHeaderExtraField(header.Extra); err != nil {
return nil, err
}

View file

@ -5,7 +5,7 @@ import (
"sort"
"testing"
"github.com/JekaMas/crand"
"github.com/maticnetwork/crand"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"

View file

@ -31,22 +31,22 @@ func (c ChainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
}
// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
type Callmsg struct {
ethereum.CallMsg
}
func (m callmsg) From() common.Address { return m.CallMsg.From }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.CallMsg.To }
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data }
func (m Callmsg) From() common.Address { return m.CallMsg.From }
func (m Callmsg) Nonce() uint64 { return 0 }
func (m Callmsg) CheckNonce() bool { return false }
func (m Callmsg) To() *common.Address { return m.CallMsg.To }
func (m Callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m Callmsg) Gas() uint64 { return m.CallMsg.Gas }
func (m Callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m Callmsg) Data() []byte { return m.CallMsg.Data }
// get system message
func GetSystemMessage(toAddress common.Address, data []byte) callmsg {
return callmsg{
func GetSystemMessage(toAddress common.Address, data []byte) Callmsg {
return Callmsg{
ethereum.CallMsg{
From: systemAddress,
Gas: math.MaxUint64 / 2,
@ -61,7 +61,7 @@ func GetSystemMessage(toAddress common.Address, data []byte) callmsg {
// apply message
func ApplyMessage(
_ context.Context,
msg callmsg,
msg Callmsg,
state *state.StateDB,
header *types.Header,
chainConfig *params.ChainConfig,
@ -93,3 +93,28 @@ func ApplyMessage(
return gasUsed, nil
}
func ApplyBorMessage(vmenv vm.EVM, msg Callmsg) (*core.ExecutionResult, error) {
initialGas := msg.Gas()
// Apply the transaction to the current state (included in the env)
ret, gasLeft, err := vmenv.Call(
vm.AccountRef(msg.From()),
*msg.To(),
msg.Data(),
msg.Gas(),
msg.Value(),
)
// Update the state with pending changes
if err != nil {
vmenv.StateDB.Finalise(true)
}
gasUsed := initialGas - gasLeft
return &core.ExecutionResult{
UsedGas: gasUsed,
Err: err,
ReturnData: ret,
}, nil
}

View file

@ -1,11 +0,0 @@
package bor
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor/valset"
)
//go:generate mockgen -destination=./validators_getter_mock.go -package=bor . ValidatorsGetter
type ValidatorsGetter interface {
GetCurrentValidators(headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error)
}

View file

@ -1,51 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/ethereum/go-ethereum/consensus/bor (interfaces: ValidatorsGetter)
// Package bor is a generated GoMock package.
package bor
import (
reflect "reflect"
common "github.com/ethereum/go-ethereum/common"
valset "github.com/ethereum/go-ethereum/consensus/bor/valset"
gomock "github.com/golang/mock/gomock"
)
// MockValidatorsGetter is a mock of ValidatorsGetter interface.
type MockValidatorsGetter struct {
ctrl *gomock.Controller
recorder *MockValidatorsGetterMockRecorder
}
// MockValidatorsGetterMockRecorder is the mock recorder for MockValidatorsGetter.
type MockValidatorsGetterMockRecorder struct {
mock *MockValidatorsGetter
}
// NewMockValidatorsGetter creates a new mock instance.
func NewMockValidatorsGetter(ctrl *gomock.Controller) *MockValidatorsGetter {
mock := &MockValidatorsGetter{ctrl: ctrl}
mock.recorder = &MockValidatorsGetterMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockValidatorsGetter) EXPECT() *MockValidatorsGetterMockRecorder {
return m.recorder
}
// GetCurrentValidators mocks base method.
func (m *MockValidatorsGetter) GetCurrentValidators(arg0 common.Hash, arg1 uint64) ([]*valset.Validator, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetCurrentValidators", arg0, arg1)
ret0, _ := ret[0].([]*valset.Validator)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetCurrentValidators indicates an expected call of GetCurrentValidators.
func (mr *MockValidatorsGetterMockRecorder) GetCurrentValidators(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCurrentValidators", reflect.TypeOf((*MockValidatorsGetter)(nil).GetCurrentValidators), arg0, arg1)
}

View file

@ -19,6 +19,7 @@ package clique
import (
"bytes"
"context"
"errors"
"fmt"
"io"
@ -569,7 +570,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
func (c *Clique) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// Finalize block
c.Finalize(chain, header, state, txs, uncles)
@ -589,7 +590,7 @@ func (c *Clique) Authorize(signer common.Address, signFn SignerFn) {
// Seal implements consensus.Engine, attempting to create a sealed block using
// the local signing credentials.
func (c *Clique) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
func (c *Clique) Seal(ctx context.Context, chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
header := block.Header()
// Sealing the genesis block is not supported

View file

@ -18,6 +18,7 @@
package consensus
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
@ -97,7 +98,7 @@ type Engine interface {
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
FinalizeAndAssemble(ctx context.Context, chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes
@ -105,7 +106,7 @@ type Engine interface {
//
// Note, the method returns immediately and will send the result async. More
// than one result may also be returned depending on the consensus algorithm.
Seal(chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
Seal(ctx context.Context, chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
// SealHash returns the hash of a block prior to it being sealed.
SealHash(header *types.Header) common.Hash

View file

@ -18,6 +18,7 @@ package ethash
import (
"bytes"
"context"
"errors"
"fmt"
"math/big"
@ -598,7 +599,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block.
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
func (ethash *Ethash) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// Finalize block
ethash.Finalize(chain, header, state, txs, uncles)

View file

@ -17,6 +17,7 @@
package ethash
import (
"context"
"io/ioutil"
"math/big"
"math/rand"
@ -38,7 +39,7 @@ func TestTestMode(t *testing.T) {
defer ethash.Close()
results := make(chan *types.Block)
err := ethash.Seal(nil, types.NewBlockWithHeader(header), results, nil)
err := ethash.Seal(context.Background(), nil, types.NewBlockWithHeader(header), results, nil)
if err != nil {
t.Fatalf("failed to seal block: %v", err)
}
@ -111,12 +112,13 @@ func TestRemoteSealer(t *testing.T) {
// Push new work.
results := make(chan *types.Block)
ethash.Seal(nil, block, results, nil)
err := ethash.Seal(context.Background(), nil, block, results, nil)
var (
work [4]string
err error
)
if err != nil {
t.Error("error in sealing block")
}
var work [4]string
if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() {
t.Error("expect to return a mining work has same hash")
}
@ -128,7 +130,11 @@ func TestRemoteSealer(t *testing.T) {
header = &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(1000)}
block = types.NewBlockWithHeader(header)
sealhash = ethash.SealHash(header)
ethash.Seal(nil, block, results, nil)
err = ethash.Seal(context.Background(), nil, block, results, nil)
if err != nil {
t.Error("error in sealing block")
}
if work, err = api.GetWork(); err != nil || work[0] != sealhash.Hex() {
t.Error("expect to return the latest pushed work")

View file

@ -48,7 +48,7 @@ var (
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
// the block's difficulty requirements.
func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
func (ethash *Ethash) Seal(ctx context.Context, chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
// If we're running a fake PoW, simply return a 0 nonce immediately
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
header := block.Header()
@ -62,7 +62,7 @@ func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block
}
// If we're running a shared PoW, delegate sealing to it
if ethash.shared != nil {
return ethash.shared.Seal(chain, block, results, stop)
return ethash.shared.Seal(ctx, chain, block, results, stop)
}
// Create a runner and the multiple search threads it directs
abort := make(chan struct{})
@ -117,7 +117,8 @@ func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block
case <-ethash.update:
// Thread count was changed on user request, restart
close(abort)
if err := ethash.Seal(chain, block, results, stop); err != nil {
if err := ethash.Seal(ctx, chain, block, results, stop); err != nil {
ethash.config.Log.Error("Failed to restart sealing after update", "err", err)
}
}

View file

@ -17,6 +17,7 @@
package ethash
import (
"context"
"encoding/json"
"io/ioutil"
"math/big"
@ -57,7 +58,11 @@ func TestRemoteNotify(t *testing.T) {
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
block := types.NewBlockWithHeader(header)
ethash.Seal(nil, block, nil, nil)
err := ethash.Seal(context.Background(), nil, block, nil, nil)
if err != nil {
t.Error("error in sealing block")
}
select {
case work := <-sink:
if want := ethash.SealHash(header).Hex(); work[0] != want {
@ -105,7 +110,11 @@ func TestRemoteNotifyFull(t *testing.T) {
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
block := types.NewBlockWithHeader(header)
ethash.Seal(nil, block, nil, nil)
err := ethash.Seal(context.Background(), nil, block, nil, nil)
if err != nil {
t.Error("error in sealing block")
}
select {
case work := <-sink:
if want := "0x" + strconv.FormatUint(header.Number.Uint64(), 16); work["number"] != want {
@ -151,7 +160,11 @@ func TestRemoteMultiNotify(t *testing.T) {
for i := 0; i < cap(sink); i++ {
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
block := types.NewBlockWithHeader(header)
ethash.Seal(nil, block, results, nil)
err := ethash.Seal(context.Background(), nil, block, results, nil)
if err != nil {
t.Error("error in sealing block")
}
}
for i := 0; i < cap(sink); i++ {
@ -167,10 +180,6 @@ func TestRemoteMultiNotify(t *testing.T) {
// Tests that pushing work packages fast to the miner doesn't cause any data race
// issues in the notifications. Full pending block body / --miner.notify.full)
func TestRemoteMultiNotifyFull(t *testing.T) {
// TODO: Understand the test case and Identify the reason for failing tests.
// Also, make it more deterministic.
t.Skip("skipping - non-deterministic test, no dependency on this test for now and not directly relevant to bor")
// Start a simple web server to capture notifications.
sink := make(chan map[string]interface{}, 64)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
@ -184,6 +193,9 @@ func TestRemoteMultiNotifyFull(t *testing.T) {
}
sink <- work
}))
// Allowing the server to start listening.
time.Sleep(2 * time.Second)
defer server.Close()
// Create the custom ethash engine.
@ -204,7 +216,11 @@ func TestRemoteMultiNotifyFull(t *testing.T) {
for i := 0; i < cap(sink); i++ {
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
block := types.NewBlockWithHeader(header)
ethash.Seal(nil, block, results, nil)
err := ethash.Seal(context.Background(), nil, block, results, nil)
if err != nil {
t.Error("error in sealing block")
}
}
for i := 0; i < cap(sink); i++ {
@ -270,7 +286,11 @@ func TestStaleSubmission(t *testing.T) {
for id, c := range testcases {
for _, h := range c.headers {
ethash.Seal(nil, types.NewBlockWithHeader(h), results, nil)
err := ethash.Seal(context.Background(), nil, types.NewBlockWithHeader(h), results, nil)
if err != nil {
t.Error("error in sealing block")
}
}
if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes {
t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res)

View file

@ -59,9 +59,10 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
}
var (
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
baseFeeChangeDenominator = new(big.Int).SetUint64(params.BaseFeeChangeDenominator)
parentGasTarget = parent.GasLimit / params.ElasticityMultiplier
parentGasTargetBig = new(big.Int).SetUint64(parentGasTarget)
baseFeeChangeDenominatorUint64 = params.BaseFeeChangeDenominator(config.Bor, parent.Number)
baseFeeChangeDenominator = new(big.Int).SetUint64(baseFeeChangeDenominatorUint64)
)
// If the parent gasUsed is the same as the target, the baseFee remains unchanged.
if parent.GasUsed == parentGasTarget {

View file

@ -20,7 +20,6 @@ import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
@ -47,12 +46,14 @@ func copyConfig(original *params.ChainConfig) *params.ChainConfig {
TerminalTotalDifficulty: original.TerminalTotalDifficulty,
Ethash: original.Ethash,
Clique: original.Clique,
Bor: original.Bor,
}
}
func config() *params.ChainConfig {
config := copyConfig(params.TestChainConfig)
config.LondonBlock = big.NewInt(5)
config.Bor.DelhiBlock = big.NewInt(8)
return config
}
@ -108,6 +109,8 @@ func TestBlockGasLimits(t *testing.T) {
// TestCalcBaseFee assumes all blocks are 1559-blocks
func TestCalcBaseFee(t *testing.T) {
t.Parallel()
tests := []struct {
parentBaseFee int64
parentGasLimit uint64
@ -117,10 +120,12 @@ func TestCalcBaseFee(t *testing.T) {
{params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target
{params.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below target
{params.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target
{params.InitialBaseFee, 20000000, 20000000, 1125000000}, // usage full
{params.InitialBaseFee, 20000000, 0, 875000000}, // usage 0
}
for i, test := range tests {
parent := &types.Header{
Number: common.Big32,
Number: big.NewInt(6),
GasLimit: test.parentGasLimit,
GasUsed: test.parentGasUsed,
BaseFee: big.NewInt(test.parentBaseFee),
@ -130,3 +135,38 @@ func TestCalcBaseFee(t *testing.T) {
}
}
}
// TestCalcBaseFee assumes all blocks are 1559-blocks post Delhi Hard Fork
func TestCalcBaseFeeDelhi(t *testing.T) {
t.Parallel()
testConfig := copyConfig(config())
// Test Delhi Hard Fork
// Hard fork kicks in at block 8
tests := []struct {
parentBaseFee int64
parentGasLimit uint64
parentGasUsed uint64
expectedBaseFee int64
}{
{params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target
{params.InitialBaseFee, 20000000, 9000000, 993750000}, // usage below target
{params.InitialBaseFee, 20000000, 11000000, 1006250000}, // usage above target
{params.InitialBaseFee, 20000000, 20000000, 1062500000}, // usage full
{params.InitialBaseFee, 20000000, 0, 937500000}, // usage 0
}
for i, test := range tests {
parent := &types.Header{
Number: big.NewInt(8),
GasLimit: test.parentGasLimit,
GasUsed: test.parentGasUsed,
BaseFee: big.NewInt(test.parentBaseFee),
}
if have, want := CalcBaseFee(testConfig, parent), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 {
t.Errorf("test %d: have %d want %d, ", i, have, want)
}
}
}

View file

@ -162,7 +162,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
// genUncles generates blocks with two uncle headers.
func genUncles(i int, gen *BlockGen) {
if i >= 6 {
if i >= 7 {
b2 := gen.PrevBlock(i - 6).Header()
b2.Extra = []byte("foo")
gen.AddUncle(b2)

View file

@ -92,7 +92,6 @@ const (
txLookupCacheLimit = 1024
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
TriesInMemory = 128
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
//
@ -132,6 +131,7 @@ type CacheConfig struct {
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
Preimages bool // Whether to store preimage of trie key to the disk
TriesInMemory uint64 // Number of recent tries to keep in memory
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
}
@ -144,6 +144,7 @@ var DefaultCacheConfig = &CacheConfig{
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
SnapshotWait: true,
TriesInMemory: 128,
}
// BlockChain represents the canonical chain given a database with a genesis
@ -229,6 +230,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
if cacheConfig == nil {
cacheConfig = DefaultCacheConfig
}
if cacheConfig.TriesInMemory <= 0 {
cacheConfig.TriesInMemory = DefaultCacheConfig.TriesInMemory
}
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
receiptsCache, _ := lru.New(receiptsCacheLimit)
@ -842,7 +847,7 @@ func (bc *BlockChain) Stop() {
if !bc.cacheConfig.TrieDirtyDisabled {
triedb := bc.stateCache.TrieDB()
for _, offset := range []uint64{0, 1, TriesInMemory - 1} {
for _, offset := range []uint64{0, 1, bc.cacheConfig.TriesInMemory - 1} {
if number := bc.CurrentBlock().NumberU64(); number > offset {
recent := bc.GetBlockByNumber(number - offset)
@ -1310,7 +1315,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
bc.triegc.Push(root, -int64(block.NumberU64()))
if current := block.NumberU64(); current > TriesInMemory {
if current := block.NumberU64(); current > bc.cacheConfig.TriesInMemory {
// If we exceeded our memory allowance, flush matured singleton nodes to disk
var (
nodes, imgs = triedb.Size()
@ -1320,7 +1325,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
triedb.Cap(limit - ethdb.IdealBatchSize)
}
// Find the next state trie we need to commit
chosen := current - TriesInMemory
chosen := current - bc.cacheConfig.TriesInMemory
// If we exceeded out time allowance, flush an entire trie to disk
if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
@ -1332,8 +1337,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
} else {
// If we're exceeding limits but haven't reached a large enough memory gap,
// warn the user that the system is becoming unstable.
if chosen < lastWrite+TriesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/TriesInMemory)
if chosen < lastWrite+bc.cacheConfig.TriesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TriesInMemory, "optimum", float64(chosen-lastWrite)/float64((bc.cacheConfig.TriesInMemory)))
}
// Flush an entire trie and restart the counters
triedb.Commit(header.Root, true, nil)

View file

@ -1652,7 +1652,7 @@ func TestTrieForkGC(t *testing.T) {
db := rawdb.NewMemoryDatabase()
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
// Generate a bunch of fork blocks, each side forking from the canonical chain
forks := make([]*types.Block, len(blocks))
@ -1681,7 +1681,7 @@ func TestTrieForkGC(t *testing.T) {
}
}
// Dereference all the recent tries and ensure no past trie is left in
for i := 0; i < TriesInMemory; i++ {
for i := 0; i < int(chain.cacheConfig.TriesInMemory); i++ {
chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
}
@ -1700,8 +1700,8 @@ func TestLargeReorgTrieGC(t *testing.T) {
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*int(DefaultCacheConfig.TriesInMemory)+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
// Import the shared chain and the original canonical one
diskdb := rawdb.NewMemoryDatabase()
@ -1736,7 +1736,8 @@ func TestLargeReorgTrieGC(t *testing.T) {
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
t.Fatalf("failed to finalize competitor chain: %v", err)
}
for i, block := range competitor[:len(competitor)-TriesInMemory] {
for i, block := range competitor[:len(competitor)-int(chain.cacheConfig.TriesInMemory)] {
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
t.Fatalf("competitor %d: competing chain state missing", i)
}
@ -1882,8 +1883,8 @@ func TestInsertReceiptChainRollback(t *testing.T) {
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
//
// Details at:
// - https://github.com/ethereum/go-ethereum/issues/18977
// - https://github.com/ethereum/go-ethereum/pull/18988
// - https://github.com/ethereum/go-ethereum/issues/18977
// - https://github.com/ethereum/go-ethereum/pull/18988
func TestLowDiffLongChain(t *testing.T) {
// Generate a canonical chain to act as the main dataset
engine := ethash.NewFaker()
@ -1892,7 +1893,7 @@ func TestLowDiffLongChain(t *testing.T) {
// We must use a pretty long chain to ensure that the fork doesn't overtake us
// until after at least 128 blocks post tip
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*TriesInMemory, func(i int, b *BlockGen) {
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})
b.OffsetTime(-9)
})
@ -1910,7 +1911,7 @@ func TestLowDiffLongChain(t *testing.T) {
}
// Generate fork chain, starting from an early block
parent := blocks[10]
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*TriesInMemory, func(i int, b *BlockGen) {
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
})
@ -1979,7 +1980,8 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// Set the terminal total difficulty in the config
gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
}
blocks, _ := GenerateChain(&chainConfig, genesis, genEngine, db, 2*TriesInMemory, func(i int, gen *BlockGen) {
blocks, _ := GenerateChain(&chainConfig, genesis, genEngine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, gen *BlockGen) {
tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key)
if err != nil {
t.Fatalf("failed to create tx: %v", err)
@ -1991,9 +1993,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
lastPrunedIndex := len(blocks) - TriesInMemory - 1
lastPrunedIndex := len(blocks) - int(chain.cacheConfig.TriesInMemory) - 1
lastPrunedBlock := blocks[lastPrunedIndex]
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory]
firstNonPrunedBlock := blocks[len(blocks)-int(chain.cacheConfig.TriesInMemory)]
// Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
@ -2019,7 +2021,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// Generate fork chain, make it longer than canon
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
parent := blocks[parentIndex]
fork, _ := GenerateChain(&chainConfig, parent, genEngine, db, 2*TriesInMemory, func(i int, b *BlockGen) {
fork, _ := GenerateChain(&chainConfig, parent, genEngine, db, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
})
// Prepend the parent(s)
@ -2046,7 +2048,8 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// That is: the sidechain for import contains some blocks already present in canon chain.
// So the blocks are
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
// ^ ^ ^ pruned
//
// ^ ^ ^ pruned
func TestPrunedImportSide(t *testing.T) {
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
//glogger.Verbosity(3)
@ -2841,9 +2844,9 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
// This internally leads to a sidechain import, since the blocks trigger an
// ErrPrunedAncestor error.
// This may e.g. happen if
// 1. Downloader rollbacks a batch of inserted blocks and exits
// 2. Downloader starts to sync again
// 3. The blocks fetched are all known and canonical blocks
// 1. Downloader rollbacks a batch of inserted blocks and exits
// 2. Downloader starts to sync again
// 3. The blocks fetched are all known and canonical blocks
func TestSideImportPrunedBlocks(t *testing.T) {
// Generate a canonical chain to act as the main dataset
engine := ethash.NewFaker()
@ -2851,7 +2854,7 @@ func TestSideImportPrunedBlocks(t *testing.T) {
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
// Generate and import the canonical chain
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*int(DefaultCacheConfig.TriesInMemory), nil)
diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
@ -2863,14 +2866,15 @@ func TestSideImportPrunedBlocks(t *testing.T) {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
lastPrunedIndex := len(blocks) - TriesInMemory - 1
lastPrunedIndex := len(blocks) - int(chain.cacheConfig.TriesInMemory) - 1
lastPrunedBlock := blocks[lastPrunedIndex]
// Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
}
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory]
firstNonPrunedBlock := blocks[len(blocks)-int(chain.cacheConfig.TriesInMemory)]
// Verify firstNonPrunedBlock is not pruned
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
@ -3356,20 +3360,19 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
// TestInitThenFailCreateContract tests a pretty notorious case that happened
// on mainnet over blocks 7338108, 7338110 and 7338115.
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
// with 0.001 ether (thus created but no code)
// - Block 7338110: a CREATE2 is attempted. The CREATE2 would deploy code on
// the same address e771789f5cccac282f23bb7add5690e1f6ca467c. However, the
// deployment fails due to OOG during initcode execution
// - Block 7338115: another tx checks the balance of
// e771789f5cccac282f23bb7add5690e1f6ca467c, and the snapshotter returned it as
// zero.
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
// with 0.001 ether (thus created but no code)
// - Block 7338110: a CREATE2 is attempted. The CREATE2 would deploy code on
// the same address e771789f5cccac282f23bb7add5690e1f6ca467c. However, the
// deployment fails due to OOG during initcode execution
// - Block 7338115: another tx checks the balance of
// e771789f5cccac282f23bb7add5690e1f6ca467c, and the snapshotter returned it as
// zero.
//
// The problem being that the snapshotter maintains a destructset, and adds items
// to the destructset in case something is created "onto" an existing item.
// We need to either roll back the snapDestructs, or not place it into snapDestructs
// in the first place.
//
func TestInitThenFailCreateContract(t *testing.T) {
var (
// Generate a canonical chain to act as the main dataset
@ -3558,13 +3561,13 @@ func TestEIP2718Transition(t *testing.T) {
// TestEIP1559Transition tests the following:
//
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
// 3. Only the transaction's tip will be received by the coinbase.
// 4. The transaction sender pays for both the tip and baseFee.
// 5. The coinbase receives only the partially realized tip when
// gasFeeCap - gasTipCap < baseFee.
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
// 3. Only the transaction's tip will be received by the coinbase.
// 4. The transaction sender pays for both the tip and baseFee.
// 5. The coinbase receives only the partially realized tip when
// gasFeeCap - gasTipCap < baseFee.
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
func TestEIP1559Transition(t *testing.T) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")

View file

@ -17,6 +17,7 @@
package core
import (
"context"
"fmt"
"math/big"
@ -258,7 +259,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
}
if b.engine != nil {
// Finalize and seal the block
block, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
block, _ := b.engine.FinalizeAndAssemble(context.Background(), chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
// Write state changes to db
root, err := statedb.Commit(config.IsEIP158(b.header.Number))

View file

@ -20,7 +20,7 @@ import (
"errors"
"math/big"
"github.com/JekaMas/crand"
"github.com/maticnetwork/crand"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"

View file

@ -1826,7 +1826,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
sideblocks, _ = core.GenerateChain(params.BorUnittestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), tt.sidechainBlocks, func(i int, b *core.BlockGen) {
b.SetCoinbase(testAddress1)
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.Sprint) {
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.CalculateSprint(b.Number().Uint64())) {
b.SetExtra(back.Genesis.ExtraData)
} else {
b.SetExtra(make([]byte, 32+crypto.SignatureLength))
@ -1841,7 +1841,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
b.SetCoinbase(miner.TestBankAddress)
b.SetDifficulty(big.NewInt(1000000))
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.Sprint) {
if bor.IsSprintStart(b.Number().Uint64(), params.BorUnittestChainConfig.Bor.CalculateSprint(b.Number().Uint64())) {
b.SetExtra(back.Genesis.ExtraData)
} else {
b.SetExtra(make([]byte, 32+crypto.SignatureLength))

View file

@ -2082,6 +2082,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
// verifyNoGaps checks that there are no gaps after the initial set of blocks in
// the database and errors if found.
//
//nolint:gocognit
func verifyNoGaps(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks) {
t.Helper()
@ -2135,6 +2136,7 @@ func verifyNoGaps(t *testing.T, chain *core.BlockChain, canonical bool, inserted
// verifyCutoff checks that there are no chain data available in the chain after
// the specified limit, but that it is available before.
//
//nolint:gocognit
func verifyCutoff(t *testing.T, chain *core.BlockChain, canonical bool, inserted types.Blocks, head int) {
t.Helper()

View file

@ -2715,6 +2715,8 @@ func defaultTxPoolRapidConfig() txPoolRapidConfig {
// TestSmallTxPool is not something to run in parallel as far it uses all CPUs
// nolint:paralleltest
func TestSmallTxPool(t *testing.T) {
t.Parallel()
t.Skip("a red test to be fixed")
cfg := defaultTxPoolRapidConfig()
@ -2734,6 +2736,8 @@ func TestSmallTxPool(t *testing.T) {
// This test is not something to run in parallel as far it uses all CPUs
// nolint:paralleltest
func TestBigTxPool(t *testing.T) {
t.Parallel()
t.Skip("a red test to be fixed")
cfg := defaultTxPoolRapidConfig()
@ -2743,6 +2747,8 @@ func TestBigTxPool(t *testing.T) {
//nolint:gocognit,thelper
func testPoolBatchInsert(t *testing.T, cfg txPoolRapidConfig) {
t.Helper()
t.Parallel()
const debug = false

View file

@ -508,6 +508,10 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa
// 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.
@ -550,6 +554,10 @@ func (t *TransactionsByPriceAndNonce) Shift() {
heap.Pop(&t.heads)
}
func (t *TransactionsByPriceAndNonce) GetTxs() int {
return len(t.txs)
}
// Pop removes the best transaction, *not* replacing it with the next one from
// the same account. This should be used when a transaction cannot be executed
// and hence all subsequent ones should be discarded from the same account.

View file

@ -74,6 +74,8 @@ type StateDB interface {
AddPreimage(common.Hash, []byte)
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
Finalise(bool)
}
// CallContext provides a basic interface for the EVM calling conventions. The EVM

View file

@ -5,18 +5,16 @@
- [Configuration file](./config.md)
## Deprecation notes
## Additional notes
- The new entrypoint to run the Bor client is ```server```.
```
$ bor server
```
```
$ bor server <flags>
```
- Toml files to configure nodes are being deprecated. Currently, we only allow for static and trusted nodes to be configured using toml files.
- Toml files used earlier just to configure static/trusted nodes are being deprecated. Instead, a toml file now can be used instead of flags and can contain all configuration for the node to run. The link to a sample config file is given above. To simply run bor with a configuration file, the following command can be used.
```
$ bor server --config ./legacy.toml
```
- ```Admin```, ```Personal``` and account related endpoints in ```Eth``` are being removed from the JsonRPC interface. Some of this functionality will be moved to the new GRPC server for operational tasks.
```
$ bor server --config <path_to_config.toml>
```

View file

@ -4,4 +4,6 @@ The ```bor removedb``` command will remove the blockchain and state databases at
## Options
- ```datadir```: Path of the data directory to store information
- ```address```: Address of the grpc endpoint
- ```datadir```: Path of the data directory to store information

View file

@ -16,18 +16,22 @@ The ```bor server``` command runs the Bor client.
- ```config```: File for the config file
- ```syncmode```: Blockchain sync mode ("fast", "full", or "snap")
- ```syncmode```: Blockchain sync mode (only "full" sync supported)
- ```gcmode```: Blockchain garbage collection mode ("full", "archive")
- ```requiredblocks```: Comma separated block number-to-hash mappings to enforce (<number>=<hash>)
- ```eth.requiredblocks```: Comma separated block number-to-hash mappings to require for peering (<number>=<hash>)
- ```snapshot```: Disables/Enables the snapshot-database mode (default = true)
- ```snapshot```: Enables the snapshot-database mode (default = true)
- ```bor.logs```: Enables bor log retrieval (default = false)
- ```bor.heimdall```: URL of Heimdall service
- ```bor.withoutheimdall```: Run without Heimdall service (for testing purpose)
- ```bor.heimdallgRPC```: Address of Heimdall gRPC service
- ```ethstats```: Reporting URL of a ethstats service (nodename:secret@host:port)
- ```gpo.blocks```: Number of recent blocks to check for gas prices
@ -76,6 +80,8 @@ The ```bor server``` command runs the Bor client.
- ```cache.preimages```: Enable recording the SHA3/keccak preimages of trie keys
- ```cache.triesinmemory```: Number of block states (tries) to keep in memory (default = 128)
- ```txlookuplimit```: Number of recent blocks to maintain transactions index for (default = about 56 days, 0 = entire chain)
### JsonRPC Options
@ -92,9 +98,7 @@ The ```bor server``` command runs the Bor client.
- ```http.vhosts```: Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.
- ```ws.corsdomain```: Comma separated list of domains from which to accept cross origin requests (browser enforced)
- ```ws.vhosts```: Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.
- ```ws.origins```: Origins from which to accept websockets requests
- ```graphql.corsdomain```: Comma separated list of domains from which to accept cross origin requests (browser enforced)

View file

@ -22,7 +22,7 @@ ethstats = ""
["eth.requiredblocks"]
[p2p]
maxpeers = 30
maxpeers = 50
maxpendpeers = 50
bind = "0.0.0.0"
port = 30303

View file

@ -218,6 +218,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
TrieTimeLimit: config.TrieTimeout,
SnapshotLimit: config.SnapshotCache,
Preimages: config.Preimages,
TriesInMemory: config.TriesInMemory,
}
)

View file

@ -176,6 +176,7 @@ type Config struct {
TrieTimeout time.Duration
SnapshotCache int
Preimages bool
TriesInMemory uint64
// Mining options
Miner miner.Config

View file

@ -337,8 +337,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
return nil, errors.New("No chain config found. Proper PublicFilterAPI initialization required")
}
// get sprint from bor config
sprint := api.chainConfig.Bor.Sprint
borConfig := api.chainConfig.Bor
var filter *Filter
var borLogsFilter *BorBlockLogsFilter
@ -347,7 +346,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
filter = NewBlockFilter(api.backend, *crit.BlockHash, crit.Addresses, crit.Topics)
// Block bor filter
if api.borLogs {
borLogsFilter = NewBorBlockLogsFilter(api.backend, sprint, *crit.BlockHash, crit.Addresses, crit.Topics)
borLogsFilter = NewBorBlockLogsFilter(api.backend, borConfig, *crit.BlockHash, crit.Addresses, crit.Topics)
}
} else {
// Convert the RPC block numbers into internal representations
@ -363,7 +362,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics)
// Block bor filter
if api.borLogs {
borLogsFilter = NewBorBlockLogsRangeFilter(api.backend, sprint, begin, end, crit.Addresses, crit.Topics)
borLogsFilter = NewBorBlockLogsRangeFilter(api.backend, borConfig, begin, end, crit.Addresses, crit.Topics)
}
}

View file

@ -23,12 +23,12 @@ func (api *PublicFilterAPI) GetBorBlockLogs(ctx context.Context, crit FilterCrit
}
// get sprint from bor config
sprint := api.chainConfig.Bor.Sprint
borConfig := api.chainConfig.Bor
var filter *BorBlockLogsFilter
if crit.BlockHash != nil {
// Block filter requested, construct a single-shot filter
filter = NewBorBlockLogsFilter(api.backend, sprint, *crit.BlockHash, crit.Addresses, crit.Topics)
filter = NewBorBlockLogsFilter(api.backend, borConfig, *crit.BlockHash, crit.Addresses, crit.Topics)
} else {
// Convert the RPC block numbers into internal representations
begin := rpc.LatestBlockNumber.Int64()
@ -40,7 +40,7 @@ func (api *PublicFilterAPI) GetBorBlockLogs(ctx context.Context, crit FilterCrit
end = crit.ToBlock.Int64()
}
// Construct the range filter
filter = NewBorBlockLogsRangeFilter(api.backend, sprint, begin, end, crit.Addresses, crit.Topics)
filter = NewBorBlockLogsRangeFilter(api.backend, borConfig, begin, end, crit.Addresses, crit.Topics)
}
// Run the filter and return all the logs

View file

@ -22,13 +22,14 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
// BorBlockLogsFilter can be used to retrieve and filter logs.
type BorBlockLogsFilter struct {
backend Backend
sprint uint64
backend Backend
borConfig *params.BorConfig
db ethdb.Database
addresses []common.Address
@ -40,9 +41,9 @@ type BorBlockLogsFilter struct {
// NewBorBlockLogsRangeFilter creates a new filter which uses a bloom filter on blocks to
// figure out whether a particular block is interesting or not.
func NewBorBlockLogsRangeFilter(backend Backend, sprint uint64, begin, end int64, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
func NewBorBlockLogsRangeFilter(backend Backend, borConfig *params.BorConfig, begin, end int64, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
// Create a generic filter and convert it into a range filter
filter := newBorBlockLogsFilter(backend, sprint, addresses, topics)
filter := newBorBlockLogsFilter(backend, borConfig, addresses, topics)
filter.begin = begin
filter.end = end
@ -51,19 +52,19 @@ func NewBorBlockLogsRangeFilter(backend Backend, sprint uint64, begin, end int64
// NewBorBlockLogsFilter creates a new filter which directly inspects the contents of
// a block to figure out whether it is interesting or not.
func NewBorBlockLogsFilter(backend Backend, sprint uint64, block common.Hash, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
func NewBorBlockLogsFilter(backend Backend, borConfig *params.BorConfig, block common.Hash, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
// Create a generic filter and convert it into a block filter
filter := newBorBlockLogsFilter(backend, sprint, addresses, topics)
filter := newBorBlockLogsFilter(backend, borConfig, addresses, topics)
filter.block = block
return filter
}
// newBorBlockLogsFilter creates a generic filter that can either filter based on a block hash,
// or based on range queries. The search criteria needs to be explicitly set.
func newBorBlockLogsFilter(backend Backend, sprint uint64, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
func newBorBlockLogsFilter(backend Backend, borConfig *params.BorConfig, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
return &BorBlockLogsFilter{
backend: backend,
sprint: sprint,
borConfig: borConfig,
addresses: addresses,
topics: topics,
db: backend.ChainDb(),
@ -94,7 +95,7 @@ func (f *BorBlockLogsFilter) Logs(ctx context.Context) ([]*types.Log, error) {
}
// adjust begin for sprint
f.begin = currentSprintEnd(f.sprint, f.begin)
f.begin = currentSprintEnd(f.borConfig.CalculateSprint(uint64(f.begin)), f.begin)
end := f.end
if f.end == -1 {
@ -110,7 +111,9 @@ func (f *BorBlockLogsFilter) Logs(ctx context.Context) ([]*types.Log, error) {
func (f *BorBlockLogsFilter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
var logs []*types.Log
for ; f.begin <= int64(end); f.begin = f.begin + int64(f.sprint) {
sprintLength := f.borConfig.CalculateSprint(uint64(f.begin))
for ; f.begin <= int64(end); f.begin = f.begin + int64(sprintLength) {
header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
if header == nil || err != nil {
return logs, err
@ -128,6 +131,7 @@ func (f *BorBlockLogsFilter) unindexedLogs(ctx context.Context, end uint64) ([]*
return logs, err
}
logs = append(logs, found...)
sprintLength = f.borConfig.CalculateSprint(uint64(f.begin))
}
return logs, nil
}

View file

@ -62,7 +62,7 @@ func TestBorFilters(t *testing.T) {
hash4 = common.BytesToHash([]byte("topic4"))
db = NewMockDatabase(ctrl)
sprint = params.TestChainConfig.Bor.Sprint
testBorConfig = params.TestChainConfig.Bor
)
backend := NewMockBackend(ctrl)
@ -74,7 +74,7 @@ func TestBorFilters(t *testing.T) {
// Block 1
backend.expectBorReceiptsFromMock([]*common.Hash{nil, &hash1, &hash2, &hash3, &hash4})
filter := NewBorBlockLogsRangeFilter(backend, sprint, 0, 18, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}})
filter := NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, 18, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}})
logs, err := filter.Logs(context.Background())
if err != nil {
@ -88,7 +88,7 @@ func TestBorFilters(t *testing.T) {
// Block 2
backend.expectBorReceiptsFromMock([]*common.Hash{&hash1, &hash3})
filter = NewBorBlockLogsRangeFilter(backend, sprint, 990, 999, []common.Address{addr}, [][]common.Hash{{hash3}})
filter = NewBorBlockLogsRangeFilter(backend, testBorConfig, 990, 999, []common.Address{addr}, [][]common.Hash{{hash3}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 1 {
@ -102,7 +102,7 @@ func TestBorFilters(t *testing.T) {
// Block 3
backend.expectBorReceiptsFromMock([]*common.Hash{&hash1, &hash2, &hash3})
filter = NewBorBlockLogsRangeFilter(backend, sprint, 992, 1000, []common.Address{addr}, [][]common.Hash{{hash3}})
filter = NewBorBlockLogsRangeFilter(backend, testBorConfig, 992, 1000, []common.Address{addr}, [][]common.Hash{{hash3}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 1 {
@ -116,7 +116,7 @@ func TestBorFilters(t *testing.T) {
// Block 4
backend.expectBorReceiptsFromMock([]*common.Hash{&hash1, &hash2, nil, &hash3})
filter = NewBorBlockLogsRangeFilter(backend, sprint, 1, 16, []common.Address{addr}, [][]common.Hash{{hash1, hash2}})
filter = NewBorBlockLogsRangeFilter(backend, testBorConfig, 1, 16, []common.Address{addr}, [][]common.Hash{{hash1, hash2}})
logs, _ = filter.Logs(context.Background())
@ -128,7 +128,7 @@ func TestBorFilters(t *testing.T) {
backend.expectBorReceiptsFromMock([]*common.Hash{&hash1, &hash2, nil, &hash3, &hash4, nil})
failHash := common.BytesToHash([]byte("fail"))
filter = NewBorBlockLogsRangeFilter(backend, sprint, 0, 20, nil, [][]common.Hash{{failHash}})
filter = NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, 20, nil, [][]common.Hash{{failHash}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 0 {
@ -139,7 +139,7 @@ func TestBorFilters(t *testing.T) {
backend.expectBorReceiptsFromMock([]*common.Hash{&hash1, &hash2, nil, &hash3, &hash4, nil})
failAddr := common.BytesToAddress([]byte("failmenow"))
filter = NewBorBlockLogsRangeFilter(backend, sprint, 0, 20, []common.Address{failAddr}, nil)
filter = NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, 20, []common.Address{failAddr}, nil)
logs, _ = filter.Logs(context.Background())
if len(logs) != 0 {
@ -149,7 +149,7 @@ func TestBorFilters(t *testing.T) {
// Block 7
backend.expectBorReceiptsFromMock([]*common.Hash{&hash1, &hash2, nil, &hash3, &hash4, nil})
filter = NewBorBlockLogsRangeFilter(backend, sprint, 0, 20, nil, [][]common.Hash{{failHash}, {hash1}})
filter = NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, 20, nil, [][]common.Hash{{failHash}, {hash1}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 0 {

View file

@ -31,9 +31,11 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
@ -70,6 +72,8 @@ const (
defaultIOFlag = false
)
var defaultBorTraceEnabled = newBoolPtr(false)
// Backend interface provides the common API services (that are provided by
// both full and light clients) with access to necessary functions.
type Backend interface {
@ -87,6 +91,9 @@ type Backend interface {
// so this method should be called with the parent.
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error)
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error)
// Bor related APIs
GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
}
// API is the collection of tracing APIs exposed over the private debugging endpoint.
@ -171,14 +178,35 @@ func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber
return api.blockByHash(ctx, hash)
}
// returns block transactions along with state-sync transaction if present
func (api *API) getAllBlockTransactions(ctx context.Context, block *types.Block) (types.Transactions, bool) {
txs := block.Transactions()
stateSyncPresent := false
borReceipt := rawdb.ReadBorReceipt(api.backend.ChainDb(), block.Hash(), block.NumberU64())
if borReceipt != nil {
txHash := types.GetDerivedBorTxHash(types.BorReceiptKey(block.Number().Uint64(), block.Hash()))
if txHash != (common.Hash{}) {
borTx, _, _, _, _ := api.backend.GetBorBlockTransactionWithBlockHash(ctx, txHash, block.Hash())
txs = append(txs, borTx)
stateSyncPresent = true
}
}
return txs, stateSyncPresent
}
// TraceConfig holds extra parameters to trace functions.
type TraceConfig struct {
*logger.Config
Tracer *string
Timeout *string
Reexec *uint64
Path *string
IOFlag *bool
Tracer *string
Timeout *string
Reexec *uint64
Path *string
IOFlag *bool
BorTraceEnabled *bool
BorTx *bool
}
// TraceCallConfig is the config for traceCall API. It holds one more
@ -194,8 +222,9 @@ type TraceCallConfig struct {
// StdTraceConfig holds extra parameters to standard-json trace functions.
type StdTraceConfig struct {
logger.Config
Reexec *uint64
TxHash common.Hash
Reexec *uint64
TxHash common.Hash
BorTraceEnabled *bool
}
// txTraceResult is the result of a single transaction trace.
@ -249,6 +278,16 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
// executes all the transactions contained within. The return value will be one item
// per transaction, dependent on the requested tracer.
func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
if config == nil {
config = &TraceConfig{
BorTraceEnabled: defaultBorTraceEnabled,
BorTx: newBoolPtr(false),
}
}
if config.BorTraceEnabled == nil {
config.BorTraceEnabled = defaultBorTraceEnabled
}
// Tracing a chain is a **long** operation, only do with subscriptions
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
@ -283,19 +322,39 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
signer := types.MakeSigner(api.backend.ChainConfig(), task.block.Number())
blockCtx := core.NewEVMBlockContext(task.block.Header(), api.chainContext(localctx), nil)
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
txs, stateSyncPresent := api.getAllBlockTransactions(ctx, task.block)
if !*config.BorTraceEnabled && stateSyncPresent {
txs = txs[:len(txs)-1]
stateSyncPresent = false
}
for i, tx := range txs {
msg, _ := tx.AsMessage(signer, task.block.BaseFee())
txctx := &Context{
BlockHash: task.block.Hash(),
TxIndex: i,
TxHash: tx.Hash(),
}
res, err := api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
var res interface{}
var err error
if stateSyncPresent && i == len(txs)-1 {
if *config.BorTraceEnabled {
config.BorTx = newBoolPtr(true)
res, err = api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
}
} else {
res, err = api.traceTx(localctx, msg, txctx, blockCtx, task.statedb, config)
}
if err != nil {
task.results[i] = &txTraceResult{Error: err.Error()}
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
break
}
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number()))
task.results[i] = &txTraceResult{Result: res}
@ -439,6 +498,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
return sub, nil
}
func newBoolPtr(bb bool) *bool {
b := bb
return &b
}
// TraceBlockByNumber returns the structured logs created during the execution of
// EVM and returns them as a JSON object.
func (api *API) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *TraceConfig) ([]*txTraceResult, error) {
@ -501,9 +565,35 @@ func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash,
return api.standardTraceBlockToFile(ctx, block, config)
}
func prepareCallMessage(msg core.Message) statefull.Callmsg {
return statefull.Callmsg{
CallMsg: ethereum.CallMsg{
From: msg.From(),
To: msg.To(),
Gas: msg.Gas(),
GasPrice: msg.GasPrice(),
GasFeeCap: msg.GasFeeCap(),
GasTipCap: msg.GasTipCap(),
Value: msg.Value(),
Data: msg.Data(),
AccessList: msg.AccessList(),
}}
}
// IntermediateRoots executes a block (bad- or canon- or side-), and returns a list
// of intermediate roots: the stateroot after each transaction.
func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) {
if config == nil {
config = &TraceConfig{
BorTraceEnabled: defaultBorTraceEnabled,
BorTx: newBoolPtr(false),
}
}
if config.BorTraceEnabled == nil {
config.BorTraceEnabled = defaultBorTraceEnabled
}
block, _ := api.blockByHash(ctx, hash)
if block == nil {
// Check in the bad blocks
@ -534,23 +624,47 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
)
for i, tx := range block.Transactions() {
txs, stateSyncPresent := api.getAllBlockTransactions(ctx, block)
for i, tx := range txs {
var (
msg, _ = tx.AsMessage(signer, block.BaseFee())
txContext = core.NewEVMTxContext(msg)
vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
)
statedb.Prepare(tx.Hash(), i)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
// We intentionally don't return the error here: if we do, then the RPC server will not
// return the roots. Most likely, the caller already knows that a certain transaction fails to
// be included, but still want the intermediate roots that led to that point.
// It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
// executable.
// N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
return roots, nil
//nolint: nestif
if stateSyncPresent && i == len(txs)-1 {
if *config.BorTraceEnabled {
callmsg := prepareCallMessage(msg)
if _, err := statefull.ApplyMessage(ctx, callmsg, statedb, block.Header(), api.backend.ChainConfig(), api.chainContext(ctx)); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
// We intentionally don't return the error here: if we do, then the RPC server will not
// return the roots. Most likely, the caller already knows that a certain transaction fails to
// be included, but still want the intermediate roots that led to that point.
// It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
// executable.
// N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
return roots, nil
}
} else {
break
}
} else {
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
// We intentionally don't return the error here: if we do, then the RPC server will not
// return the roots. Most likely, the caller already knows that a certain transaction fails to
// be included, but still want the intermediate roots that led to that point.
// It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
// executable.
// N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
return roots, nil
}
}
// calling IntermediateRoot will internally call Finalize on the state
// so any modifications are written to the trie
roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects))
@ -573,6 +687,18 @@ func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Has
// executes all the transactions contained within. The return value will be one item
// per transaction, dependent on the requestd tracer.
func (api *API) traceBlock(ctx context.Context, block *types.Block, config *TraceConfig) ([]*txTraceResult, error) {
if config == nil {
config = &TraceConfig{
BorTraceEnabled: defaultBorTraceEnabled,
BorTx: newBoolPtr(false),
}
}
if config.BorTraceEnabled == nil {
config.BorTraceEnabled = defaultBorTraceEnabled
}
if block.NumberU64() == 0 {
return nil, errors.New("genesis is not traceable")
}
@ -609,9 +735,9 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
// Execute all the transaction contained within the block concurrently
var (
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
txs = block.Transactions()
results = make([]*txTraceResult, len(txs))
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
txs, stateSyncPresent = api.getAllBlockTransactions(ctx, block)
results = make([]*txTraceResult, len(txs))
pend = new(sync.WaitGroup)
jobs = make(chan *txTraceTask, len(txs))
@ -634,7 +760,21 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
TxIndex: task.index,
TxHash: txs[task.index].Hash(),
}
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
var res interface{}
var err error
if stateSyncPresent && task.index == len(txs)-1 {
if *config.BorTraceEnabled {
config.BorTx = newBoolPtr(true)
res, err = api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
} else {
break
}
} else {
res, err = api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
}
if err != nil {
results[task.index] = &txTraceResult{Error: err.Error()}
continue
@ -674,16 +814,31 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
// Generate the next state snapshot fast without tracing
msg, _ := tx.AsMessage(signer, block.BaseFee())
statedb.Prepare(tx.Hash(), i)
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
// nolint: nestif
if !ioflag {
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
failed = err
break
//nolint: nestif
if stateSyncPresent && i == len(txs)-1 {
if *config.BorTraceEnabled {
callmsg := prepareCallMessage(msg)
if _, err := statefull.ApplyBorMessage(*vmenv, callmsg); err != nil {
failed = err
break
}
} else {
break
}
} else {
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
failed = err
break
}
// Finalize the state so any modifications are written to the trie
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
}
// Finalize the state so any modifications are written to the trie
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
} else {
coinbaseBalance := statedb.GetBalance(blockCtx.Coinbase)
@ -749,16 +904,30 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
if failed != nil {
return nil, failed
}
return results, nil
if !*config.BorTraceEnabled && stateSyncPresent {
return results[:len(results)-1], nil
} else {
return results, nil
}
}
// standardTraceBlockToFile configures a new tracer which uses standard JSON output,
// and traces either a full block or an individual transaction. The return value will
// be one filename per transaction traced.
func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block, config *StdTraceConfig) ([]string, error) {
if config == nil {
config = &StdTraceConfig{
BorTraceEnabled: defaultBorTraceEnabled,
}
}
if config.BorTraceEnabled == nil {
config.BorTraceEnabled = defaultBorTraceEnabled
}
// If we're tracing a single transaction, make sure it's present
if config != nil && config.TxHash != (common.Hash{}) {
if !containsTx(block, config.TxHash) {
if !api.containsTx(ctx, block, config.TxHash) {
return nil, fmt.Errorf("transaction %#x not found in block", config.TxHash)
}
}
@ -813,7 +982,14 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
canon = false
}
}
for i, tx := range block.Transactions() {
txs, stateSyncPresent := api.getAllBlockTransactions(ctx, block)
if !*config.BorTraceEnabled && stateSyncPresent {
txs = txs[:len(txs)-1]
stateSyncPresent = false
}
for i, tx := range txs {
// Prepare the trasaction for un-traced execution
var (
msg, _ = tx.AsMessage(signer, block.BaseFee())
@ -847,10 +1023,23 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
// Execute the transaction and flush any traces to disk
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf)
statedb.Prepare(tx.Hash(), i)
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
if writer != nil {
writer.Flush()
//nolint: nestif
if stateSyncPresent && i == len(txs)-1 {
if *config.BorTraceEnabled {
callmsg := prepareCallMessage(msg)
_, err = statefull.ApplyBorMessage(*vmenv, callmsg)
if writer != nil {
writer.Flush()
}
}
} else {
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
if writer != nil {
writer.Flush()
}
}
if dump != nil {
dump.Close()
log.Info("Wrote standard trace", "file", dump.Name())
@ -872,8 +1061,9 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
// containsTx reports whether the transaction with a certain hash
// is contained within the specified block.
func containsTx(block *types.Block, hash common.Hash) bool {
for _, tx := range block.Transactions() {
func (api *API) containsTx(ctx context.Context, block *types.Block, hash common.Hash) bool {
txs, _ := api.getAllBlockTransactions(ctx, block)
for _, tx := range txs {
if tx.Hash() == hash {
return true
}
@ -884,6 +1074,17 @@ func containsTx(block *types.Block, hash common.Hash) bool {
// TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object.
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
if config == nil {
config = &TraceConfig{
BorTraceEnabled: defaultBorTraceEnabled,
BorTx: newBoolPtr(false),
}
}
if config.BorTraceEnabled == nil {
config.BorTraceEnabled = defaultBorTraceEnabled
}
tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
if tx == nil {
// For BorTransaction, there will be no trace available
@ -919,6 +1120,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
TxIndex: int(index),
TxHash: hash,
}
return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
}
@ -973,6 +1175,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
Reexec: config.Reexec,
}
}
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
}
@ -980,6 +1183,18 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
// executes the given message in the provided environment. The return value will
// be tracer dependent.
func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
if config == nil {
config = &TraceConfig{
BorTraceEnabled: defaultBorTraceEnabled,
BorTx: newBoolPtr(false),
}
}
if config.BorTraceEnabled == nil {
config.BorTraceEnabled = defaultBorTraceEnabled
}
// Assemble the structured logger or the JavaScript tracer
var (
tracer vm.EVMLogger
@ -1019,9 +1234,22 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
// Call Prepare to clear out the statedb access list
statedb.Prepare(txctx.TxHash, txctx.TxIndex)
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
var result *core.ExecutionResult
if config.BorTx == nil {
config.BorTx = newBoolPtr(false)
}
if *config.BorTx {
callmsg := prepareCallMessage(message)
if result, err = statefull.ApplyBorMessage(*vmenv, callmsg); err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}
} else {
result, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}
}
// Depending on the tracer type, format and return the output.

View file

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -68,14 +69,14 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
// Execute all the transaction contained within the block concurrently
var (
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
txs = block.Transactions()
deleteEmptyObjects = api.backend.ChainConfig().IsEIP158(block.Number())
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
txs, stateSyncPresent = api.getAllBlockTransactions(ctx, block)
deleteEmptyObjects = api.backend.ChainConfig().IsEIP158(block.Number())
)
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
traceTxn := func(indx int, tx *types.Transaction) *TxTraceResult {
traceTxn := func(indx int, tx *types.Transaction, borTx bool) *TxTraceResult {
message, _ := tx.AsMessage(signer, block.BaseFee())
txContext := core.NewEVMTxContext(message)
@ -88,7 +89,15 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
// Not sure if we need to do this
statedb.Prepare(tx.Hash(), indx)
execRes, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
var execRes *core.ExecutionResult
if borTx {
callmsg := prepareCallMessage(message)
execRes, err = statefull.ApplyBorMessage(*vmenv, callmsg)
} else {
execRes, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
}
if err != nil {
return &TxTraceResult{
Error: err.Error(),
@ -115,7 +124,11 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
}
for indx, tx := range txs {
res.Transactions = append(res.Transactions, traceTxn(indx, tx))
if stateSyncPresent && indx == len(txs)-1 {
res.Transactions = append(res.Transactions, traceTxn(indx, tx, true))
} else {
res.Transactions = append(res.Transactions, traceTxn(indx, tx, false))
}
}
return res, nil

View file

@ -176,6 +176,11 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}
func (b *testBackend) GetBorBlockTransactionWithBlockHash(ctx context.Context, txHash common.Hash, blockHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
tx, blockHash, blockNumber, index := rawdb.ReadBorTransactionWithBlockHash(b.ChainDb(), txHash, blockHash)
return tx, blockHash, blockNumber, index, nil
}
func TestTraceCall(t *testing.T) {
t.Parallel()

6
go.mod
View file

@ -5,7 +5,6 @@ go 1.19
require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0
github.com/BurntSushi/toml v1.1.0
github.com/JekaMas/crand v1.0.1
github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d
github.com/VictoriaMetrics/fastcache v1.6.0
github.com/aws/aws-sdk-go-v2 v1.2.0
@ -48,6 +47,7 @@ require (
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
github.com/julienschmidt/httprouter v1.3.0
github.com/karalabe/usb v0.0.2
github.com/maticnetwork/crand v1.0.2
github.com/maticnetwork/polyproto v0.0.2
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.12
@ -127,7 +127,7 @@ require (
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml v1.9.5
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posener/complete v1.1.1 // indirect
@ -135,7 +135,7 @@ require (
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/zclconf/go-cty v1.8.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.2.0 // indirect
go.opentelemetry.io/otel/trace v1.2.0 // indirect
go.opentelemetry.io/otel/trace v1.2.0
go.opentelemetry.io/proto/otlp v0.10.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect

4
go.sum
View file

@ -29,8 +29,6 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/JekaMas/crand v1.0.1 h1:FMPxkUQqH/hExl0aUXsr0UCGYZ4lJH9IJ5H/KbM6Y9A=
github.com/JekaMas/crand v1.0.1/go.mod h1:GGzGpMCht/tbaNQ5A4kSiKSqEoNAhhyTfSDQyIENBQU=
github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d h1:RO27lgfZF8s9lZ3pWyzc0gCE0RZC+6/PXbRjAa0CNp8=
github.com/JekaMas/go-grpc-net-conn v0.0.0-20220708155319-6aff21f2d13d/go.mod h1:romz7UPgSYhfJkKOalzEEyV6sWtt/eAEm0nX2aOrod0=
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
@ -350,6 +348,8 @@ github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/maticnetwork/crand v1.0.2 h1:Af0tAivC8zrxXDpGWNWVT/0s1fOz8w0eRbahZgURS8I=
github.com/maticnetwork/crand v1.0.2/go.mod h1:/NRNL3bj2eYdqpWmoIP5puxndTpi0XRxpj5ZKxfHjyg=
github.com/maticnetwork/polyproto v0.0.2 h1:cPxuxbIDItdwGnucc3lZB58U8Zfe1mH73PWTGd15554=
github.com/maticnetwork/polyproto v0.0.2/go.mod h1:e1mU2EXSwEpn5jM7GfNwu3AupsV6WAGoPFFfswXOF0o=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=

View file

@ -3,33 +3,42 @@ set -e
balanceInit=$(docker exec bor0 bash -c "bor attach /root/.bor/data/bor.ipc -exec 'Math.round(web3.fromWei(eth.getBalance(eth.accounts[0])))'")
delay=600
stateSyncFound="false"
checkpointFound="false"
SECONDS=0
start_time=$SECONDS
echo "Wait ${delay} seconds for state-sync..."
sleep $delay
while true
do
balance=$(docker exec bor0 bash -c "bor attach /root/.bor/data/bor.ipc -exec 'Math.round(web3.fromWei(eth.getBalance(eth.accounts[0])))'")
if ! [[ "$balance" =~ ^[0-9]+$ ]]; then
echo "Something is wrong! Can't find the balance of first account in bor network."
exit 1
fi
balance=$(docker exec bor0 bash -c "bor attach /root/.bor/data/bor.ipc -exec 'Math.round(web3.fromWei(eth.getBalance(eth.accounts[0])))'")
if (( $balance > $balanceInit )); then
if [ $stateSyncFound != "true" ]; then
stateSyncTime=$(( SECONDS - start_time ))
stateSyncFound="true"
fi
fi
if ! [[ "$balance" =~ ^[0-9]+$ ]]; then
echo "Something is wrong! Can't find the balance of first account in bor network."
exit 1
fi
checkpointID=$(curl -sL http://localhost:1317/checkpoints/latest | jq .result.id)
echo "Found matic balance on account[0]: " $balance
if [ $checkpointID != "null" ]; then
if [ $checkpointFound != "true" ]; then
checkpointTime=$(( SECONDS - start_time ))
checkpointFound="true"
fi
fi
if (( $balance <= $balanceInit )); then
echo "Balance in bor network has not increased. This indicates that something is wrong with state sync."
exit 1
fi
if [ $stateSyncFound == "true" ] && [ $checkpointFound == "true" ]; then
break
fi
checkpointID=$(curl -sL http://localhost:1317/checkpoints/latest | jq .result.id)
if [ $checkpointID == "null" ]; then
echo "Something is wrong! Could not find any checkpoint."
exit 1
else
echo "Found checkpoint ID:" $checkpointID
fi
echo "All tests have passed!"
done
echo "Both state sync and checkpoint went through. All tests have passed!"
echo "Time taken for state sync: $(printf '%02dm:%02ds\n' $(($stateSyncTime%3600/60)) $(($stateSyncTime%60)))"
echo "Time taken for checkpoint: $(printf '%02dm:%02ds\n' $(($checkpointTime%3600/60)) $(($checkpointTime%60)))"

View file

@ -29,12 +29,16 @@ var mainnetBor = &Chain{
BerlinBlock: big.NewInt(14750000),
LondonBlock: big.NewInt(23850000),
Bor: &params.BorConfig{
JaipurBlock: 23850000,
JaipurBlock: big.NewInt(23850000),
Period: map[string]uint64{
"0": 2,
},
ProducerDelay: 6,
Sprint: 64,
ProducerDelay: map[string]uint64{
"0": 6,
},
Sprint: map[string]uint64{
"0": 64,
},
BackupMultiplier: map[string]uint64{
"0": 2,
},

View file

@ -29,13 +29,17 @@ var mumbaiTestnet = &Chain{
BerlinBlock: big.NewInt(13996000),
LondonBlock: big.NewInt(22640000),
Bor: &params.BorConfig{
JaipurBlock: 22770000,
JaipurBlock: big.NewInt(22770000),
Period: map[string]uint64{
"0": 2,
"25275000": 5,
},
ProducerDelay: 6,
Sprint: 64,
ProducerDelay: map[string]uint64{
"0": 6,
},
Sprint: map[string]uint64{
"0": 64,
},
BackupMultiplier: map[string]uint64{
"0": 2,
"25275000": 5,

File diff suppressed because one or more lines are too long

View file

@ -18,12 +18,18 @@
"londonBlock":13996000,
"bor":{
"period":{
"0":2
"0":2,
"25275000": 5
},
"producerDelay":{
"0": 6
},
"sprint":{
"0": 64
},
"producerDelay":6,
"sprint":64,
"backupMultiplier":{
"0":2
"0": 2,
"25275000": 5
},
"validatorContract":"0x0000000000000000000000000000000000001000",
"stateReceiverContract":"0x0000000000000000000000000000000000001001",

View file

@ -62,9 +62,12 @@ type Config struct {
// GcMode selects the garbage collection mode for the trie
GcMode string `hcl:"gcmode,optional" toml:"gcmode,optional"`
// Snapshot disables/enables the snapshot database mode
// Snapshot enables the snapshot database mode
Snapshot bool `hcl:"snapshot,optional" toml:"snapshot,optional"`
// BorLogs enables bor log retrieval
BorLogs bool `hcl:"bor.logs,optional" toml:"bor.logs,optional"`
// Ethstats is the address of the ethstats server to send telemetry
Ethstats string `hcl:"ethstats,optional" toml:"ethstats,optional"`
@ -263,6 +266,9 @@ type APIConfig struct {
// Cors is the list of Cors endpoints
Cors []string `hcl:"corsdomain,optional" toml:"corsdomain,optional"`
// Origins is the list of endpoints to accept requests from (only consumed for websockets)
Origins []string `hcl:"origins,optional" toml:"origins,optional"`
}
type GpoConfig struct {
@ -361,6 +367,9 @@ type CacheConfig struct {
// TxLookupLimit sets the maximum number of blocks from head whose tx indices are reserved.
TxLookupLimit uint64 `hcl:"txlookuplimit,optional" toml:"txlookuplimit,optional"`
// Number of block states to keep in memory (default = 128)
TriesInMemory uint64 `hcl:"triesinmemory,optional" toml:"triesinmemory,optional"`
}
type AccountsConfig struct {
@ -396,7 +405,7 @@ func DefaultConfig() *Config {
LogLevel: "INFO",
DataDir: DefaultDataDir(),
P2P: &P2PConfig{
MaxPeers: 30,
MaxPeers: 50,
MaxPendPeers: 50,
Bind: "0.0.0.0",
Port: 30303,
@ -420,6 +429,7 @@ func DefaultConfig() *Config {
SyncMode: "full",
GcMode: "full",
Snapshot: true,
BorLogs: false,
TxPool: &TxPoolConfig{
Locals: []string{},
NoLocals: false,
@ -466,8 +476,7 @@ func DefaultConfig() *Config {
Prefix: "",
Host: "localhost",
API: []string{"net", "web3"},
Cors: []string{"localhost"},
VHost: []string{"localhost"},
Origins: []string{"localhost"},
},
Graphql: &APIConfig{
Enabled: false,
@ -505,6 +514,7 @@ func DefaultConfig() *Config {
NoPrefetch: false,
Preimages: false,
TxLookupLimit: 2350000,
TriesInMemory: 128,
},
Accounts: &AccountsConfig{
Unlock: []string{},
@ -649,6 +659,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
n.NetworkId = c.chain.NetworkId
n.Genesis = c.chain.Genesis
}
n.HeimdallURL = c.Heimdall.URL
n.WithoutHeimdall = c.Heimdall.Without
n.HeimdallgRPCAddress = c.Heimdall.GRPCAddress
@ -881,6 +892,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
}
}
n.BorLogs = c.BorLogs
n.DatabaseHandles = dbHandles
return &n, nil
@ -920,7 +932,7 @@ func (c *Config) buildNode() (*node.Config, error) {
HTTPVirtualHosts: c.JsonRPC.Http.VHost,
HTTPPathPrefix: c.JsonRPC.Http.Prefix,
WSModules: c.JsonRPC.Ws.API,
WSOrigins: c.JsonRPC.Ws.Cors,
WSOrigins: c.JsonRPC.Ws.Origins,
WSPathPrefix: c.JsonRPC.Ws.Prefix,
GraphQLCors: c.JsonRPC.Graphql.Cors,
GraphQLVirtualHosts: c.JsonRPC.Graphql.VHost,

View file

@ -45,7 +45,7 @@ func (c *Command) Flags() *flagset.Flagset {
})
f.StringFlag(&flagset.StringFlag{
Name: "syncmode",
Usage: `Blockchain sync mode ("fast", "full", or "snap")`,
Usage: `Blockchain sync mode (only "full" sync supported)`,
Value: &c.cliConfig.SyncMode,
Default: c.cliConfig.SyncMode,
})
@ -62,10 +62,16 @@ func (c *Command) Flags() *flagset.Flagset {
})
f.BoolFlag(&flagset.BoolFlag{
Name: "snapshot",
Usage: `Disables/Enables the snapshot-database mode (default = true)`,
Usage: `Enables the snapshot-database mode (default = true)`,
Value: &c.cliConfig.Snapshot,
Default: c.cliConfig.Snapshot,
})
f.BoolFlag(&flagset.BoolFlag{
Name: "bor.logs",
Usage: `Enables bor log retrieval (default = false)`,
Value: &c.cliConfig.BorLogs,
Default: c.cliConfig.BorLogs,
})
// heimdall
f.StringFlag(&flagset.StringFlag{
@ -298,6 +304,13 @@ func (c *Command) Flags() *flagset.Flagset {
Default: c.cliConfig.Cache.Preimages,
Group: "Cache",
})
f.Uint64Flag(&flagset.Uint64Flag{
Name: "cache.triesinmemory",
Usage: "Number of block states (tries) to keep in memory (default = 128)",
Value: &c.cliConfig.Cache.TriesInMemory,
Default: c.cliConfig.Cache.TriesInMemory,
Group: "Cache",
})
f.Uint64Flag(&flagset.Uint64Flag{
Name: "txlookuplimit",
Usage: "Number of recent blocks to maintain transactions index for (default = about 56 days, 0 = entire chain)",
@ -350,17 +363,10 @@ func (c *Command) Flags() *flagset.Flagset {
Group: "JsonRPC",
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "ws.corsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: &c.cliConfig.JsonRPC.Ws.Cors,
Default: c.cliConfig.JsonRPC.Ws.Cors,
Group: "JsonRPC",
})
f.SliceStringFlag(&flagset.SliceStringFlag{
Name: "ws.vhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: &c.cliConfig.JsonRPC.Ws.VHost,
Default: c.cliConfig.JsonRPC.Ws.VHost,
Name: "ws.origins",
Usage: "Origins from which to accept websockets requests",
Value: &c.cliConfig.JsonRPC.Ws.Origins,
Default: c.cliConfig.JsonRPC.Ws.Origins,
Group: "JsonRPC",
})
f.SliceStringFlag(&flagset.SliceStringFlag{

View file

@ -36,6 +36,10 @@ import (
"github.com/ethereum/go-ethereum/metrics/influxdb"
"github.com/ethereum/go-ethereum/metrics/prometheus"
"github.com/ethereum/go-ethereum/node"
// Force-load the tracer engines to trigger registration
_ "github.com/ethereum/go-ethereum/eth/tracers/js"
_ "github.com/ethereum/go-ethereum/eth/tracers/native"
)
type Server struct {
@ -253,6 +257,12 @@ func (s *Server) Stop() {
}
func (s *Server) setupMetrics(config *TelemetryConfig, serviceName string) error {
// Check the global metrics if they're matching with the provided config
if metrics.Enabled != config.Enabled || metrics.EnabledExpensive != config.Expensive {
log.Warn("Metric misconfiguration, some of them might not be visible")
}
// Update the values anyways (for services which don't need immediate attention)
metrics.Enabled = config.Enabled
metrics.EnabledExpensive = config.Expensive
@ -263,6 +273,10 @@ func (s *Server) setupMetrics(config *TelemetryConfig, serviceName string) error
log.Info("Enabling metrics collection")
if metrics.EnabledExpensive {
log.Info("Enabling expensive metrics collection")
}
// influxdb
if v1Enabled, v2Enabled := config.InfluxDB.V1Enabled, config.InfluxDB.V2Enabled; v1Enabled || v2Enabled {
if v1Enabled && v2Enabled {

View file

@ -316,7 +316,7 @@ func TestGetStaleCodeLes4(t *testing.T) { testGetStaleCode(t, 4) }
func testGetStaleCode(t *testing.T, protocol int) {
netconfig := testnetConfig{
blocks: core.TriesInMemory + 4,
blocks: int(core.DefaultCacheConfig.TriesInMemory) + 4,
protocol: protocol,
nopruning: true,
}
@ -430,7 +430,7 @@ func TestGetStaleProofLes4(t *testing.T) { testGetStaleProof(t, 4) }
func testGetStaleProof(t *testing.T, protocol int) {
netconfig := testnetConfig{
blocks: core.TriesInMemory + 4,
blocks: int(core.DefaultCacheConfig.TriesInMemory) + 4,
protocol: protocol,
nopruning: true,
}

View file

@ -1058,7 +1058,7 @@ func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, ge
// If local ethereum node is running in archive mode, advertise ourselves we have
// all version state data. Otherwise only recent state is available.
stateRecent := uint64(core.TriesInMemory - blockSafetyMargin)
stateRecent := core.DefaultCacheConfig.TriesInMemory - blockSafetyMargin
if server.archiveMode {
stateRecent = 0
}

View file

@ -297,7 +297,7 @@ func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) {
// Refuse to search stale state data in the database since looking for
// a non-exist key is kind of expensive.
local := bc.CurrentHeader().Number.Uint64()
if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local {
if !backend.ArchiveMode() && header.Number.Uint64()+core.DefaultCacheConfig.TriesInMemory <= local {
p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local)
p.bumpInvalid()
continue
@ -396,7 +396,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
// Refuse to search stale state data in the database since looking for
// a non-exist key is kind of expensive.
local := bc.CurrentHeader().Number.Uint64()
if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local {
if !backend.ArchiveMode() && header.Number.Uint64()+core.DefaultCacheConfig.TriesInMemory <= local {
p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local)
p.bumpInvalid()
continue

View file

@ -377,7 +377,7 @@ func (p *testPeer) handshakeWithClient(t *testing.T, td *big.Int, head common.Ha
sendList = sendList.add("serveHeaders", nil)
sendList = sendList.add("serveChainSince", uint64(0))
sendList = sendList.add("serveStateSince", uint64(0))
sendList = sendList.add("serveRecentState", uint64(core.TriesInMemory-4))
sendList = sendList.add("serveRecentState", core.DefaultCacheConfig.TriesInMemory-4)
sendList = sendList.add("txRelay", nil)
sendList = sendList.add("flowControl/BL", testBufLimit)
sendList = sendList.add("flowControl/MRR", testBufRecharge)

View file

@ -11,7 +11,7 @@ import (
"strings"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/BurntSushi/toml"
)
// Enabled is checked by the constructor functions for all of the
@ -32,26 +32,71 @@ var enablerFlags = []string{"metrics"}
// expensiveEnablerFlags is the CLI flag names to use to enable metrics collections.
var expensiveEnablerFlags = []string{"metrics.expensive"}
// configFlag is the CLI flag name to use to start node by providing a toml based config
var configFlag = "config"
// Init enables or disables the metrics system. Since we need this to run before
// any other code gets to create meters and timers, we'll actually do an ugly hack
// and peek into the command line args for the metrics flag.
func init() {
for _, arg := range os.Args {
var configFile string
for i := 0; i < len(os.Args); i++ {
arg := os.Args[i]
flag := strings.TrimLeft(arg, "-")
// check for existence of `config` flag
if flag == configFlag && i < len(os.Args)-1 {
configFile = strings.TrimLeft(os.Args[i+1], "-") // find the value of flag
}
for _, enabler := range enablerFlags {
if !Enabled && flag == enabler {
log.Info("Enabling metrics collection")
Enabled = true
}
}
for _, enabler := range expensiveEnablerFlags {
if !EnabledExpensive && flag == enabler {
log.Info("Enabling expensive metrics collection")
EnabledExpensive = true
}
}
}
// Update the global metrics value, if they're provided in the config file
updateMetricsFromConfig(configFile)
}
func updateMetricsFromConfig(path string) {
// Don't act upon any errors here. They're already taken into
// consideration when the toml config file will be parsed in the cli.
data, err := os.ReadFile(path)
tomlData := string(data)
if err != nil {
return
}
// Create a minimal config to decode
type TelemetryConfig struct {
Enabled bool `hcl:"metrics,optional" toml:"metrics,optional"`
Expensive bool `hcl:"expensive,optional" toml:"expensive,optional"`
}
type CliConfig struct {
Telemetry *TelemetryConfig `hcl:"telemetry,block" toml:"telemetry,block"`
}
conf := &CliConfig{}
if _, err := toml.Decode(tomlData, &conf); err != nil || conf == nil {
return
}
// We have the values now, update them
Enabled = conf.Telemetry.Enabled
EnabledExpensive = conf.Telemetry.Expensive
}
// CollectProcessMetrics periodically collects various metrics about the running

View file

@ -138,6 +138,9 @@ func NewDBForFakes(t TensingObject) (ethdb.Database, *core.Genesis, *params.Chai
chainConfig.Bor.Period = map[string]uint64{
"0": 1,
}
chainConfig.Bor.Sprint = map[string]uint64{
"0": 64,
}
return chainDB, genesis, chainConfig
}

View file

@ -17,6 +17,7 @@
package miner
import (
"context"
"errors"
"fmt"
"math/big"
@ -25,8 +26,12 @@ import (
"time"
mapset "github.com/deckarep/golang-set"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/tracing"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core"
@ -144,6 +149,8 @@ func (env *environment) discard() {
// task contains all information for consensus engine sealing and result submitting.
type task struct {
//nolint:containedctx
ctx context.Context
receipts []*types.Receipt
state *state.StateDB
block *types.Block
@ -158,6 +165,8 @@ const (
// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier.
type newWorkReq struct {
//nolint:containedctx
ctx context.Context
interrupt *int32
noempty bool
timestamp int64
@ -165,6 +174,8 @@ type newWorkReq struct {
// getWorkReq represents a request for getting a new sealing work with provided parameters.
type getWorkReq struct {
//nolint:containedctx
ctx context.Context
params *generateParams
err error
result chan *types.Block
@ -287,9 +298,12 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
recommit = minRecommitInterval
}
ctx := tracing.WithTracer(context.Background(), otel.GetTracerProvider().Tracer("MinerWorker"))
worker.wg.Add(4)
go worker.mainLoop()
go worker.newWorkLoop(recommit)
go worker.mainLoop(ctx)
go worker.newWorkLoop(ctx, recommit)
go worker.resultLoop()
go worker.taskLoop()
@ -419,7 +433,9 @@ func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) t
}
// newWorkLoop is a standalone goroutine to submit new sealing work upon received events.
func (w *worker) newWorkLoop(recommit time.Duration) {
//
//nolint:gocognit
func (w *worker) newWorkLoop(ctx context.Context, recommit time.Duration) {
defer w.wg.Done()
var (
interrupt *int32
@ -433,12 +449,16 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
// commit aborts in-flight transaction execution with given signal and resubmits a new one.
commit := func(noempty bool, s int32) {
// we close spans only by the place we created them
ctx, span := tracing.Trace(ctx, "worker.newWorkLoop.commit")
tracing.EndSpan(span)
if interrupt != nil {
atomic.StoreInt32(interrupt, s)
}
interrupt = new(int32)
select {
case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}:
case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp, ctx: ctx}:
case <-w.exitCh:
return
}
@ -447,6 +467,9 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
}
// clearPending cleans the stale pending tasks.
clearPending := func(number uint64) {
_, span := tracing.Trace(ctx, "worker.newWorkLoop.clearPending")
tracing.EndSpan(span)
w.pendingMu.Lock()
for h, t := range w.pendingTasks {
if t.block.NumberU64()+staleThreshold <= number {
@ -519,7 +542,8 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
// mainLoop is responsible for generating and submitting sealing work based on
// the received event. It can support two modes: automatically generate task and
// submit it or return task according to given parameters for various proposes.
func (w *worker) mainLoop() {
// nolint: gocognit
func (w *worker) mainLoop(ctx context.Context) {
defer w.wg.Done()
defer w.txsSub.Unsubscribe()
defer w.chainHeadSub.Unsubscribe()
@ -536,10 +560,10 @@ func (w *worker) mainLoop() {
for {
select {
case req := <-w.newWorkCh:
w.commitWork(req.interrupt, req.noempty, req.timestamp)
w.commitWork(req.ctx, req.interrupt, req.noempty, req.timestamp)
case req := <-w.getWorkCh:
block, err := w.generateWork(req.params)
block, err := w.generateWork(req.ctx, req.params)
if err != nil {
req.err = err
req.result <- nil
@ -567,7 +591,10 @@ func (w *worker) mainLoop() {
if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 {
start := time.Now()
if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
w.commit(w.current.copy(), nil, true, start)
commitErr := w.commit(ctx, w.current.copy(), nil, true, start)
if commitErr != nil {
log.Error("error while committing work for mining", "err", commitErr)
}
}
}
@ -614,7 +641,7 @@ func (w *worker) mainLoop() {
// submit sealing work here since all empty submission will be rejected
// by clique. Of course the advance sealing(empty submission) is disabled.
if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 {
w.commitWork(nil, true, time.Now().Unix())
w.commitWork(ctx, nil, true, time.Now().Unix())
}
}
atomic.AddInt32(&w.newTxs, int32(len(ev.Txs)))
@ -670,7 +697,7 @@ func (w *worker) taskLoop() {
w.pendingTasks[sealHash] = task
w.pendingMu.Unlock()
if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil {
if err := w.engine.Seal(task.ctx, w.chain, task.block, w.resultCh, stopCh); err != nil {
log.Warn("Block sealing failed", "err", err)
w.pendingMu.Lock()
delete(w.pendingTasks, sealHash)
@ -694,10 +721,12 @@ func (w *worker) resultLoop() {
if block == nil {
continue
}
// Short circuit when receiving duplicate result caused by resubmitting.
if w.chain.HasBlock(block.Hash(), block.NumberU64()) {
continue
}
oldBlock := w.chain.GetBlockByNumber(block.NumberU64())
if oldBlock != nil {
oldBlockAuthor, _ := w.chain.Engine().Author(oldBlock.Header())
@ -707,49 +736,72 @@ func (w *worker) resultLoop() {
continue
}
}
var (
sealhash = w.engine.SealHash(block.Header())
hash = block.Hash()
)
w.pendingMu.RLock()
task, exist := w.pendingTasks[sealhash]
w.pendingMu.RUnlock()
if !exist {
log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash)
continue
}
// Different block could share same sealhash, deep copy here to prevent write-write conflict.
var (
receipts = make([]*types.Receipt, len(task.receipts))
logs []*types.Log
err error
)
for i, taskReceipt := range task.receipts {
receipt := new(types.Receipt)
receipts[i] = receipt
*receipt = *taskReceipt
// add block location fields
receipt.BlockHash = hash
receipt.BlockNumber = block.Number()
receipt.TransactionIndex = uint(i)
tracing.Exec(task.ctx, "resultLoop", func(ctx context.Context, span trace.Span) {
for i, taskReceipt := range task.receipts {
receipt := new(types.Receipt)
receipts[i] = receipt
*receipt = *taskReceipt
// Update the block hash in all logs since it is now available and not when the
// receipt/log of individual transactions were created.
receipt.Logs = make([]*types.Log, len(taskReceipt.Logs))
for i, taskLog := range taskReceipt.Logs {
log := new(types.Log)
receipt.Logs[i] = log
*log = *taskLog
log.BlockHash = hash
// add block location fields
receipt.BlockHash = hash
receipt.BlockNumber = block.Number()
receipt.TransactionIndex = uint(i)
// Update the block hash in all logs since it is now available and not when the
// receipt/log of individual transactions were created.
receipt.Logs = make([]*types.Log, len(taskReceipt.Logs))
for i, taskLog := range taskReceipt.Logs {
log := new(types.Log)
receipt.Logs[i] = log
*log = *taskLog
log.BlockHash = hash
}
logs = append(logs, receipt.Logs...)
}
logs = append(logs, receipt.Logs...)
}
// Commit block and state to database.
_, err := w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true)
// Commit block and state to database.
tracing.ElapsedTime(ctx, span, "WriteBlockAndSetHead time taken", func(_ context.Context, _ trace.Span) {
_, err = w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true)
})
tracing.SetAttributes(
span,
attribute.String("hash", hash.String()),
attribute.Int("number", int(block.Number().Uint64())),
attribute.Int("txns", block.Transactions().Len()),
attribute.Int("gas used", int(block.GasUsed())),
attribute.Int("elapsed", int(time.Since(task.createdAt).Milliseconds())),
attribute.Bool("error", err != nil),
)
})
if err != nil {
log.Error("Failed writing block to chain", "err", err)
continue
}
log.Info("Successfully sealed new block", "number", block.Number(), "sealhash", sealhash, "hash", hash,
"elapsed", common.PrettyDuration(time.Since(task.createdAt)))
@ -758,7 +810,6 @@ func (w *worker) resultLoop() {
// Insert the block into the set of pending ones to resultLoop for confirmations
w.unconfirmed.Insert(block.NumberU64(), block.Hash())
case <-w.exitCh:
return
}
@ -1069,78 +1120,176 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
// fillTransactions retrieves the pending transactions from the txpool and fills them
// into the given sealing block. The transaction selection and ordering strategy can
// be customized with the plugin in the future.
func (w *worker) fillTransactions(interrupt *int32, env *environment) {
func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *environment) {
ctx, span := tracing.StartSpan(ctx, "fillTransactions")
defer tracing.EndSpan(span)
// Split the pending transactions into locals and remotes
// Fill the block with all available pending transactions.
pending := w.eth.TxPool().Pending(true)
localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending
for _, account := range w.eth.TxPool().Locals() {
if txs := remoteTxs[account]; len(txs) > 0 {
delete(remoteTxs, account)
localTxs[account] = txs
var (
localTxsCount int
remoteTxsCount int
localTxs = make(map[common.Address]types.Transactions)
remoteTxs map[common.Address]types.Transactions
)
tracing.Exec(ctx, "worker.SplittingTransactions", func(ctx context.Context, span trace.Span) {
pending := w.eth.TxPool().Pending(true)
remoteTxs = pending
for _, account := range w.eth.TxPool().Locals() {
if txs := remoteTxs[account]; len(txs) > 0 {
delete(remoteTxs, account)
localTxs[account] = txs
}
}
}
if len(localTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
if w.commitTransactions(env, txs, interrupt) {
localTxsCount = len(localTxs)
remoteTxsCount = len(remoteTxs)
tracing.SetAttributes(
span,
attribute.Int("len of local txs", localTxsCount),
attribute.Int("len of remote txs", remoteTxsCount),
)
})
var (
localEnvTCount int
remoteEnvTCount int
committed bool
)
if localTxsCount > 0 {
var txs *types.TransactionsByPriceAndNonce
tracing.Exec(ctx, "worker.LocalTransactionsByPriceAndNonce", func(ctx context.Context, span trace.Span) {
txs = types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
tracing.SetAttributes(
span,
attribute.Int("len of tx local Heads", txs.GetTxs()),
)
})
tracing.Exec(ctx, "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
committed = w.commitTransactions(env, txs, interrupt)
})
if committed {
return
}
localEnvTCount = env.tcount
}
if len(remoteTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee)
if w.commitTransactions(env, txs, interrupt) {
if remoteTxsCount > 0 {
var txs *types.TransactionsByPriceAndNonce
tracing.Exec(ctx, "worker.RemoteTransactionsByPriceAndNonce", func(ctx context.Context, span trace.Span) {
txs = types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee)
tracing.SetAttributes(
span,
attribute.Int("len of tx remote Heads", txs.GetTxs()),
)
})
tracing.Exec(ctx, "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
committed = w.commitTransactions(env, txs, interrupt)
})
if committed {
return
}
remoteEnvTCount = env.tcount
}
tracing.SetAttributes(
span,
attribute.Int("len of final local txs ", localEnvTCount),
attribute.Int("len of final remote txs", remoteEnvTCount),
)
}
// generateWork generates a sealing block based on the given parameters.
func (w *worker) generateWork(params *generateParams) (*types.Block, error) {
func (w *worker) generateWork(ctx context.Context, params *generateParams) (*types.Block, error) {
work, err := w.prepareWork(params)
if err != nil {
return nil, err
}
defer work.discard()
w.fillTransactions(nil, work)
return w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts)
w.fillTransactions(ctx, nil, work)
return w.engine.FinalizeAndAssemble(ctx, w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts)
}
// commitWork generates several new sealing tasks based on the parent block
// and submit them to the sealer.
func (w *worker) commitWork(interrupt *int32, noempty bool, timestamp int64) {
func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool, timestamp int64) {
start := time.Now()
// Set the coinbase if the worker is running or it's required
var coinbase common.Address
if w.isRunning() {
if w.coinbase == (common.Address{}) {
log.Error("Refusing to mine without etherbase")
return
var (
work *environment
err error
)
tracing.Exec(ctx, "worker.prepareWork", func(ctx context.Context, span trace.Span) {
// Set the coinbase if the worker is running or it's required
var coinbase common.Address
if w.isRunning() {
if w.coinbase == (common.Address{}) {
log.Error("Refusing to mine without etherbase")
return
}
coinbase = w.coinbase // Use the preset address as the fee recipient
}
coinbase = w.coinbase // Use the preset address as the fee recipient
}
work, err := w.prepareWork(&generateParams{
timestamp: uint64(timestamp),
coinbase: coinbase,
work, err = w.prepareWork(&generateParams{
timestamp: uint64(timestamp),
coinbase: coinbase,
})
})
if err != nil {
return
}
ctx, span := tracing.StartSpan(ctx, "commitWork")
defer tracing.EndSpan(span)
tracing.SetAttributes(
span,
attribute.Int("number", int(work.header.Number.Uint64())),
)
// Create an empty block based on temporary copied state for
// sealing in advance without waiting block execution finished.
if !noempty && atomic.LoadUint32(&w.noempty) == 0 {
w.commit(work.copy(), nil, false, start)
err = w.commit(ctx, work.copy(), nil, false, start)
if err != nil {
return
}
}
// Fill pending transactions from the txpool
w.fillTransactions(interrupt, work)
w.commit(work.copy(), w.fullTaskHook, true, start)
w.fillTransactions(ctx, interrupt, work)
err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start)
if err != nil {
return
}
// Swap out the old work with the new one, terminating any leftover
// prefetcher processes in the mean time and starting a new one.
if w.current != nil {
w.current.discard()
}
w.current = work
}
@ -1148,22 +1297,38 @@ func (w *worker) commitWork(interrupt *int32, noempty bool, timestamp int64) {
// and commits new work if consensus engine is running.
// Note the assumption is held that the mutation is allowed to the passed env, do
// the deep copy first.
func (w *worker) commit(env *environment, interval func(), update bool, start time.Time) error {
func (w *worker) commit(ctx context.Context, env *environment, interval func(), update bool, start time.Time) error {
if w.isRunning() {
ctx, span := tracing.StartSpan(ctx, "commit")
defer tracing.EndSpan(span)
if interval != nil {
interval()
}
// Create a local environment copy, avoid the data race with snapshot state.
// https://github.com/ethereum/go-ethereum/issues/24299
env := env.copy()
block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts)
block, err := w.engine.FinalizeAndAssemble(ctx, w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts)
tracing.SetAttributes(
span,
attribute.Int("number", int(block.Number().Uint64())),
attribute.String("hash", block.Hash().String()),
attribute.String("sealhash", w.engine.SealHash(block.Header()).String()),
attribute.Int("len of env.txs", len(env.txs)),
attribute.Bool("error", err != nil),
)
if err != nil {
return err
}
// If we're post merge, just ignore
if !w.isTTDReached(block.Header()) {
select {
case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}:
case w.taskCh <- &task{ctx: ctx, receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}:
w.unconfirmed.Shift(block.NumberU64() - 1)
log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
"uncles", len(env.uncles), "txs", env.tcount,
@ -1178,11 +1343,14 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
if update {
w.updateSnapshot(env)
}
return nil
}
// getSealingBlock generates the sealing block based on the given parameters.
func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase common.Address, random common.Hash) (*types.Block, error) {
ctx := tracing.WithTracer(context.Background(), otel.GetTracerProvider().Tracer("getSealingBlock"))
req := &getWorkReq{
params: &generateParams{
timestamp: timestamp,
@ -1194,13 +1362,16 @@ func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase
noExtra: true,
},
result: make(chan *types.Block, 1),
ctx: ctx,
}
select {
case w.getWorkCh <- req:
block := <-req.result
if block == nil {
return nil, req.err
}
return block, nil
case <-w.exitCh:
return nil, errors.New("miner closed")

View file

@ -278,8 +278,12 @@ var (
Period: map[string]uint64{
"0": 2,
},
ProducerDelay: 6,
Sprint: 64,
ProducerDelay: map[string]uint64{
"0": 6,
},
Sprint: map[string]uint64{
"0": 64,
},
BackupMultiplier: map[string]uint64{
"0": 2,
},
@ -310,8 +314,12 @@ var (
Period: map[string]uint64{
"0": 1,
},
ProducerDelay: 3,
Sprint: 32,
ProducerDelay: map[string]uint64{
"0": 3,
},
Sprint: map[string]uint64{
"0": 32,
},
BackupMultiplier: map[string]uint64{
"0": 2,
},
@ -341,13 +349,17 @@ var (
BerlinBlock: big.NewInt(13996000),
LondonBlock: big.NewInt(22640000),
Bor: &BorConfig{
JaipurBlock: 22770000,
JaipurBlock: big.NewInt(22770000),
Period: map[string]uint64{
"0": 2,
"25275000": 5,
},
ProducerDelay: 6,
Sprint: 64,
ProducerDelay: map[string]uint64{
"0": 6,
},
Sprint: map[string]uint64{
"0": 64,
},
BackupMultiplier: map[string]uint64{
"0": 2,
"25275000": 5,
@ -386,12 +398,16 @@ var (
BerlinBlock: big.NewInt(14750000),
LondonBlock: big.NewInt(23850000),
Bor: &BorConfig{
JaipurBlock: 23850000,
JaipurBlock: big.NewInt(23850000),
Period: map[string]uint64{
"0": 2,
},
ProducerDelay: 6,
Sprint: 64,
ProducerDelay: map[string]uint64{
"0": 6,
},
Sprint: map[string]uint64{
"0": 64,
},
BackupMultiplier: map[string]uint64{
"0": 2,
},
@ -436,8 +452,10 @@ var (
// adding flags to the config to also have to set these fields.
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, &BorConfig{BurntContract: map[string]string{"0": "0x000000000000000000000000000000000000dead"}}}
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, &BorConfig{Sprint: 4, BurntContract: map[string]string{"0": "0x000000000000000000000000000000000000dead"}}}
TestRules = TestChainConfig.Rules(new(big.Int), false)
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, &BorConfig{Sprint: map[string]uint64{
"0": 4,
}, BurntContract: map[string]string{"0": "0x000000000000000000000000000000000000dead"}}}
TestRules = TestChainConfig.Rules(new(big.Int), false)
)
// TrustedCheckpoint represents a set of post-processed trie roots (CHT and
@ -550,15 +568,16 @@ func (c *CliqueConfig) String() string {
// BorConfig is the consensus engine configs for Matic bor based sealing.
type BorConfig struct {
Period map[string]uint64 `json:"period"` // Number of seconds between blocks to enforce
ProducerDelay uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval
Sprint uint64 `json:"sprint"` // Epoch length to proposer
ProducerDelay map[string]uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval
Sprint map[string]uint64 `json:"sprint"` // Epoch length to proposer
BackupMultiplier map[string]uint64 `json:"backupMultiplier"` // Backup multiplier to determine the wiggle time
ValidatorContract string `json:"validatorContract"` // Validator set contract
StateReceiverContract string `json:"stateReceiverContract"` // State receiver contract
OverrideStateSyncRecords map[string]int `json:"overrideStateSyncRecords"` // override state records count
BlockAlloc map[string]interface{} `json:"blockAlloc"`
BurntContract map[string]string `json:"burntContract"` // governance contract where the token will be sent to and burnt in london fork
JaipurBlock uint64 `json:"jaipurBlock"` // Jaipur switch block (nil = no fork, 0 = already on jaipur)
JaipurBlock *big.Int `json:"jaipurBlock"` // Jaipur switch block (nil = no fork, 0 = already on jaipur)
DelhiBlock *big.Int `json:"delhiBlock"` // Delhi switch block (nil = no fork, 0 = already on delhi)
}
// String implements the stringer interface, returning the consensus engine details.
@ -566,6 +585,14 @@ func (b *BorConfig) String() string {
return "bor"
}
func (c *BorConfig) CalculateProducerDelay(number uint64) uint64 {
return c.calculateSprintSizeHelper(c.ProducerDelay, number)
}
func (c *BorConfig) CalculateSprint(number uint64) uint64 {
return c.calculateSprintSizeHelper(c.Sprint, number)
}
func (c *BorConfig) CalculateBackupMultiplier(number uint64) uint64 {
return c.calculateBorConfigHelper(c.BackupMultiplier, number)
}
@ -574,8 +601,12 @@ func (c *BorConfig) CalculatePeriod(number uint64) uint64 {
return c.calculateBorConfigHelper(c.Period, number)
}
func (c *BorConfig) IsJaipur(number uint64) bool {
return number >= c.JaipurBlock
func (c *BorConfig) IsJaipur(number *big.Int) bool {
return isForked(c.JaipurBlock, number)
}
func (c *BorConfig) IsDelhi(number *big.Int) bool {
return isForked(c.DelhiBlock, number)
}
func (c *BorConfig) calculateBorConfigHelper(field map[string]uint64, number uint64) uint64 {
@ -589,6 +620,7 @@ func (c *BorConfig) calculateBorConfigHelper(field map[string]uint64, number uin
for i := 0; i < len(keys)-1; i++ {
valUint, _ := strconv.ParseUint(keys[i], 10, 64)
valUintNext, _ := strconv.ParseUint(keys[i+1], 10, 64)
if number > valUint && number < valUintNext {
return field[keys[i]]
}
@ -597,6 +629,26 @@ func (c *BorConfig) calculateBorConfigHelper(field map[string]uint64, number uin
return field[keys[len(keys)-1]]
}
func (c *BorConfig) calculateSprintSizeHelper(field map[string]uint64, number uint64) uint64 {
keys := make([]string, 0, len(field))
for k := range field {
keys = append(keys, k)
}
sort.Strings(keys)
for i := 0; i < len(keys)-1; i++ {
valUint, _ := strconv.ParseUint(keys[i], 10, 64)
valUintNext, _ := strconv.ParseUint(keys[i+1], 10, 64)
if number >= valUint && number < valUintNext {
return field[keys[i]]
}
}
return field[keys[len(keys)-1]]
}
func (c *BorConfig) CalculateBurntContract(number uint64) string {
keys := make([]string, 0, len(c.BurntContract))
for k := range c.BurntContract {

View file

@ -119,9 +119,11 @@ const (
// Introduced in Tangerine Whistle (Eip 150)
CreateBySelfdestructGas uint64 = 25000
BaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks.
ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
BaseFeeChangeDenominatorPreDelhi = 8 // Bounds the amount the base fee can change between blocks before Delhi Hard Fork.
BaseFeeChangeDenominatorPostDelhi = 16 // Bounds the amount the base fee can change between blocks after Delhi Hard Fork.
ElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
@ -168,3 +170,11 @@ var (
MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be.
DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not.
)
func BaseFeeChangeDenominator(borConfig *BorConfig, number *big.Int) uint64 {
if borConfig.IsDelhi(number) {
return BaseFeeChangeDenominatorPostDelhi
} else {
return BaseFeeChangeDenominatorPreDelhi
}
}

View file

@ -23,7 +23,7 @@ import (
const (
VersionMajor = 0 // Major version component of the current release
VersionMinor = 3 // Minor version component of the current release
VersionPatch = 0 // Patch version component of the current release
VersionPatch = 1 // Patch version component of the current release
VersionMeta = "beta" // Version metadata to append to the version string
)
@ -43,7 +43,8 @@ var VersionWithMeta = func() string {
// ArchiveVersion holds the textual version string used for Geth archives.
// e.g. "1.8.11-dea1ce05" for stable releases, or
// "1.8.13-unstable-21c059b6" for unstable releases
//
// "1.8.13-unstable-21c059b6" for unstable releases
func ArchiveVersion(gitCommit string) string {
vsn := Version
if VersionMeta != "stable" {

View file

@ -95,7 +95,7 @@ var flagMap = map[string][]string{
"override.arrowglacier": {"notABoolFlag", "No"},
"override.terminaltotaldifficulty": {"notABoolFlag", "No"},
"verbosity": {"notABoolFlag", "YesFV"},
"ws.origins": {"notABoolFlag", "YesF"},
"ws.origins": {"notABoolFlag", "No"},
}
// map from cli flags to corresponding toml tags
@ -109,8 +109,9 @@ var nameTagMap = map[string]string{
"gcmode": "gcmode",
"eth.requiredblocks": "eth.requiredblocks",
"0-snapshot": "snapshot",
"\"bor.logs\"": "bor.logs",
"url": "bor.heimdall",
"bor.without": "bor.withoutheimdall",
"\"bor.without\"": "bor.withoutheimdall",
"grpc-address": "bor.heimdallgRPC",
"locals": "txpool.locals",
"nolocals": "txpool.nolocals",
@ -149,8 +150,7 @@ var nameTagMap = map[string]string{
"ipcpath": "ipcpath",
"1-corsdomain": "http.corsdomain",
"1-vhosts": "http.vhosts",
"2-corsdomain": "ws.corsdomain",
"2-vhosts": "ws.vhosts",
"origins": "ws.origins",
"3-corsdomain": "graphql.corsdomain",
"3-vhosts": "graphql.vhosts",
"1-enabled": "http",
@ -225,12 +225,12 @@ var replacedFlagsMapFlagAndValue = map[string]map[string]map[string]string{
},
}
var replacedFlagsMapFlag = map[string]string{
"ws.origins": "ws.corsdomain",
}
// Do not remove
var replacedFlagsMapFlag = map[string]string{}
var currentBoolFlags = []string{
"snapshot",
"bor.logs",
"bor.withoutheimdall",
"txpool.nolocals",
"mine",

View file

@ -1,10 +1,19 @@
#!/usr/bin/env sh
#!/bin/bash
set -e
# Instructions:
# Execute `./getconfig.sh`, and follow the instructions displayed on the terminal
# The `*-config.toml` file will be created in the same directory as start.sh
# It is recommended to check the flags generated in config.toml
# Some checks to make commands OS independent
OS="$(uname -s)"
MKTEMPOPTION=
SEDOPTION= ## Not used as of now (TODO)
shopt -s nocasematch; if [[ "$OS" == "darwin"* ]]; then
SEDOPTION="''"
MKTEMPOPTION="-t"
fi
read -p "* Path to start.sh: " startPath
# check if start.sh is present
@ -15,7 +24,7 @@ then
fi
read -p "* Your validator address (e.g. 0xca67a8D767e45056DC92384b488E9Af654d78DE2), or press Enter to skip if running a sentry node: " ADD
echo "\nThank you, your inputs are:"
printf "\nThank you, your inputs are:\n"
echo "Path to start.sh: "$startPath
echo "Address: "$ADD
@ -26,8 +35,9 @@ if [[ -f $confPath ]]
then
echo "WARN: config.toml exists, data will be overwritten."
fi
printf "\n"
tmpDir="$(mktemp -d -t ./temp-dir-XXXXXXXXXXX || oops "Can't create temporary directory")"
tmpDir="$(mktemp -d $MKTEMPOPTION ./temp-dir-XXXXXXXXXXX || oops "Can't create temporary directory")"
cleanup() {
rm -rf "$tmpDir"
}
@ -39,8 +49,11 @@ chmod +x $tmpDir/3305fe263dd4a999d58f96deb064e21bb70123d9.sh
$tmpDir/3305fe263dd4a999d58f96deb064e21bb70123d9.sh $ADD
rm $tmpDir/3305fe263dd4a999d58f96deb064e21bb70123d9.sh
sed -i '' "s%*%'*'%g" ./temp
shopt -s nocasematch; if [[ "$OS" == "darwin"* ]]; then
sed -i '' "s%*%'*'%g" ./temp
else
sed -i "s%*%'*'%g" ./temp
fi
# read the flags from `./temp`
dumpconfigflags=$(head -1 ./temp)
@ -51,17 +64,27 @@ bash -c "$command"
rm ./temp
printf "\n"
if [[ -f ./tempStaticNodes.json ]]
then
echo "JSON StaticNodes found"
staticnodesjson=$(head -1 ./tempStaticNodes.json)
sed -i '' "s%static-nodes = \[\]%static-nodes = \[\"${staticnodesjson}\"\]%" $confPath
shopt -s nocasematch; if [[ "$OS" == "darwin"* ]]; then
sed -i '' "s%static-nodes = \[\]%static-nodes = \[\"${staticnodesjson}\"\]%" $confPath
else
sed -i "s%static-nodes = \[\]%static-nodes = \[\"${staticnodesjson}\"\]%" $confPath
fi
rm ./tempStaticNodes.json
elif [[ -f ./tempStaticNodes.toml ]]
then
echo "TOML StaticNodes found"
staticnodestoml=$(head -1 ./tempStaticNodes.toml)
sed -i '' "s%static-nodes = \[\]%static-nodes = \[\"${staticnodestoml}\"\]%" $confPath
shopt -s nocasematch; if [[ "$OS" == "darwin"* ]]; then
sed -i '' "s%static-nodes = \[\]%static-nodes = \[\"${staticnodestoml}\"\]%" $confPath
else
sed -i "s%static-nodes = \[\]%static-nodes = \[\"${staticnodestoml}\"\]%" $confPath
fi
rm ./tempStaticNodes.toml
else
echo "neither JSON nor TOML StaticNodes found"
@ -71,12 +94,18 @@ if [[ -f ./tempTrustedNodes.toml ]]
then
echo "TOML TrustedNodes found"
trustednodestoml=$(head -1 ./tempTrustedNodes.toml)
sed -i '' "s%trusted-nodes = \[\]%trusted-nodes = \[\"${trustednodestoml}\"\]%" $confPath
shopt -s nocasematch; if [[ "$OS" == "darwin"* ]]; then
sed -i '' "s%trusted-nodes = \[\]%trusted-nodes = \[\"${trustednodestoml}\"\]%" $confPath
else
sed -i "s%trusted-nodes = \[\]%trusted-nodes = \[\"${trustednodestoml}\"\]%" $confPath
fi
rm ./tempTrustedNodes.toml
else
echo "neither JSON nor TOML TrustedNodes found"
fi
printf "\n"
# comment flags in $configPath that were not passed through $startPath
# SHA1 hash of `tempStart` -> `3305fe263dd4a999d58f96deb064e21bb70123d9`
sed "s%bor --%go run getconfig.go ${confPath} --%" $startPath > $tmpDir/3305fe263dd4a999d58f96deb064e21bb70123d9.sh

View file

@ -138,7 +138,7 @@ func TestAPIs(t *testing.T) {
}()
genesis := core.GenesisBlockForTesting(db, addrr, big.NewInt(1000000))
sprint := params.TestChainConfig.Bor.Sprint
testBorConfig := params.TestChainConfig.Bor
chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 6, func(i int, gen *core.BlockGen) {
switch i {
@ -208,7 +208,7 @@ func TestAPIs(t *testing.T) {
blockBatch := db.NewBatch()
if i%int(sprint-1) != 0 {
if i%int(testBorConfig.CalculateSprint(block.NumberU64())-1) != 0 {
// if it is not sprint start write all the transactions as normal transactions.
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i])
} else {

View file

@ -31,12 +31,17 @@ func TestBorFilters(t *testing.T) {
hash2 = common.BytesToHash([]byte("topic2"))
hash3 = common.BytesToHash([]byte("topic3"))
hash4 = common.BytesToHash([]byte("topic4"))
hash5 = common.BytesToHash([]byte("topic5"))
)
defer db.Close()
genesis := core.GenesisBlockForTesting(db, addr, big.NewInt(1000000))
sprint := params.TestChainConfig.Bor.Sprint
testBorConfig := params.TestChainConfig.Bor
testBorConfig.Sprint = map[string]uint64{
"0": 4,
"8": 2,
}
chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 1000, func(i int, gen *core.BlockGen) {
switch i {
@ -73,7 +78,7 @@ func TestBorFilters(t *testing.T) {
gen.AddUncheckedReceipt(receipt)
gen.AddUncheckedTx(types.NewTransaction(992, common.HexToAddress("0x992"), big.NewInt(992), 992, gen.BaseFee(), nil))
case 999: //state-sync tx at block 1000
case 993: //state-sync tx at block 994
receipt := types.NewReceipt(nil, false, 0)
receipt.Logs = []*types.Log{
{
@ -82,6 +87,17 @@ func TestBorFilters(t *testing.T) {
},
}
gen.AddUncheckedReceipt(receipt)
gen.AddUncheckedTx(types.NewTransaction(994, common.HexToAddress("0x994"), big.NewInt(994), 994, gen.BaseFee(), nil))
case 999: //state-sync tx at block 1000
receipt := types.NewReceipt(nil, false, 0)
receipt.Logs = []*types.Log{
{
Address: addr,
Topics: []common.Hash{hash5},
},
}
gen.AddUncheckedReceipt(receipt)
gen.AddUncheckedTx(types.NewTransaction(1000, common.HexToAddress("0x1000"), big.NewInt(1000), 1000, gen.BaseFee(), nil))
}
})
@ -122,14 +138,14 @@ func TestBorFilters(t *testing.T) {
}
}
filter := filters.NewBorBlockLogsRangeFilter(backend, sprint, 0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}})
filter := filters.NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4, hash5}})
logs, _ := filter.Logs(context.Background())
if len(logs) != 4 {
t.Error("expected 4 log, got", len(logs))
if len(logs) != 5 {
t.Error("expected 5 log, got", len(logs))
}
filter = filters.NewBorBlockLogsRangeFilter(backend, sprint, 900, 999, []common.Address{addr}, [][]common.Hash{{hash3}})
filter = filters.NewBorBlockLogsRangeFilter(backend, testBorConfig, 900, 999, []common.Address{addr}, [][]common.Hash{{hash3}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 1 {
@ -140,7 +156,7 @@ func TestBorFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
}
filter = filters.NewBorBlockLogsRangeFilter(backend, sprint, 992, -1, []common.Address{addr}, [][]common.Hash{{hash3}})
filter = filters.NewBorBlockLogsRangeFilter(backend, testBorConfig, 992, -1, []common.Address{addr}, [][]common.Hash{{hash3}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 1 {
@ -151,7 +167,7 @@ func TestBorFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
}
filter = filters.NewBorBlockLogsRangeFilter(backend, sprint, 1, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2}})
filter = filters.NewBorBlockLogsRangeFilter(backend, testBorConfig, 1, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 2 {
@ -159,7 +175,7 @@ func TestBorFilters(t *testing.T) {
}
failHash := common.BytesToHash([]byte("fail"))
filter = filters.NewBorBlockLogsRangeFilter(backend, sprint, 0, -1, nil, [][]common.Hash{{failHash}})
filter = filters.NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, -1, nil, [][]common.Hash{{failHash}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 0 {
@ -167,14 +183,14 @@ func TestBorFilters(t *testing.T) {
}
failAddr := common.BytesToAddress([]byte("failmenow"))
filter = filters.NewBorBlockLogsRangeFilter(backend, sprint, 0, -1, []common.Address{failAddr}, nil)
filter = filters.NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, -1, []common.Address{failAddr}, nil)
logs, _ = filter.Logs(context.Background())
if len(logs) != 0 {
t.Error("expected 0 log, got", len(logs))
}
filter = filters.NewBorBlockLogsRangeFilter(backend, sprint, 0, -1, nil, [][]common.Hash{{failHash}, {hash1}})
filter = filters.NewBorBlockLogsRangeFilter(backend, testBorConfig, 0, -1, nil, [][]common.Hash{{failHash}, {hash1}})
logs, _ = filter.Logs(context.Background())
if len(logs) != 0 {

View file

@ -1,278 +0,0 @@
//go:build integration
package bor
import (
"crypto/ecdsa"
"encoding/json"
"io/ioutil"
"math/big"
"os"
"testing"
"time"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/assert"
)
var (
// addr1 = 0x71562b71999873DB5b286dF957af199Ec94617F7
pkey1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// addr2 = 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791
pkey2, _ = crypto.HexToECDSA("9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3")
keys = []*ecdsa.PrivateKey{pkey1, pkey2}
)
func initMiner(genesis *core.Genesis, privKey *ecdsa.PrivateKey) (*node.Node, *eth.Ethereum, error) {
// Define the basic configurations for the Ethereum node
datadir, _ := ioutil.TempDir("", "")
config := &node.Config{
Name: "geth",
Version: params.Version,
DataDir: datadir,
P2P: p2p.Config{
ListenAddr: "0.0.0.0:0",
NoDiscovery: true,
MaxPeers: 25,
},
UseLightweightKDF: true,
}
// Create the node and configure a full Ethereum node on it
stack, err := node.New(config)
if err != nil {
return nil, nil, err
}
ethBackend, err := eth.New(stack, &ethconfig.Config{
Genesis: genesis,
NetworkId: genesis.Config.ChainID.Uint64(),
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
TxPool: core.DefaultTxPoolConfig,
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{
Etherbase: crypto.PubkeyToAddress(privKey.PublicKey),
GasCeil: genesis.GasLimit * 11 / 10,
GasPrice: big.NewInt(1),
Recommit: time.Second,
},
WithoutHeimdall: true,
})
if err != nil {
return nil, nil, err
}
// register backend to account manager with keystore for signing
keydir := stack.KeyStoreDir()
n, p := keystore.StandardScryptN, keystore.StandardScryptP
kStore := keystore.NewKeyStore(keydir, n, p)
kStore.ImportECDSA(privKey, "")
acc := kStore.Accounts()[0]
kStore.Unlock(acc, "")
// proceed to authorize the local account manager in any case
ethBackend.AccountManager().AddBackend(kStore)
// ethBackend.AccountManager().AddBackend()
err = stack.Start()
return stack, ethBackend, err
}
func initGenesis(t *testing.T, faucets []*ecdsa.PrivateKey) *core.Genesis {
// sprint size = 8 in genesis
genesisData, err := ioutil.ReadFile("./testdata/genesis_2val.json")
if err != nil {
t.Fatalf("%s", err)
}
genesis := &core.Genesis{}
if err := json.Unmarshal(genesisData, genesis); err != nil {
t.Fatalf("%s", err)
}
genesis.Config.ChainID = big.NewInt(15001)
genesis.Config.EIP150Hash = common.Hash{}
return genesis
}
func TestValidatorWentOffline(t *testing.T) {
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
fdlimit.Raise(2048)
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
// Create an Ethash network based off of the Ropsten config
genesis := initGenesis(t, faucets)
var (
stacks []*node.Node
nodes []*eth.Ethereum
enodes []*enode.Node
)
for i := 0; i < 2; i++ {
// Start the node and wait until it's up
stack, ethBackend, err := initMiner(genesis, keys[i])
if err != nil {
panic(err)
}
defer stack.Close()
for stack.Server().NodeInfo().Ports.Listener == 0 {
time.Sleep(250 * time.Millisecond)
}
// Connect the node to all the previous ones
for _, n := range enodes {
stack.Server().AddPeer(n)
}
// Start tracking the node and its enode
stacks = append(stacks, stack)
nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self())
}
// Iterate over all the nodes and start mining
time.Sleep(3 * time.Second)
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
panic(err)
}
}
for {
// for block 1 to 8, the primary validator is node0
// for block 9 to 16, the primary validator is node1
// for block 17 to 24, the primary validator is node0
// for block 25 to 32, the primary validator is node1
blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader()
// we remove peer connection between node0 and node1
if blockHeaderVal0.Number.Uint64() == 9 {
stacks[0].Server().RemovePeer(enodes[1])
}
// here, node1 is the primary validator, node0 will sign out-of-turn
// we add peer connection between node1 and node0
if blockHeaderVal0.Number.Uint64() == 14 {
stacks[0].Server().AddPeer(enodes[1])
}
// reorg happens here, node1 has higher difficulty, it will replace blocks by node0
if blockHeaderVal0.Number.Uint64() == 30 {
break
}
}
// check block 10 miner ; expected author is node1 signer
blockHeaderVal0 := nodes[0].BlockChain().GetHeaderByNumber(10)
blockHeaderVal1 := nodes[1].BlockChain().GetHeaderByNumber(10)
authorVal0, err := nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err := nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 10
assert.Equal(t, authorVal0, authorVal1)
// check node0 has block mined by node1
assert.Equal(t, authorVal0, nodes[1].AccountManager().Accounts()[0])
// check node1 has block mined by node1
assert.Equal(t, authorVal1, nodes[1].AccountManager().Accounts()[0])
// check block 11 miner ; expected author is node1 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(11)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(11)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 11
assert.Equal(t, authorVal0, authorVal1)
// check node0 has block mined by node1
assert.Equal(t, authorVal0, nodes[1].AccountManager().Accounts()[0])
// check node1 has block mined by node1
assert.Equal(t, authorVal1, nodes[1].AccountManager().Accounts()[0])
// check block 12 miner ; expected author is node1 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(12)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(12)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 12
assert.Equal(t, authorVal0, authorVal1)
// check node0 has block mined by node1
assert.Equal(t, authorVal0, nodes[1].AccountManager().Accounts()[0])
// check node1 has block mined by node1
assert.Equal(t, authorVal1, nodes[1].AccountManager().Accounts()[0])
// check block 17 miner ; expected author is node0 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(17)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(17)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 17
assert.Equal(t, authorVal0, authorVal1)
// check node0 has block mined by node1
assert.Equal(t, authorVal0, nodes[0].AccountManager().Accounts()[0])
// check node1 has block mined by node1
assert.Equal(t, authorVal1, nodes[0].AccountManager().Accounts()[0])
}

View file

@ -0,0 +1,855 @@
package bor
import (
"crypto/ecdsa"
"encoding/csv"
"encoding/json"
"fmt"
"io/ioutil" // nolint: staticcheck
_log "log"
"math/big"
"os"
"sync"
"testing"
"time"
"gotest.tools/assert"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
)
var (
// Only this account is a validator for the 0th span
keySprintLength, _ = crypto.HexToECDSA(privKeySprintLength)
// This account is one the validators for 1st span (0-indexed)
keySprintLength2, _ = crypto.HexToECDSA(privKeySprintLength2)
keysSprintLength = []*ecdsa.PrivateKey{keySprintLength, keySprintLength2}
)
const (
privKeySprintLength = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
privKeySprintLength2 = "9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3"
)
// Sprint length change tests
func TestValidatorsBlockProduction(t *testing.T) {
t.Parallel()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
_, err := fdlimit.Raise(2048)
if err != nil {
panic(err)
}
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
// Create an Ethash network based off of the Ropsten config
// Generate a batch of accounts to seal and fund with
genesis := InitGenesisSprintLength(t, faucets, "./testdata/genesis_sprint_length_change.json", 8)
nodes := make([]*eth.Ethereum, 2)
enodes := make([]*enode.Node, 2)
for i := 0; i < 2; i++ {
// Start the node and wait until it's up
stack, ethBackend, err := InitMinerSprintLength(genesis, keysSprintLength[i], true)
if err != nil {
panic(err)
}
defer stack.Close()
for stack.Server().NodeInfo().Ports.Listener == 0 {
time.Sleep(250 * time.Millisecond)
}
// Connect the node to all the previous ones
for j, n := range enodes {
if j < i {
stack.Server().AddPeer(n)
}
}
// Start tracking the node and its enode
nodes[i] = ethBackend
enodes[i] = stack.Server().Self()
}
// Iterate over all the nodes and start mining
time.Sleep(3 * time.Second)
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
panic(err)
}
}
for {
// for block 0 to 7, the primary validator is node0
// for block 8 to 15, the primary validator is node1
// for block 16 to 19, the primary validator is node0
// for block 20 to 23, the primary validator is node1
blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader()
if blockHeaderVal0.Number.Uint64() == 24 {
break
}
}
// check block 7 miner ; expected author is node0 signer
blockHeaderVal0 := nodes[0].BlockChain().GetHeaderByNumber(7)
blockHeaderVal1 := nodes[1].BlockChain().GetHeaderByNumber(7)
authorVal0, err := nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err := nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 7
assert.Equal(t, authorVal0, authorVal1)
// check block mined by node0
assert.Equal(t, authorVal0, nodes[0].AccountManager().Accounts()[0])
// check block 15 miner ; expected author is node1 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(15)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(15)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 15
assert.Equal(t, authorVal0, authorVal1)
// check block mined by node1
assert.Equal(t, authorVal0, nodes[1].AccountManager().Accounts()[0])
// check block 19 miner ; expected author is node0 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(19)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(19)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 19
assert.Equal(t, authorVal0, authorVal1)
// check block mined by node0
assert.Equal(t, authorVal0, nodes[0].AccountManager().Accounts()[0])
// check block 23 miner ; expected author is node1 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(23)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(23)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 23
assert.Equal(t, authorVal0, authorVal1)
// check block mined by node1
assert.Equal(t, authorVal0, nodes[1].AccountManager().Accounts()[0])
}
func TestSprintLengths(t *testing.T) {
t.Parallel()
testBorConfig := params.TestChainConfig.Bor
testBorConfig.Sprint = map[string]uint64{
"0": 16,
"8": 4,
}
assert.Equal(t, testBorConfig.CalculateSprint(0), uint64(16))
assert.Equal(t, testBorConfig.CalculateSprint(8), uint64(4))
assert.Equal(t, testBorConfig.CalculateSprint(9), uint64(4))
}
func TestProducerDelay(t *testing.T) {
t.Parallel()
testBorConfig := params.TestChainConfig.Bor
testBorConfig.ProducerDelay = map[string]uint64{
"0": 16,
"8": 4,
}
assert.Equal(t, testBorConfig.CalculateProducerDelay(0), uint64(16))
assert.Equal(t, testBorConfig.CalculateProducerDelay(8), uint64(4))
assert.Equal(t, testBorConfig.CalculateProducerDelay(9), uint64(4))
}
var keys_21val = []map[string]string{
{
"address": "0x5C3E1B893B9315a968fcC6bce9EB9F7d8E22edB3",
"priv_key": "c19fac8e538447124ad2408d9fbaeda2bb686fee763dca7a6bab58ea12442413",
"pub_key": "0x0495421933eda03dcc37f9186c24e255b569513aefae71e96d55d0db3df17502e24e86297b01a167fab9ce1174f06ee3110510ac242e39218bd964de5b345edbd6",
},
{
"address": "0x73E033779C9030D4528d86FbceF5B02e97488921",
"priv_key": "61eb51cf8936309151ab7b931841ea033b6a09931f6a100b464fbbd74f3e0bd7",
"pub_key": "0x04f9a5e9bf76b45ac58f1b018ccba4b83b3531010cdadf42174c18a9db9879ef1dcb5d1254ce834bc108b110cd8d0186ed69a0387528a142bdb5936faf58bf98c9",
},
{
"address": "0x751eC4877450B8a4D652d0D70197337FC38a42e6",
"priv_key": "6e7f48d012c9c0baadbdc88af32521e2e477fd6898a9b65e6abe19fd6652cb2e",
"pub_key": "0x0479db4c0b757bf0e5d9b8954b078ab7c0e91d6c19697904d23d07ea4853c8584382de91174929ba5c598214b8a991471ae051458ea787cdc15a4e435a55ef8059",
},
{
"address": "0xA464DC4810Bc79B956810759e314d85BcE35cD1c",
"priv_key": "3efcf3f7014a6257f4a443119851414111820c681b27525dab3f35e72e28e51e",
"pub_key": "0x040180920306bf598ea050e258f2c7e50804a77a64f5a11705e08d18ee71eb0a80fafc95d0a42b92371ded042edda16c1f0b5f2fef7c4113ad66c59a71c29d977e",
},
{
"address": "0xb005bc07015170266Bd430f3EC1322938603be20",
"priv_key": "17cd9b38c2b3a639c7d97ccbf2bb6c7140ab8f625aec4c249bc8e4cfd3bf9a96",
"pub_key": "0x04435a70d343aa569e6f3386c73e39a1aa6f88c30e5943baedda9618b55cc944a2de1d114aff6d0e9fa002bebc780b04ef6c1b8a06bbf0d41c10d1efa55390f198",
},
{
"address": "0xE8d02Da3dFeeB3e755472D95D666BD6821D92129",
"priv_key": "45c9ef66361a2283cef14184f128c41949103b791aa622ead3c0bc844648b835",
"pub_key": "0x04a14651ddc80467eb589d72d95153fa695e4cb2e4bb99edeb912e840d309d61313b6f4676081b099f29e6598ecf98cb7b44bb862d019920718b558f27ba94ca51",
},
{
"address": "0xF93B54Cf36E917f625B48e1e3C9F93BC2344Fb06",
"priv_key": "93788a1305605808df1f9a96b5e1157da191680cf08bc15e077138f517563cd5",
"pub_key": "0x045eee11dceccd9cccc371ca3d96d74c848e785223f1e5df4d1a7f08efdfeb90bd8f0035342a9c26068cf6c7ab395ca3ceea555541325067fc187c375390efa57d",
},
}
func getTestSprintLengthReorgCases2Nodes() []map[string]interface{} {
sprintSizes := []uint64{64}
faultyNodes := [][]uint64{{0, 1}, {1, 2}, {0, 2}}
reorgsLengthTests := make([]map[string]interface{}, 0)
for i := uint64(0); i < uint64(len(sprintSizes)); i++ {
maxReorgLength := sprintSizes[i] * 4
for j := uint64(20); j <= maxReorgLength; j = j + 8 {
maxStartBlock := sprintSizes[i] - 1
for k := sprintSizes[i] / 2; k <= maxStartBlock; k = k + 4 {
for l := uint64(0); l < uint64(len(faultyNodes)); l++ {
if j+k < sprintSizes[i] {
continue
}
reorgsLengthTest := map[string]interface{}{
"reorgLength": j,
"startBlock": k,
"sprintSize": sprintSizes[i],
"faultyNodes": faultyNodes[l], // node 1(index) is primary validator of the first sprint
}
reorgsLengthTests = append(reorgsLengthTests, reorgsLengthTest)
}
}
}
}
// reorgsLengthTests := []map[string]uint64{
// {
// "reorgLength": 3,
// "startBlock": 7,
// "sprintSize": 8,
// "faultyNode": 1,
// },
// }
return reorgsLengthTests
}
func getTestSprintLengthReorgCases() []map[string]uint64 {
sprintSizes := []uint64{64, 32, 16, 8}
faultyNodes := []uint64{0, 1}
reorgsLengthTests := make([]map[string]uint64, 0)
for i := uint64(0); i < uint64(len(sprintSizes)); i++ {
maxReorgLength := sprintSizes[i] * 4
for j := uint64(3); j <= maxReorgLength; j = j + 4 {
maxStartBlock := sprintSizes[i] - 1
for k := sprintSizes[i] / 2; k <= maxStartBlock; k = k + 4 {
for l := uint64(0); l < uint64(len(faultyNodes)); l++ {
if j+k < sprintSizes[i] {
continue
}
reorgsLengthTest := map[string]uint64{
"reorgLength": j,
"startBlock": k,
"sprintSize": sprintSizes[i],
"faultyNode": faultyNodes[l], // node 1(index) is primary validator of the first sprint
}
reorgsLengthTests = append(reorgsLengthTests, reorgsLengthTest)
}
}
}
}
// reorgsLengthTests := []map[string]uint64{
// {
// "reorgLength": 3,
// "startBlock": 7,
// "sprintSize": 8,
// "faultyNode": 1,
// },
// }
return reorgsLengthTests
}
func SprintLengthReorgIndividual(t *testing.T, index int, tt map[string]uint64) (uint64, uint64, uint64, uint64, uint64, uint64) {
t.Helper()
log.Warn("Case ----- ", "Index", index, "InducedReorgLength", tt["reorgLength"], "BlockStart", tt["startBlock"], "SprintSize", tt["sprintSize"], "DisconnectedNode", tt["faultyNode"])
observerOldChainLength, faultyOldChainLength := SetupValidatorsAndTest(t, tt)
if observerOldChainLength > 0 {
log.Warn("Observer", "Old Chain length", observerOldChainLength)
}
if faultyOldChainLength > 0 {
log.Warn("Faulty", "Old Chain length", faultyOldChainLength)
}
return tt["reorgLength"], tt["startBlock"], tt["sprintSize"], tt["faultyNode"], faultyOldChainLength, observerOldChainLength
}
func SprintLengthReorgIndividual2Nodes(t *testing.T, index int, tt map[string]interface{}) (uint64, uint64, uint64, []uint64, uint64, uint64) {
t.Helper()
log.Warn("Case ----- ", "Index", index, "InducedReorgLength", tt["reorgLength"], "BlockStart", tt["startBlock"], "SprintSize", tt["sprintSize"], "DisconnectedNode", tt["faultyNodes"])
observerOldChainLength, faultyOldChainLength := SetupValidatorsAndTest2Nodes(t, tt)
if observerOldChainLength > 0 {
log.Warn("Observer", "Old Chain length", observerOldChainLength)
}
if faultyOldChainLength > 0 {
log.Warn("Faulty", "Old Chain length", faultyOldChainLength)
}
fNodes, _ := tt["faultyNodes"].([]uint64)
return tt["reorgLength"].(uint64), tt["startBlock"].(uint64), tt["sprintSize"].(uint64), fNodes, faultyOldChainLength, observerOldChainLength
}
func TestSprintLengthReorg2Nodes(t *testing.T) {
t.Skip()
t.Parallel()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
_, err := fdlimit.Raise(2048)
if err != nil {
panic(err)
}
reorgsLengthTests := getTestSprintLengthReorgCases2Nodes()
f, err := os.Create("sprintReorg2Nodes.csv")
defer func() {
err = f.Close()
if err != nil {
panic(err)
}
}()
if err != nil {
_log.Fatalln("failed to open file", err)
}
w := csv.NewWriter(f)
err = w.Write([]string{"Induced Reorg Length", "Start Block", "Sprint Size", "Disconnected Node Ids", "Disconnected Node Id's Reorg Length", "Observer Node Id's Reorg Length"})
w.Flush()
if err != nil {
panic(err)
}
var wg sync.WaitGroup
for index, tt := range reorgsLengthTests {
if index%4 == 0 {
wg.Wait()
}
wg.Add(1)
go SprintLengthReorgIndividual2NodesHelper(t, index, tt, w, &wg)
}
}
func TestSprintLengthReorg(t *testing.T) {
t.Skip()
t.Parallel()
reorgsLengthTests := getTestSprintLengthReorgCases()
f, err := os.Create("sprintReorg.csv")
defer func() {
err = f.Close()
if err != nil {
panic(err)
}
}()
if err != nil {
_log.Fatalln("failed to open file", err)
}
w := csv.NewWriter(f)
err = w.Write([]string{"Induced Reorg Length", "Start Block", "Sprint Size", "Disconnected Node Id", "Disconnected Node Id's Reorg Length", "Observer Node Id's Reorg Length"})
w.Flush()
if err != nil {
panic(err)
}
var wg sync.WaitGroup
for index, tt := range reorgsLengthTests {
if index%4 == 0 {
wg.Wait()
}
wg.Add(1)
go SprintLengthReorgIndividualHelper(t, index, tt, w, &wg)
}
}
func SprintLengthReorgIndividualHelper(t *testing.T, index int, tt map[string]uint64, w *csv.Writer, wg *sync.WaitGroup) {
t.Helper()
r1, r2, r3, r4, r5, r6 := SprintLengthReorgIndividual(t, index, tt)
err := w.Write([]string{fmt.Sprint(r1), fmt.Sprint(r2), fmt.Sprint(r3), fmt.Sprint(r4), fmt.Sprint(r5), fmt.Sprint(r6)})
if err != nil {
panic(err)
}
w.Flush()
(*wg).Done()
}
func SprintLengthReorgIndividual2NodesHelper(t *testing.T, index int, tt map[string]interface{}, w *csv.Writer, wg *sync.WaitGroup) {
t.Helper()
r1, r2, r3, r4, r5, r6 := SprintLengthReorgIndividual2Nodes(t, index, tt)
err := w.Write([]string{fmt.Sprint(r1), fmt.Sprint(r2), fmt.Sprint(r3), fmt.Sprint(r4), fmt.Sprint(r5), fmt.Sprint(r6)})
if err != nil {
panic(err)
}
w.Flush()
(*wg).Done()
}
// nolint: gocognit
func SetupValidatorsAndTest(t *testing.T, tt map[string]uint64) (uint64, uint64) {
t.Helper()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
_, err := fdlimit.Raise(2048)
if err != nil {
panic(err)
}
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
// Create an Ethash network based off of the Ropsten config
// Generate a batch of accounts to seal and fund with
genesis := InitGenesisSprintLength(t, faucets, "./testdata/genesis_7val.json", tt["sprintSize"])
nodes := make([]*eth.Ethereum, len(keys_21val))
enodes := make([]*enode.Node, len(keys_21val))
stacks := make([]*node.Node, len(keys_21val))
pkeys_21val := make([]*ecdsa.PrivateKey, len(keys_21val))
for index, signerdata := range keys_21val {
pkeys_21val[index], _ = crypto.HexToECDSA(signerdata["priv_key"])
}
for i := 0; i < len(keys_21val); i++ {
// Start the node and wait until it's up
stack, ethBackend, err := InitMinerSprintLength(genesis, pkeys_21val[i], true)
if err != nil {
panic(err)
}
defer stack.Close()
for stack.Server().NodeInfo().Ports.Listener == 0 {
time.Sleep(250 * time.Millisecond)
}
// Connect the node to all the previous ones
for j, n := range enodes {
if j < i {
stack.Server().AddPeer(n)
}
}
// Start tracking the node and its enode
stacks[i] = stack
nodes[i] = ethBackend
enodes[i] = stack.Server().Self()
}
// Iterate over all the nodes and start mining
time.Sleep(3 * time.Second)
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
panic(err)
}
}
chain2HeadChObserver := make(chan core.Chain2HeadEvent, 64)
chain2HeadChFaulty := make(chan core.Chain2HeadEvent, 64)
var observerOldChainLength, faultyOldChainLength uint64
faultyProducerIndex := tt["faultyNode"] // node causing reorg :: faulty ::
subscribedNodeIndex := 6 // node on different partition, produces 7th sprint but our testcase does not run till 7th sprint. :: observer ::
nodes[subscribedNodeIndex].BlockChain().SubscribeChain2HeadEvent(chain2HeadChObserver)
nodes[faultyProducerIndex].BlockChain().SubscribeChain2HeadEvent(chain2HeadChFaulty)
stacks[faultyProducerIndex].Server().NoDiscovery = true
for {
blockHeaderObserver := nodes[subscribedNodeIndex].BlockChain().CurrentHeader()
blockHeaderFaulty := nodes[faultyProducerIndex].BlockChain().CurrentHeader()
log.Warn("Current Observer block", "number", blockHeaderObserver.Number, "hash", blockHeaderObserver.Hash())
log.Warn("Current Faulty block", "number", blockHeaderFaulty.Number, "hash", blockHeaderFaulty.Hash())
if blockHeaderFaulty.Number.Uint64() == tt["startBlock"] {
stacks[faultyProducerIndex].Server().MaxPeers = 0
for _, enode := range enodes {
stacks[faultyProducerIndex].Server().RemovePeer(enode)
}
}
if blockHeaderObserver.Number.Uint64() >= tt["startBlock"] && blockHeaderObserver.Number.Uint64() < tt["startBlock"]+tt["reorgLength"] {
for _, enode := range enodes {
stacks[faultyProducerIndex].Server().RemovePeer(enode)
}
}
if blockHeaderObserver.Number.Uint64() == tt["startBlock"]+tt["reorgLength"] {
stacks[faultyProducerIndex].Server().NoDiscovery = false
stacks[faultyProducerIndex].Server().MaxPeers = 100
for _, enode := range enodes {
stacks[faultyProducerIndex].Server().AddPeer(enode)
}
}
if blockHeaderFaulty.Number.Uint64() >= 255 {
break
}
select {
case ev := <-chain2HeadChObserver:
if ev.Type == core.Chain2HeadReorgEvent {
if len(ev.OldChain) > 1 {
observerOldChainLength = uint64(len(ev.OldChain))
return observerOldChainLength, 0
}
}
case ev := <-chain2HeadChFaulty:
if ev.Type == core.Chain2HeadReorgEvent {
if len(ev.OldChain) > 1 {
faultyOldChainLength = uint64(len(ev.OldChain))
return 0, faultyOldChainLength
}
}
default:
time.Sleep(500 * time.Millisecond)
}
}
return 0, 0
}
// nolint: gocognit
func SetupValidatorsAndTest2Nodes(t *testing.T, tt map[string]interface{}) (uint64, uint64) {
t.Helper()
log.Root().SetHandler(log.LvlFilterHandler(3, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
_, err := fdlimit.Raise(2048)
if err != nil {
panic(err)
}
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
// Create an Ethash network based off of the Ropsten config
// Generate a batch of accounts to seal and fund with
genesis := InitGenesisSprintLength(t, faucets, "./testdata/genesis_7val.json", tt["sprintSize"].(uint64))
nodes := make([]*eth.Ethereum, len(keys_21val))
enodes := make([]*enode.Node, len(keys_21val))
stacks := make([]*node.Node, len(keys_21val))
pkeys_21val := make([]*ecdsa.PrivateKey, len(keys_21val))
for index, signerdata := range keys_21val {
pkeys_21val[index], _ = crypto.HexToECDSA(signerdata["priv_key"])
}
for i := 0; i < len(keys_21val); i++ {
// Start the node and wait until it's up
stack, ethBackend, err := InitMinerSprintLength(genesis, pkeys_21val[i], true)
if err != nil {
panic(err)
}
defer stack.Close()
for stack.Server().NodeInfo().Ports.Listener == 0 {
time.Sleep(250 * time.Millisecond)
}
// Connect the node to all the previous ones
for j, n := range enodes {
if j < i {
stack.Server().AddPeer(n)
}
}
// Start tracking the node and its enode
stacks[i] = stack
nodes[i] = ethBackend
enodes[i] = stack.Server().Self()
}
// Iterate over all the nodes and start mining
time.Sleep(3 * time.Second)
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
panic(err)
}
}
chain2HeadChObserver := make(chan core.Chain2HeadEvent, 64)
chain2HeadChFaulty := make(chan core.Chain2HeadEvent, 64)
var observerOldChainLength, faultyOldChainLength uint64
faultyProducerIndex := tt["faultyNodes"].([]uint64)[0] // node causing reorg :: faulty ::
subscribedNodeIndex := 6 // node on different partition, produces 7th sprint but our testcase does not run till 7th sprint. :: observer ::
nodes[subscribedNodeIndex].BlockChain().SubscribeChain2HeadEvent(chain2HeadChObserver)
nodes[faultyProducerIndex].BlockChain().SubscribeChain2HeadEvent(chain2HeadChFaulty)
stacks[faultyProducerIndex].Server().NoDiscovery = true
for {
blockHeaderObserver := nodes[subscribedNodeIndex].BlockChain().CurrentHeader()
blockHeaderFaulty := nodes[faultyProducerIndex].BlockChain().CurrentHeader()
log.Warn("Current Observer block", "number", blockHeaderObserver.Number, "hash", blockHeaderObserver.Hash())
log.Warn("Current Faulty block", "number", blockHeaderFaulty.Number, "hash", blockHeaderFaulty.Hash())
if blockHeaderObserver.Number.Uint64() >= tt["startBlock"].(uint64) && blockHeaderObserver.Number.Uint64() < tt["startBlock"].(uint64)+tt["reorgLength"].(uint64) {
for _, n := range tt["faultyNodes"].([]uint64) {
stacks[n].Server().MaxPeers = 1
for _, enode := range enodes {
stacks[n].Server().RemovePeer(enode)
}
for _, m := range tt["faultyNodes"].([]uint64) {
stacks[m].Server().AddPeer(enodes[n])
}
}
}
if blockHeaderObserver.Number.Uint64() == tt["startBlock"].(uint64)+tt["reorgLength"].(uint64) {
stacks[faultyProducerIndex].Server().NoDiscovery = false
stacks[faultyProducerIndex].Server().MaxPeers = 100
for _, enode := range enodes {
stacks[faultyProducerIndex].Server().AddPeer(enode)
}
}
if blockHeaderFaulty.Number.Uint64() >= 255 {
break
}
select {
case ev := <-chain2HeadChObserver:
if ev.Type == core.Chain2HeadReorgEvent {
if len(ev.OldChain) > 1 {
observerOldChainLength = uint64(len(ev.OldChain))
return observerOldChainLength, 0
}
}
case ev := <-chain2HeadChFaulty:
if ev.Type == core.Chain2HeadReorgEvent {
if len(ev.OldChain) > 1 {
faultyOldChainLength = uint64(len(ev.OldChain))
return 0, faultyOldChainLength
}
}
default:
time.Sleep(500 * time.Millisecond)
}
}
return 0, 0
}
func InitGenesisSprintLength(t *testing.T, faucets []*ecdsa.PrivateKey, fileLocation string, sprintSize uint64) *core.Genesis {
t.Helper()
// sprint size = 8 in genesis
genesisData, err := ioutil.ReadFile(fileLocation)
if err != nil {
t.Fatalf("%s", err)
}
genesis := &core.Genesis{}
if err := json.Unmarshal(genesisData, genesis); err != nil {
t.Fatalf("%s", err)
}
genesis.Config.ChainID = big.NewInt(15001)
genesis.Config.EIP150Hash = common.Hash{}
genesis.Config.Bor.Sprint["0"] = sprintSize
return genesis
}
func InitMinerSprintLength(genesis *core.Genesis, privKey *ecdsa.PrivateKey, withoutHeimdall bool) (*node.Node, *eth.Ethereum, error) {
// Define the basic configurations for the Ethereum node
datadir, _ := ioutil.TempDir("", "")
config := &node.Config{
Name: "geth",
Version: params.Version,
DataDir: datadir,
P2P: p2p.Config{
ListenAddr: "0.0.0.0:0",
NoDiscovery: true,
MaxPeers: 25,
},
UseLightweightKDF: true,
}
// Create the node and configure a full Ethereum node on it
stack, err := node.New(config)
if err != nil {
return nil, nil, err
}
ethBackend, err := eth.New(stack, &ethconfig.Config{
Genesis: genesis,
NetworkId: genesis.Config.ChainID.Uint64(),
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
TxPool: core.DefaultTxPoolConfig,
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{
Etherbase: crypto.PubkeyToAddress(privKey.PublicKey),
GasCeil: genesis.GasLimit * 11 / 10,
GasPrice: big.NewInt(1),
Recommit: time.Second,
},
WithoutHeimdall: withoutHeimdall,
})
if err != nil {
return nil, nil, err
}
// register backend to account manager with keystore for signing
keydir := stack.KeyStoreDir()
n, p := keystore.StandardScryptN, keystore.StandardScryptP
kStore := keystore.NewKeyStore(keydir, n, p)
_, err = kStore.ImportECDSA(privKey, "")
if err != nil {
return nil, nil, err
}
acc := kStore.Accounts()[0]
err = kStore.Unlock(acc, "")
if err != nil {
return nil, nil, err
}
// proceed to authorize the local account manager in any case
ethBackend.AccountManager().AddBackend(kStore)
err = stack.Start()
return stack, ethBackend, err
}

View file

@ -1,21 +1,27 @@
//go:build integration
// +build integration
package bor
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"io"
"math/big"
"os"
"sync"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/consensus/bor"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/heimdall/checkpoint"
@ -26,11 +32,337 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests/bor/mocks"
)
var (
// addr1 = 0x71562b71999873DB5b286dF957af199Ec94617F7
pkey1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// addr2 = 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791
pkey2, _ = crypto.HexToECDSA("9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3")
)
func TestValidatorWentOffline(t *testing.T) {
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
fdlimit.Raise(2048)
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
// Create an Ethash network based off of the Ropsten config
// Generate a batch of accounts to seal and fund with
genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8)
var (
stacks []*node.Node
nodes []*eth.Ethereum
enodes []*enode.Node
)
for i := 0; i < 2; i++ {
// Start the node and wait until it's up
stack, ethBackend, err := InitMiner(genesis, keys[i], true)
if err != nil {
panic(err)
}
defer stack.Close()
for stack.Server().NodeInfo().Ports.Listener == 0 {
time.Sleep(250 * time.Millisecond)
}
// Connect the node to all the previous ones
for _, n := range enodes {
stack.Server().AddPeer(n)
}
// Start tracking the node and its enode
stacks = append(stacks, stack)
nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self())
}
// Iterate over all the nodes and start mining
time.Sleep(3 * time.Second)
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
panic(err)
}
}
for {
// for block 1 to 8, the primary validator is node0
// for block 9 to 16, the primary validator is node1
// for block 17 to 24, the primary validator is node0
// for block 25 to 32, the primary validator is node1
blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader()
// we remove peer connection between node0 and node1
if blockHeaderVal0.Number.Uint64() == 9 {
stacks[0].Server().RemovePeer(enodes[1])
}
// here, node1 is the primary validator, node0 will sign out-of-turn
// we add peer connection between node1 and node0
if blockHeaderVal0.Number.Uint64() == 14 {
stacks[0].Server().AddPeer(enodes[1])
}
// reorg happens here, node1 has higher difficulty, it will replace blocks by node0
if blockHeaderVal0.Number.Uint64() == 30 {
break
}
}
// check block 10 miner ; expected author is node1 signer
blockHeaderVal0 := nodes[0].BlockChain().GetHeaderByNumber(10)
blockHeaderVal1 := nodes[1].BlockChain().GetHeaderByNumber(10)
authorVal0, err := nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err := nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 10
assert.Equal(t, authorVal0, authorVal1)
// check node0 has block mined by node1
assert.Equal(t, authorVal0, nodes[1].AccountManager().Accounts()[0])
// check node1 has block mined by node1
assert.Equal(t, authorVal1, nodes[1].AccountManager().Accounts()[0])
// check block 11 miner ; expected author is node1 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(11)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(11)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 11
assert.Equal(t, authorVal0, authorVal1)
// check node0 has block mined by node1
assert.Equal(t, authorVal0, nodes[1].AccountManager().Accounts()[0])
// check node1 has block mined by node1
assert.Equal(t, authorVal1, nodes[1].AccountManager().Accounts()[0])
// check block 12 miner ; expected author is node1 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(12)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(12)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 12
assert.Equal(t, authorVal0, authorVal1)
// check node0 has block mined by node1
assert.Equal(t, authorVal0, nodes[1].AccountManager().Accounts()[0])
// check node1 has block mined by node1
assert.Equal(t, authorVal1, nodes[1].AccountManager().Accounts()[0])
// check block 17 miner ; expected author is node0 signer
blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(17)
blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(17)
authorVal0, err = nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
log.Error("Error in getting author", "err", err)
}
authorVal1, err = nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
log.Error("Error in getting author", "err", err)
}
// check both nodes have the same block 17
assert.Equal(t, authorVal0, authorVal1)
// check node0 has block mined by node1
assert.Equal(t, authorVal0, nodes[0].AccountManager().Accounts()[0])
// check node1 has block mined by node1
assert.Equal(t, authorVal1, nodes[0].AccountManager().Accounts()[0])
}
func TestForkWithBlockTime(t *testing.T) {
cases := []struct {
name string
sprint map[string]uint64
blockTime map[string]uint64
change uint64
producerDelay map[string]uint64
forkExpected bool
}{
{
name: "No fork after 2 sprints with producer delay = max block time",
sprint: map[string]uint64{
"0": 128,
},
blockTime: map[string]uint64{
"0": 5,
"128": 2,
"256": 8,
},
change: 2,
producerDelay: map[string]uint64{
"0": 8,
},
forkExpected: false,
},
{
name: "No Fork after 1 sprint producer delay = max block time",
sprint: map[string]uint64{
"0": 64,
},
blockTime: map[string]uint64{
"0": 5,
"64": 2,
},
change: 1,
producerDelay: map[string]uint64{
"0": 5,
},
forkExpected: false,
},
{
name: "Fork after 4 sprints with producer delay < max block time",
sprint: map[string]uint64{
"0": 16,
},
blockTime: map[string]uint64{
"0": 2,
"64": 5,
},
change: 4,
producerDelay: map[string]uint64{
"0": 4,
},
forkExpected: true,
},
}
// Create an Ethash network based off of the Ropsten config
// Generate a batch of accounts to seal and fund with
faucets := make([]*ecdsa.PrivateKey, 128)
for i := 0; i < len(faucets); i++ {
faucets[i], _ = crypto.GenerateKey()
}
genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8)
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
genesis.Config.Bor.Sprint = test.sprint
genesis.Config.Bor.Period = test.blockTime
genesis.Config.Bor.BackupMultiplier = test.blockTime
genesis.Config.Bor.ProducerDelay = test.producerDelay
stacks, nodes, _ := setupMiner(t, 2, genesis)
defer func() {
for _, stack := range stacks {
stack.Close()
}
}()
// Iterate over all the nodes and start mining
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
t.Fatal("Error occured while starting miner", "node", node, "error", err)
}
}
var wg sync.WaitGroup
blockHeaders := make([]*types.Header, 2)
ticker := time.NewTicker(time.Duration(test.blockTime["0"]) * time.Second)
defer ticker.Stop()
for i := 0; i < 2; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
for range ticker.C {
blockHeaders[i] = nodes[i].BlockChain().GetHeaderByNumber(test.sprint["0"]*test.change + 10)
if blockHeaders[i] != nil {
break
}
}
}(i)
}
wg.Wait()
// Before the end of sprint
blockHeaderVal0 := nodes[0].BlockChain().GetHeaderByNumber(test.sprint["0"] - 1)
blockHeaderVal1 := nodes[1].BlockChain().GetHeaderByNumber(test.sprint["0"] - 1)
assert.Equal(t, blockHeaderVal0.Hash(), blockHeaderVal1.Hash())
assert.Equal(t, blockHeaderVal0.Time, blockHeaderVal1.Time)
author0, err := nodes[0].Engine().Author(blockHeaderVal0)
if err != nil {
t.Error("Error occured while fetching author", "err", err)
}
author1, err := nodes[1].Engine().Author(blockHeaderVal1)
if err != nil {
t.Error("Error occured while fetching author", "err", err)
}
assert.Equal(t, author0, author1)
// After the end of sprint
author2, err := nodes[0].Engine().Author(blockHeaders[0])
if err != nil {
t.Error("Error occured while fetching author", "err", err)
}
author3, err := nodes[1].Engine().Author(blockHeaders[1])
if err != nil {
t.Error("Error occured while fetching author", "err", err)
}
if test.forkExpected {
assert.NotEqual(t, blockHeaders[0].Hash(), blockHeaders[1].Hash())
assert.NotEqual(t, blockHeaders[0].Time, blockHeaders[1].Time)
assert.NotEqual(t, author2, author3)
} else {
assert.Equal(t, blockHeaders[0].Hash(), blockHeaders[1].Hash())
assert.Equal(t, blockHeaders[0].Time, blockHeaders[1].Time)
assert.Equal(t, author2, author3)
}
})
}
}
func TestInsertingSpanSizeBlocks(t *testing.T) {
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
chain := init.ethereum.BlockChain()
@ -341,13 +673,13 @@ func TestSignerNotFound(t *testing.T) {
// TestEIP1559Transition tests the following:
//
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
// 3. Only the transaction's tip will be received by the coinbase.
// 4. The transaction sender pays for both the tip and baseFee.
// 5. The coinbase receives only the partially realized tip when
// gasFeeCap - gasTipCap < baseFee.
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
// 3. Only the transaction's tip will be received by the coinbase.
// 4. The transaction sender pays for both the tip and baseFee.
// 5. The coinbase receives only the partially realized tip when
// gasFeeCap - gasTipCap < baseFee.
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
func TestEIP1559Transition(t *testing.T) {
var (
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
@ -741,11 +1073,11 @@ func TestJaipurFork(t *testing.T) {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators)
insertNewBlock(t, chain, block)
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock-1 {
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock.Uint64()-1 {
require.Equal(t, testSealHash(block.Header(), init.genesis.Config.Bor), bor.SealHash(block.Header(), init.genesis.Config.Bor))
}
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock {
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock.Uint64() {
require.Equal(t, testSealHash(block.Header(), init.genesis.Config.Bor), bor.SealHash(block.Header(), init.genesis.Config.Bor))
}
}
@ -777,7 +1109,7 @@ func testEncodeSigHeader(w io.Writer, header *types.Header, c *params.BorConfig)
header.MixDigest,
header.Nonce,
}
if c.IsJaipur(header.Number.Uint64()) {
if c.IsJaipur(header.Number) {
if header.BaseFee != nil {
enc = append(enc, header.BaseFee)
}

View file

@ -3,6 +3,8 @@
package bor
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"encoding/json"
"fmt"
@ -15,6 +17,7 @@ import (
"github.com/golang/mock/gomock"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
@ -31,7 +34,13 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/tests/bor/mocks"
)
@ -45,6 +54,8 @@ var (
// This account is one the validators for 1st span (0-indexed)
key2, _ = crypto.HexToECDSA(privKey2)
addr2 = crypto.PubkeyToAddress(key2.PublicKey) // 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791
keys = []*ecdsa.PrivateKey{key, key2}
)
const (
@ -65,6 +76,39 @@ type initializeData struct {
ethereum *eth.Ethereum
}
func setupMiner(t *testing.T, n int, genesis *core.Genesis) ([]*node.Node, []*eth.Ethereum, []*enode.Node) {
t.Helper()
// Create an Ethash network based off of the Ropsten config
var (
stacks []*node.Node
nodes []*eth.Ethereum
enodes []*enode.Node
)
for i := 0; i < n; i++ {
// Start the node and wait until it's up
stack, ethBackend, err := InitMiner(genesis, keys[i], true)
if err != nil {
t.Fatal("Error occured while initialising miner", "error", err)
}
for stack.Server().NodeInfo().Ports.Listener == 0 {
time.Sleep(250 * time.Millisecond)
}
// Connect the node to all the previous ones
for _, n := range enodes {
stack.Server().AddPeer(n)
}
// Start tracking the node and its enode
stacks = append(stacks, stack)
nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self())
}
return stacks, nodes, enodes
}
func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
genesisData, err := ioutil.ReadFile("./testdata/genesis.json")
if err != nil {
@ -172,8 +216,10 @@ func buildNextBlock(t *testing.T, _bor consensus.Engine, chain *core.BlockChain,
b.addTxWithChain(chain, state, tx, addr)
}
ctx := context.Background()
// Finalize and seal the block
block, _ := _bor.FinalizeAndAssemble(chain, b.header, state, b.txs, nil, b.receipts)
block, _ := _bor.FinalizeAndAssemble(ctx, chain, b.header, state, b.txs, nil, b.receipts)
// Write state changes to db
root, err := state.Commit(chain.Config().IsEIP158(b.header.Number))
@ -187,7 +233,7 @@ func buildNextBlock(t *testing.T, _bor consensus.Engine, chain *core.BlockChain,
res := make(chan *types.Block, 1)
err = _bor.Seal(chain, block, res, nil)
err = _bor.Seal(ctx, chain, block, res, nil)
if err != nil {
// an error case - sign manually
sign(t, header, signer, borConfig)
@ -359,3 +405,95 @@ func IsSprintStart(number uint64) bool {
func IsSprintEnd(number uint64) bool {
return (number+1)%sprintSize == 0
}
func InitGenesis(t *testing.T, faucets []*ecdsa.PrivateKey, fileLocation string, sprintSize uint64) *core.Genesis {
t.Helper()
// sprint size = 8 in genesis
genesisData, err := ioutil.ReadFile(fileLocation)
if err != nil {
t.Fatalf("%s", err)
}
genesis := &core.Genesis{}
if err := json.Unmarshal(genesisData, genesis); err != nil {
t.Fatalf("%s", err)
}
genesis.Config.ChainID = big.NewInt(15001)
genesis.Config.EIP150Hash = common.Hash{}
genesis.Config.Bor.Sprint["0"] = sprintSize
return genesis
}
func InitMiner(genesis *core.Genesis, privKey *ecdsa.PrivateKey, withoutHeimdall bool) (*node.Node, *eth.Ethereum, error) {
// Define the basic configurations for the Ethereum node
datadir, _ := ioutil.TempDir("", "")
config := &node.Config{
Name: "geth",
Version: params.Version,
DataDir: datadir,
P2P: p2p.Config{
ListenAddr: "0.0.0.0:0",
NoDiscovery: true,
MaxPeers: 25,
},
UseLightweightKDF: true,
}
// Create the node and configure a full Ethereum node on it
stack, err := node.New(config)
if err != nil {
return nil, nil, err
}
ethBackend, err := eth.New(stack, &ethconfig.Config{
Genesis: genesis,
NetworkId: genesis.Config.ChainID.Uint64(),
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
TxPool: core.DefaultTxPoolConfig,
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{
Etherbase: crypto.PubkeyToAddress(privKey.PublicKey),
GasCeil: genesis.GasLimit * 11 / 10,
GasPrice: big.NewInt(1),
Recommit: time.Second,
},
WithoutHeimdall: withoutHeimdall,
})
if err != nil {
return nil, nil, err
}
// register backend to account manager with keystore for signing
keydir := stack.KeyStoreDir()
n, p := keystore.StandardScryptN, keystore.StandardScryptP
kStore := keystore.NewKeyStore(keydir, n, p)
_, err = kStore.ImportECDSA(privKey, "")
if err != nil {
return nil, nil, err
}
acc := kStore.Accounts()[0]
err = kStore.Unlock(acc, "")
if err != nil {
return nil, nil, err
}
// proceed to authorize the local account manager in any case
ethBackend.AccountManager().AddBackend(kStore)
err = stack.Start()
return stack, ethBackend, err
}

View file

@ -15,11 +15,17 @@
"londonBlock": 1,
"bor": {
"jaipurBlock": 2,
"delhiBlock" :3,
"period": {
"0": 1
},
"producerDelay": 4,
"sprint": 4,
"producerDelay": {
"0": 6
},
"sprint": {
"0": 4,
"32": 2
},
"backupMultiplier": {
"0": 1
},

126
tests/bor/testdata/genesis_21val.json vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -15,11 +15,16 @@
"londonBlock": 1,
"bor": {
"jaipurBlock": 2,
"delhiBlock" :3,
"period": {
"0": 1
},
"producerDelay": 4,
"sprint": 8,
"producerDelay": {
"0": 4
},
"sprint": {
"0": 8
},
"backupMultiplier": {
"0": 1
},
@ -61,4 +66,3 @@
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

83
tests/bor/testdata/genesis_7val.json vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -24,6 +24,8 @@ import (
"strconv"
"strings"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
@ -37,7 +39,6 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
// StateTest checks transaction processing without block context.
@ -217,15 +218,16 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
// Prepare the EVM.
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
context.GetHash = vmTestBlockHash
context.BaseFee = baseFee
evmContext := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
evmContext.GetHash = vmTestBlockHash
evmContext.BaseFee = baseFee
if t.json.Env.Random != nil {
rnd := common.BigToHash(t.json.Env.Random)
context.Random = &rnd
context.Difficulty = big.NewInt(0)
evmContext.Random = &rnd
evmContext.Difficulty = big.NewInt(0)
}
evm := vm.NewEVM(context, txContext, statedb, config, vmconfig)
evm := vm.NewEVM(evmContext, txContext, statedb, config, vmconfig)
// Execute the message.
snapshot := statedb.Snapshot()
gaspool := new(core.GasPool)