Merge remote-tracking branch 'upstream/develop' into block-stm

This commit is contained in:
Jerry 2023-05-01 11:10:34 -07:00
commit a093c4e7df
No known key found for this signature in database
GPG key ID: 5B33FA23CB103211
54 changed files with 1398 additions and 505 deletions

View file

@ -61,6 +61,116 @@ jobs:
- name: Copying systemd file - name: Copying systemd file
run: cp -rp packaging/templates/systemd/bor.service packaging/deb/bor/lib/systemd/system/bor.service run: cp -rp packaging/templates/systemd/bor.service packaging/deb/bor/lib/systemd/system/bor.service
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} bootnode for ${{ env.ARCH }}
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env:
ARCH: amd64
NODE: bootnode
NETWORK: any
- name: Putting toml for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/testnet-v4/sentry/sentry/bor/config.toml packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/var/lib/bor/
env:
ARCH: amd64
NODE: bootnode
NETWORK: mumbai
- name: Copying the preinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/preinst packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/preinst
env:
ARCH: amd64
NODE: bootnode
NETWORK: any
- name: Copying the postinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/postinst.profile packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postinst
env:
ARCH: amd64
NODE: bootnode
NETWORK: any
- name: Copying the prerm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/prerm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/prerm
env:
ARCH: amd64
NODE: bootnode
NETWORK: any
- name: Copying the postrm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/postrm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postrm
env:
ARCH: amd64
NODE: bootnode
NETWORK: any
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
env:
ARCH: amd64
NODE: bootnode
NETWORK: any
- name: Copying profile control file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/control.profile.amd64 packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/control
env:
ARCH: amd64
NODE: bootnode
NETWORK: mumbai
- name: Running package build for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: dpkg-deb --build --root-owner-group packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env:
ARCH: amd64
NODE: bootnode
NETWORK: mumbai
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} bootnode for ${{ env.ARCH }}
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Putting toml for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/var/lib/bor/
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Copying the preinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/preinst packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/preinst
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Copying the postinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/postinst.profile packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postinst
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Copying the prerm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/prerm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/prerm
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Copying the postrm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/postrm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postrm
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Copying profile control file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/control.profile.amd64 packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/control
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Running package build for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: dpkg-deb --build --root-owner-group packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env:
ARCH: amd64
NODE: bootnode
NETWORK: mainnet
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} node for ${{ env.ARCH }} - name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} node for ${{ env.ARCH }}
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }} run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env: env:
@ -416,6 +526,116 @@ jobs:
- name: Updating the control file to use with the arm64 profile - name: Updating the control file to use with the arm64 profile
run: cp -rp packaging/templates/package_scripts/control.profile.arm64 packaging/deb/bor/DEBIAN/control run: cp -rp packaging/templates/package_scripts/control.profile.arm64 packaging/deb/bor/DEBIAN/control
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} bootnode for ${{ env.ARCH }}
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Putting toml for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/testnet-v4/sentry/sentry/bor/config.toml packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/var/lib/bor/
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Copying the preinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/preinst packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/preinst
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Copying the postinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/postinst.profile packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postinst
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Copying the prerm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/prerm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/prerm
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Copying the postrm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/postrm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postrm
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Copying profile control file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/control.profile.amd64 packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/control
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Running package build for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: dpkg-deb --build --root-owner-group packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env:
ARCH: arm64
NODE: bootnode
NETWORK: mumbai
- name: Prepping ${{ env.NETWORK }} ${{ env.NODE }} bootnode for ${{ env.ARCH }}
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Putting toml for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/mainnet-v1/sentry/sentry/bor/config.toml packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/var/lib/bor/
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Copying the preinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/preinst packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/preinst
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Copying the postinst for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/postinst.profile packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postinst
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Copying the prerm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/prerm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/prerm
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Copying the postrm for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/postrm packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/postrm
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Copying systemd file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/systemd/bor_bootnode.service packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/lib/systemd/system/
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Copying profile control file for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/templates/package_scripts/control.profile.amd64 packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}/DEBIAN/control
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Running package build for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: dpkg-deb --build --root-owner-group packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env:
ARCH: arm64
NODE: bootnode
NETWORK: mainnet
- name: Setting up bor for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }} - name: Setting up bor for ${{ env.NODE }} on ${{ env.NETWORK }} on ${{ env.ARCH }}
run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }} run: cp -rp packaging/deb/bor packaging/deb/bor-${{ env.NETWORK }}-${{ env.NODE }}-config_${{ env.GIT_TAG }}-${{ env.ARCH }}
env: env:

View file

@ -637,8 +637,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
// about the transaction and calling mechanisms. // about the transaction and calling mechanisms.
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true}) vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
gasPool := new(core.GasPool).AddGas(math.MaxUint64) gasPool := new(core.GasPool).AddGas(math.MaxUint64)
// nolint : contextcheck
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb() return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb(context.Background())
} }
// SendTransaction updates the pending block to include the given transaction. // SendTransaction updates the pending block to include the given transaction.

View file

@ -173,7 +173,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
// (ret []byte, usedGas uint64, failed bool, err error) // (ret []byte, usedGas uint64, failed bool, err error)
msgResult, err := core.ApplyMessage(evm, msg, gaspool) msgResult, err := core.ApplyMessage(evm, msg, gaspool, nil)
if err != nil { if err != nil {
statedb.RevertToSnapshot(snapshot) statedb.RevertToSnapshot(snapshot)
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err) log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err)

View file

@ -75,13 +75,34 @@ func rankMapDifficulties(values map[common.Address]uint64) []difficultiesKV {
} }
// GetSnapshotProposerSequence retrieves the in-turn signers of all sprints in a span // GetSnapshotProposerSequence retrieves the in-turn signers of all sprints in a span
func (api *API) GetSnapshotProposerSequence(number *rpc.BlockNumber) (BlockSigners, error) { func (api *API) GetSnapshotProposerSequence(blockNrOrHash *rpc.BlockNumberOrHash) (BlockSigners, error) {
snapNumber := *number - 1 var header *types.Header
//nolint:nestif
if blockNrOrHash == nil {
header = api.chain.CurrentHeader()
} else {
if blockNr, ok := blockNrOrHash.Number(); ok {
if blockNr == rpc.LatestBlockNumber {
header = api.chain.CurrentHeader()
} else {
header = api.chain.GetHeaderByNumber(uint64(blockNr))
}
} else {
if blockHash, ok := blockNrOrHash.Hash(); ok {
header = api.chain.GetHeaderByHash(blockHash)
}
}
}
if header == nil {
return BlockSigners{}, errUnknownBlock
}
snapNumber := rpc.BlockNumber(header.Number.Int64() - 1)
snap, err := api.GetSnapshot(&snapNumber)
var difficulties = make(map[common.Address]uint64) var difficulties = make(map[common.Address]uint64)
snap, err := api.GetSnapshot(&snapNumber)
if err != nil { if err != nil {
return BlockSigners{}, err return BlockSigners{}, err
} }
@ -101,7 +122,7 @@ func (api *API) GetSnapshotProposerSequence(number *rpc.BlockNumber) (BlockSigne
rankedDifficulties := rankMapDifficulties(difficulties) rankedDifficulties := rankMapDifficulties(difficulties)
author, err := api.GetAuthor(number) author, err := api.GetAuthor(blockNrOrHash)
if err != nil { if err != nil {
return BlockSigners{}, err return BlockSigners{}, err
} }
@ -117,9 +138,31 @@ func (api *API) GetSnapshotProposerSequence(number *rpc.BlockNumber) (BlockSigne
} }
// GetSnapshotProposer retrieves the in-turn signer at a given block. // GetSnapshotProposer retrieves the in-turn signer at a given block.
func (api *API) GetSnapshotProposer(number *rpc.BlockNumber) (common.Address, error) { func (api *API) GetSnapshotProposer(blockNrOrHash *rpc.BlockNumberOrHash) (common.Address, error) {
*number -= 1 var header *types.Header
snap, err := api.GetSnapshot(number) //nolint:nestif
if blockNrOrHash == nil {
header = api.chain.CurrentHeader()
} else {
if blockNr, ok := blockNrOrHash.Number(); ok {
if blockNr == rpc.LatestBlockNumber {
header = api.chain.CurrentHeader()
} else {
header = api.chain.GetHeaderByNumber(uint64(blockNr))
}
} else {
if blockHash, ok := blockNrOrHash.Hash(); ok {
header = api.chain.GetHeaderByHash(blockHash)
}
}
}
if header == nil {
return common.Address{}, errUnknownBlock
}
snapNumber := rpc.BlockNumber(header.Number.Int64() - 1)
snap, err := api.GetSnapshot(&snapNumber)
if err != nil { if err != nil {
return common.Address{}, err return common.Address{}, err
@ -129,14 +172,26 @@ func (api *API) GetSnapshotProposer(number *rpc.BlockNumber) (common.Address, er
} }
// GetAuthor retrieves the author a block. // GetAuthor retrieves the author a block.
func (api *API) GetAuthor(number *rpc.BlockNumber) (*common.Address, error) { func (api *API) GetAuthor(blockNrOrHash *rpc.BlockNumberOrHash) (*common.Address, error) {
// Retrieve the requested block number (or current if none requested) // Retrieve the requested block number (or current if none requested)
var header *types.Header var header *types.Header
if number == nil || *number == rpc.LatestBlockNumber {
//nolint:nestif
if blockNrOrHash == nil {
header = api.chain.CurrentHeader() header = api.chain.CurrentHeader()
} else { } else {
header = api.chain.GetHeaderByNumber(uint64(number.Int64())) if blockNr, ok := blockNrOrHash.Number(); ok {
header = api.chain.GetHeaderByNumber(uint64(blockNr))
if blockNr == rpc.LatestBlockNumber {
header = api.chain.CurrentHeader()
} }
} else {
if blockHash, ok := blockNrOrHash.Hash(); ok {
header = api.chain.GetHeaderByHash(blockHash)
}
}
}
// Ensure we have an actually valid block and return its snapshot // Ensure we have an actually valid block and return its snapshot
if header == nil { if header == nil {
return nil, errUnknownBlock return nil, errUnknownBlock

View file

@ -83,6 +83,7 @@ func ApplyMessage(
msg.Data(), msg.Data(),
msg.Gas(), msg.Gas(),
msg.Value(), msg.Value(),
nil,
) )
// Update the state with pending changes // Update the state with pending changes
if err != nil { if err != nil {
@ -104,6 +105,7 @@ func ApplyBorMessage(vmenv vm.EVM, msg Callmsg) (*core.ExecutionResult, error) {
msg.Data(), msg.Data(),
msg.Gas(), msg.Gas(),
msg.Value(), msg.Value(),
nil,
) )
// Update the state with pending changes // Update the state with pending changes
if err != nil { if err != nil {

View file

@ -105,7 +105,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
b.SetCoinbase(common.Address{}) b.SetCoinbase(common.Address{})
} }
b.statedb.Prepare(tx.Hash(), len(b.txs)) b.statedb.Prepare(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -117,7 +117,7 @@ func (task *ExecutionTask) Execute(mvh *blockstm.MVHashMap, incarnation int) (er
// Apply the transaction to the current state (included in the env). // Apply the transaction to the current state (included in the env).
if *task.shouldDelayFeeCal { if *task.shouldDelayFeeCal {
task.result, err = ApplyMessageNoFeeBurnOrTip(evm, task.msg, new(GasPool).AddGas(task.gasLimit)) task.result, err = ApplyMessageNoFeeBurnOrTip(evm, task.msg, new(GasPool).AddGas(task.gasLimit), nil)
if task.result == nil || err != nil { if task.result == nil || err != nil {
return blockstm.ErrExecAbortError{Dependency: task.statedb.DepTxIndex(), OriginError: err} return blockstm.ErrExecAbortError{Dependency: task.statedb.DepTxIndex(), OriginError: err}
@ -137,7 +137,7 @@ func (task *ExecutionTask) Execute(mvh *blockstm.MVHashMap, incarnation int) (er
task.shouldRerunWithoutFeeDelay = true task.shouldRerunWithoutFeeDelay = true
} }
} else { } else {
task.result, err = ApplyMessage(evm, task.msg, new(GasPool).AddGas(task.gasLimit)) task.result, err = ApplyMessage(evm, task.msg, new(GasPool).AddGas(task.gasLimit), nil)
} }
if task.statedb.HadInvalidRead() || err != nil { if task.statedb.HadInvalidRead() || err != nil {

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"context"
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
@ -89,6 +90,6 @@ func precacheTransaction(msg types.Message, config *params.ChainConfig, gaspool
// Update the evm with the new transaction context. // Update the evm with the new transaction context.
evm.Reset(NewEVMTxContext(msg), statedb) evm.Reset(NewEVMTxContext(msg), statedb)
// Add addresses to access list if applicable // Add addresses to access list if applicable
_, err := ApplyMessage(evm, msg, gaspool) _, err := ApplyMessage(evm, msg, gaspool, context.Background())
return err return err
} }

View file

@ -88,7 +88,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
} }
statedb.Prepare(tx.Hash(), i) statedb.Prepare(tx.Hash(), i)
receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, nil)
if err != nil { if err != nil {
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
} }
@ -101,7 +101,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
return receipts, allLogs, *usedGas, nil return receipts, allLogs, *usedGas, nil
} }
func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { // nolint : unparam
func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, interruptCtx context.Context) (*types.Receipt, error) {
// Create a new context to be used in the EVM environment. // Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg) txContext := NewEVMTxContext(msg)
evm.Reset(txContext, statedb) evm.Reset(txContext, statedb)
@ -120,7 +121,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
// resume recording read and write // resume recording read and write
statedb.SetMVHashmap(backupMVHashMap) statedb.SetMVHashmap(backupMVHashMap)
result, err = ApplyMessageNoFeeBurnOrTip(evm, msg, gp) result, err = ApplyMessageNoFeeBurnOrTip(evm, msg, gp, interruptCtx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -151,6 +152,10 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
output2.Add(output2, result.FeeTipped), output2.Add(output2, result.FeeTipped),
) )
if result.Err == vm.ErrInterrupt {
return nil, result.Err
}
// Update the state with pending changes. // Update the state with pending changes.
var root []byte var root []byte
if config.IsByzantium(blockNumber) { if config.IsByzantium(blockNumber) {
@ -189,7 +194,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
// and uses the input parameters for its environment. It returns the receipt // and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed, // for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid. // indicating the block was invalid.
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, interruptCtx context.Context) (*types.Receipt, error) {
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee) msg, err := tx.AsMessage(types.MakeSigner(config, header.Number), header.BaseFee)
if err != nil { if err != nil {
return nil, err return nil, err
@ -197,5 +202,6 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// Create a new context to be used in the EVM environment // Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author) blockContext := NewEVMBlockContext(header, bc, author)
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, interruptCtx)
} }

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"context"
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
@ -188,15 +189,15 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
// the gas used (which includes gas refunds) and an error if it failed. An error always // the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular // indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block. // state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) { func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, interruptCtx context.Context) (*ExecutionResult, error) {
return NewStateTransition(evm, msg, gp).TransitionDb() return NewStateTransition(evm, msg, gp).TransitionDb(interruptCtx)
} }
func ApplyMessageNoFeeBurnOrTip(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) { func ApplyMessageNoFeeBurnOrTip(evm *vm.EVM, msg Message, gp *GasPool, interruptCtx context.Context) (*ExecutionResult, error) {
st := NewStateTransition(evm, msg, gp) st := NewStateTransition(evm, msg, gp)
st.noFeeBurnAndTip = true st.noFeeBurnAndTip = true
return st.TransitionDb() return st.TransitionDb(interruptCtx)
} }
// to returns the recipient of the message. // to returns the recipient of the message.
@ -290,7 +291,7 @@ func (st *StateTransition) preCheck() error {
// //
// However if any consensus issue encountered, return the error directly with // However if any consensus issue encountered, return the error directly with
// nil evm execution result. // nil evm execution result.
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*ExecutionResult, error) {
input1 := st.state.GetBalance(st.msg.From()) input1 := st.state.GetBalance(st.msg.From())
var input2 *big.Int var input2 *big.Int
@ -348,7 +349,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
} else { } else {
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value) ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value, interruptCtx)
} }
if !london { if !london {

View file

@ -1815,7 +1815,7 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
chainConfig.LondonBlock = big.NewInt(0) chainConfig.LondonBlock = big.NewInt(0)
_, back, closeFn := miner.NewTestWorker(t, chainConfig, engine, db, 0, 0, 0) _, back, closeFn := miner.NewTestWorker(t, chainConfig, engine, db, 0, 0, 0, 0)
defer closeFn() defer closeFn()
genesis := back.BlockChain().Genesis() genesis := back.BlockChain().Genesis()

View file

@ -17,6 +17,7 @@
package vm package vm
import ( import (
"context"
"math/big" "math/big"
"sync/atomic" "sync/atomic"
"time" "time"
@ -165,7 +166,7 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
// parameters. It also handles any necessary value transfer required and takes // parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an // the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer. // execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, interruptCtx context.Context) (ret []byte, leftOverGas uint64, err error) {
// Fail if we're trying to execute above the call depth limit // Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth return nil, gas, ErrDepth
@ -225,7 +226,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// The depth-check is already done, and precompiles handled above // The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas) contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.PreRun(contract, input, false, interruptCtx)
gas = contract.Gas gas = contract.Gas
} }
} }
@ -282,7 +283,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// The contract is a scoped environment for this execution context only. // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(caller.Address()), value, gas) contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.PreRun(contract, input, false, nil)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -322,7 +323,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// Initialise a new contract and make initialise the delegate values // Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.PreRun(contract, input, false, nil)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -378,7 +379,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors. // when we're in Homestead this also counts for code storage gas errors.
ret, err = evm.interpreter.Run(contract, input, true) ret, err = evm.interpreter.PreRun(contract, input, true, nil)
gas = contract.Gas gas = contract.Gas
} }
if err != nil { if err != nil {
@ -450,7 +451,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
start := time.Now() start := time.Now()
ret, err := evm.interpreter.Run(contract, nil, false) ret, err := evm.interpreter.PreRun(contract, nil, false, nil)
// Check whether the max code size has been exceeded, assign err if the case. // Check whether the max code size has been exceeded, assign err if the case.
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {

View file

@ -93,7 +93,7 @@ func TestEIP2200(t *testing.T) {
} }
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int)) _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int), nil)
if err != tt.failure { if err != tt.failure {
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
} }

View file

@ -392,16 +392,21 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
// opExtCodeHash returns the code hash of a specified account. // opExtCodeHash returns the code hash of a specified account.
// There are several cases when the function is called, while we can relay everything // There are several cases when the function is called, while we can relay everything
// to `state.GetCodeHash` function to ensure the correctness. // to `state.GetCodeHash` function to ensure the correctness.
//
// (1) Caller tries to get the code hash of a normal contract account, state // (1) Caller tries to get the code hash of a normal contract account, state
//
// should return the relative code hash and set it as the result. // should return the relative code hash and set it as the result.
// //
// (2) Caller tries to get the code hash of a non-existent account, state should // (2) Caller tries to get the code hash of a non-existent account, state should
//
// return common.Hash{} and zero will be set as the result. // return common.Hash{} and zero will be set as the result.
// //
// (3) Caller tries to get the code hash for an account without contract code, // (3) Caller tries to get the code hash for an account without contract code,
//
// state should return emptyCodeHash(0xc5d246...) as the result. // state should return emptyCodeHash(0xc5d246...) as the result.
// //
// (4) Caller tries to get the code hash of a precompiled account, the result // (4) Caller tries to get the code hash of a precompiled account, the result
//
// should be zero or emptyCodeHash. // should be zero or emptyCodeHash.
// //
// It is worth noting that in order to avoid unnecessary create and clean, // It is worth noting that in order to avoid unnecessary create and clean,
@ -411,9 +416,11 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
// customized chain, the return value will be zero. // customized chain, the return value will be zero.
// //
// (5) Caller tries to get the code hash for an account which is marked as suicided // (5) Caller tries to get the code hash for an account which is marked as suicided
//
// in the current transaction, the code hash of this account should be returned. // in the current transaction, the code hash of this account should be returned.
// //
// (6) Caller tries to get the code hash for an account which is marked as deleted, // (6) Caller tries to get the code hash for an account which is marked as deleted,
//
// this account should be regarded as a non-existent account and zero should be returned. // this account should be regarded as a non-existent account and zero should be returned.
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek() slot := scope.Stack.peek()
@ -688,7 +695,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
bigVal = value.ToBig() bigVal = value.ToBig()
} }
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal) ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal, nil)
if err != nil { if err != nil {
temp.Clear() temp.Clear()

View file

@ -17,11 +17,33 @@
package vm package vm
import ( import (
"context"
"errors"
"hash" "hash"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
lru "github.com/hashicorp/golang-lru"
)
var (
opcodeCommitInterruptCounter = metrics.NewRegisteredCounter("worker/opcodeCommitInterrupt", nil)
ErrInterrupt = errors.New("EVM execution interrupted")
ErrNoCache = errors.New("no tx cache found")
ErrNoCurrentTx = errors.New("no current tx found in interruptCtx")
)
const (
// These are keys for the interruptCtx
InterruptCtxDelayKey = "delay"
InterruptCtxOpcodeDelayKey = "opcodeDelay"
// InterruptedTxCacheSize is size of lru cache for interrupted txs
InterruptedTxCacheSize = 90000
) )
// Config are the configuration options for the Interpreter // Config are the configuration options for the Interpreter
@ -68,6 +90,54 @@ type EVMInterpreter struct {
returnData []byte // Last CALL's return data for subsequent reuse returnData []byte // Last CALL's return data for subsequent reuse
} }
// TxCacher is an wrapper of lru.cache for caching transactions that get interrupted
type TxCache struct {
Cache *lru.Cache
}
type txCacheKey struct{}
type InterruptedTxContext_currenttxKey struct{}
// SetCurrentTxOnContext sets the current tx on the context
func SetCurrentTxOnContext(ctx context.Context, txHash common.Hash) context.Context {
return context.WithValue(ctx, InterruptedTxContext_currenttxKey{}, txHash)
}
// GetCurrentTxFromContext gets the current tx from the context
func GetCurrentTxFromContext(ctx context.Context) (common.Hash, error) {
val := ctx.Value(InterruptedTxContext_currenttxKey{})
if val == nil {
return common.Hash{}, ErrNoCurrentTx
}
c, ok := val.(common.Hash)
if !ok {
return common.Hash{}, ErrNoCurrentTx
}
return c, nil
}
// GetCache returns the txCache from the context
func GetCache(ctx context.Context) (*TxCache, error) {
val := ctx.Value(txCacheKey{})
if val == nil {
return nil, ErrNoCache
}
c, ok := val.(*TxCache)
if !ok {
return nil, ErrNoCache
}
return c, nil
}
// PutCache puts the txCache into the context
func PutCache(ctx context.Context, cache *TxCache) context.Context {
return context.WithValue(ctx, txCacheKey{}, cache)
}
// NewEVMInterpreter returns a new instance of the Interpreter. // NewEVMInterpreter returns a new instance of the Interpreter.
func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// If jump table was not initialised we set the default one. // If jump table was not initialised we set the default one.
@ -111,14 +181,192 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
} }
} }
// PreRun is a wrapper around Run that allows for a delay to be injected before each opcode when induced by tests else it calls the lagace Run() method
func (in *EVMInterpreter) PreRun(contract *Contract, input []byte, readOnly bool, interruptCtx context.Context) (ret []byte, err error) {
var opcodeDelay interface{}
if interruptCtx != nil {
if interruptCtx.Value(InterruptCtxOpcodeDelayKey) != nil {
opcodeDelay = interruptCtx.Value(InterruptCtxOpcodeDelayKey)
}
}
if opcodeDelay != nil {
return in.RunWithDelay(contract, input, readOnly, interruptCtx, opcodeDelay.(uint))
}
return in.Run(contract, input, readOnly, interruptCtx)
}
// Run loops and evaluates the contract's code with the given input data and returns // Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred. // the return byte-slice and an error if one occurred.
// //
// It's important to note that any errors returned by the interpreter should be // It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation except for // considered a revert-and-consume-all-gas operation except for
// ErrExecutionReverted which means revert-and-keep-gas-left. // ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { // nolint: gocognit
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, interruptCtx context.Context) (ret []byte, err error) {
// Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()
// Make sure the readOnly is only set if we aren't in readOnly yet.
// This also makes sure that the readOnly flag isn't removed for child calls.
if readOnly && !in.readOnly {
in.readOnly = true
defer func() { in.readOnly = false }()
}
// Reset the previous call's return data. It's unimportant to preserve the old buffer
// as every returning call will return new data anyway.
in.returnData = nil
// Don't bother with the execution if there's no code.
if len(contract.Code) == 0 {
return nil, nil
}
var (
op OpCode // current opcode
mem = NewMemory() // bound memory
stack = newstack() // local stack
callContext = &ScopeContext{
Memory: mem,
Stack: stack,
Contract: contract,
}
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC
// to be uint256. Practically much less so feasible.
pc = uint64(0) // program counter
cost uint64
// copies used by tracer
pcCopy uint64 // needed for the deferred EVMLogger
gasCopy uint64 // for EVMLogger to log gas remaining before execution
logged bool // deferred EVMLogger should ignore already logged steps
res []byte // result of the opcode execution function
)
// Don't move this deferrred function, it's placed before the capturestate-deferred method,
// so that it get's executed _after_: the capturestate needs the stacks before
// they are returned to the pools
defer func() {
returnStack(stack)
}()
contract.Input = input
if in.cfg.Debug {
defer func() {
if err != nil {
if !logged {
in.cfg.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
} else {
in.cfg.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
}
}
}()
}
// The Interpreter main run loop (contextual). This loop runs until either an
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
// parent context.
for {
if interruptCtx != nil {
// case of interrupting by timeout
select {
case <-interruptCtx.Done():
txHash, _ := GetCurrentTxFromContext(interruptCtx)
interruptedTxCache, _ := GetCache(interruptCtx)
// if the tx is already in the cache, it means that it has been interrupted before and we will not interrupt it again
found, _ := interruptedTxCache.Cache.ContainsOrAdd(txHash, true)
if found {
interruptedTxCache.Cache.Remove(txHash)
} else {
// if the tx is not in the cache, it means that it has not been interrupted before and we will interrupt it
opcodeCommitInterruptCounter.Inc(1)
log.Warn("OPCODE Level interrupt")
return nil, ErrInterrupt
}
default:
}
}
if in.cfg.Debug {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas
}
// Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation.
op = contract.GetOp(pc)
operation := in.cfg.JumpTable[op]
cost = operation.constantGas // For tracing
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
if !contract.UseGas(cost) {
return nil, ErrOutOfGas
}
// nolint : nestif
if operation.dynamicGas != nil {
// All ops with a dynamic memory usage also has a dynamic gas cost.
var memorySize uint64
// calculate the new memory size and expand the memory to fit
// the operation
// Memory check needs to be done prior to evaluating the dynamic gas portion,
// to detect calculation overflows
if operation.memorySize != nil {
memSize, overflow := operation.memorySize(stack)
if overflow {
return nil, ErrGasUintOverflow
}
// memory is expanded in words of 32 bytes. Gas
// is also calculated in words.
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
return nil, ErrGasUintOverflow
}
}
// Consume the gas and return an error if not enough gas is available.
// cost is explicitly set so that the capture state defer method can get the proper cost
var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
cost += dynamicCost // for tracing
if err != nil || !contract.UseGas(dynamicCost) {
return nil, ErrOutOfGas
}
if memorySize > 0 {
mem.Resize(memorySize)
}
}
if in.cfg.Debug {
in.cfg.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
logged = true
}
// execute the operation
res, err = operation.execute(&pc, in, callContext)
if err != nil {
break
}
pc++
}
if err == errStopToken {
err = nil // clear stop token error
}
return res, err
}
// nolint: gocognit
// RunWithDelay is Run() with a delay between each opcode. Only used by testcases.
func (in *EVMInterpreter) RunWithDelay(contract *Contract, input []byte, readOnly bool, interruptCtx context.Context, opcodeDelay uint) (ret []byte, err error) {
// Increment the call depth which is restricted to 1024 // Increment the call depth which is restricted to 1024
in.evm.depth++ in.evm.depth++
defer func() { in.evm.depth-- }() defer func() { in.evm.depth-- }()
@ -183,6 +431,31 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// the execution of one of the operations or until the done flag is set by the // the execution of one of the operations or until the done flag is set by the
// parent context. // parent context.
for { for {
if interruptCtx != nil {
// case of interrupting by timeout
select {
case <-interruptCtx.Done():
txHash, _ := GetCurrentTxFromContext(interruptCtx)
interruptedTxCache, _ := GetCache(interruptCtx)
// if the tx is already in the cache, it means that it has been interrupted before and we will not interrupt it again
found, _ := interruptedTxCache.Cache.ContainsOrAdd(txHash, true)
log.Info("FOUND", "found", found, "txHash", txHash)
if found {
interruptedTxCache.Cache.Remove(txHash)
} else {
// if the tx is not in the cache, it means that it has not been interrupted before and we will interrupt it
opcodeCommitInterruptCounter.Inc(1)
log.Warn("OPCODE Level interrupt")
return nil, ErrInterrupt
}
default:
}
}
time.Sleep(time.Duration(opcodeDelay) * time.Millisecond)
if in.cfg.Debug { if in.cfg.Debug {
// Capture pre-execution values for tracing. // Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas logged, pcCopy, gasCopy = false, pc, contract.Gas

View file

@ -53,7 +53,7 @@ func TestLoopInterrupt(t *testing.T) {
timeout := make(chan bool) timeout := make(chan bool)
go func(evm *EVM) { go func(evm *EVM) {
_, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(big.Int)) _, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(big.Int), nil)
errChannel <- err errChannel <- err
}(evm) }(evm)

View file

@ -131,6 +131,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
input, input,
cfg.GasLimit, cfg.GasLimit,
cfg.Value, cfg.Value,
nil,
) )
return ret, cfg.State, err return ret, cfg.State, err
@ -186,6 +187,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
input, input,
cfg.GasLimit, cfg.GasLimit,
cfg.Value, cfg.Value,
nil,
) )
return ret, leftOverGas, err return ret, leftOverGas, err
} }

View file

@ -386,12 +386,15 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
//cfg.State.CreateAccount(cfg.Origin) //cfg.State.CreateAccount(cfg.Origin)
// set the receiver's (the executing contract) code for execution. // set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(destination, code) cfg.State.SetCode(destination, code)
vmenv.Call(sender, destination, nil, gas, cfg.Value)
// nolint: errcheck
vmenv.Call(sender, destination, nil, gas, cfg.Value, nil)
b.Run(name, func(b *testing.B) { b.Run(name, func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
vmenv.Call(sender, destination, nil, gas, cfg.Value) // nolint: errcheck
vmenv.Call(sender, destination, nil, gas, cfg.Value, nil)
} }
}) })
} }

View file

@ -477,10 +477,21 @@ func (f *BlockFetcher) loop() {
} }
defer req.Close() defer req.Close()
res := <-resCh timeout := time.NewTimer(2 * fetchTimeout) // 2x leeway before dropping the peer
res.Done <- nil defer timeout.Stop()
select {
case res := <-resCh:
res.Done <- nil
f.FilterHeaders(peer, *res.Res.(*eth.BlockHeadersPacket), time.Now().Add(res.Time)) f.FilterHeaders(peer, *res.Res.(*eth.BlockHeadersPacket), time.Now().Add(res.Time))
case <-timeout.C:
// The peer didn't respond in time. The request
// was already rescheduled at this point, we were
// waiting for a catchup. With an unresponsive
// peer however, it's a protocol violation.
f.dropPeer(peer)
}
}(hash) }(hash)
} }
}(peer) }(peer)
@ -523,11 +534,23 @@ func (f *BlockFetcher) loop() {
} }
defer req.Close() defer req.Close()
res := <-resCh timeout := time.NewTimer(2 * fetchTimeout) // 2x leeway before dropping the peer
defer timeout.Stop()
select {
case res := <-resCh:
res.Done <- nil res.Done <- nil
txs, uncles := res.Res.(*eth.BlockBodiesPacket).Unpack() txs, uncles := res.Res.(*eth.BlockBodiesPacket).Unpack()
f.FilterBodies(peer, txs, uncles, time.Now()) f.FilterBodies(peer, txs, uncles, time.Now())
case <-timeout.C:
// The peer didn't respond in time. The request
// was already rescheduled at this point, we were
// waiting for a catchup. With an unresponsive
// peer however, it's a protocol violation.
f.dropPeer(peer)
}
}(peer, hashes) }(peer, hashes)
} }
// Schedule the next fetch if blocks are still pending // Schedule the next fetch if blocks are still pending

View file

@ -193,9 +193,11 @@ func (eth *Ethereum) stateAtTransaction(block *types.Block, txIndex int, reexec
// Not yet the searched for transaction, execute on top of the current state // Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
statedb.Prepare(tx.Hash(), idx) statedb.Prepare(tx.Hash(), idx)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), nil); err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
} }
// Ensure any modifications are committed to the state // Ensure any modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))

View file

@ -654,7 +654,8 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
break break
} }
} else { } else {
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { // nolint : contextcheck
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), context.Background()); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) 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 // 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 // return the roots. Most likely, the caller already knows that a certain transaction fails to
@ -833,7 +834,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
break break
} }
} else { } else {
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), context.Background()); err != nil {
failed = err failed = err
break break
} }
@ -844,7 +845,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
} else { } else {
coinbaseBalance := statedb.GetBalance(blockCtx.Coinbase) coinbaseBalance := statedb.GetBalance(blockCtx.Coinbase)
result, err := core.ApplyMessageNoFeeBurnOrTip(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) result, err := core.ApplyMessageNoFeeBurnOrTip(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), context.Background())
if err != nil { if err != nil {
failed = err failed = err
@ -1036,7 +1037,8 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
} }
} }
} else { } else {
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) // nolint : contextcheck
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), context.Background())
if writer != nil { if writer != nil {
writer.Flush() writer.Flush()
} }
@ -1248,7 +1250,8 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
return nil, fmt.Errorf("tracing failed: %w", err) return nil, fmt.Errorf("tracing failed: %w", err)
} }
} else { } else {
result, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) // nolint : contextcheck
result, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), context.Background())
if err != nil { if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err) return nil, fmt.Errorf("tracing failed: %w", err)
} }

View file

@ -95,7 +95,7 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T
callmsg := prepareCallMessage(message) callmsg := prepareCallMessage(message)
execRes, err = statefull.ApplyBorMessage(*vmenv, callmsg) execRes, err = statefull.ApplyBorMessage(*vmenv, callmsg)
} else { } else {
execRes, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas())) execRes, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), nil)
} }
if err != nil { if err != nil {

View file

@ -167,14 +167,17 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
for idx, tx := range block.Transactions() { for idx, tx := range block.Transactions() {
msg, _ := tx.AsMessage(signer, block.BaseFee()) msg, _ := tx.AsMessage(signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg) txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), b.chain, nil) blockContext := core.NewEVMBlockContext(block.Header(), b.chain, nil)
if idx == txIndex { if idx == txIndex {
return msg, context, statedb, nil return msg, blockContext, statedb, nil
} }
vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { vmenv := vm.NewEVM(blockContext, txContext, statedb, b.chainConfig, vm.Config{})
// nolint : contextcheck
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), context.Background()); err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
} }
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
} }
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())

View file

@ -17,6 +17,7 @@
package tracetest package tracetest
import ( import (
"context"
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
@ -168,7 +169,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
Origin: origin, Origin: origin,
GasPrice: tx.GasPrice(), GasPrice: tx.GasPrice(),
} }
context = vm.BlockContext{ blockContext = vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
Coinbase: test.Context.Miner, Coinbase: test.Context.Miner,
@ -183,13 +184,13 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}) evm := vm.NewEVM(blockContext, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
msg, err := tx.AsMessage(signer, nil) msg, err := tx.AsMessage(signer, nil)
if err != nil { if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil { if _, err = st.TransitionDb(context.Background()); err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)
} }
// Retrieve the trace result and compare against the etalon // Retrieve the trace result and compare against the etalon
@ -279,7 +280,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
Origin: origin, Origin: origin,
GasPrice: tx.GasPrice(), GasPrice: tx.GasPrice(),
} }
context := vm.BlockContext{ blockContext := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
Coinbase: test.Context.Miner, Coinbase: test.Context.Miner,
@ -297,15 +298,19 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
if err != nil { if err != nil {
b.Fatalf("failed to create call tracer: %v", err) b.Fatalf("failed to create call tracer: %v", err)
} }
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
evm := vm.NewEVM(blockContext, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
snap := statedb.Snapshot() snap := statedb.Snapshot()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil {
if _, err = st.TransitionDb(context.Background()); err != nil {
b.Fatalf("failed to execute transaction: %v", err) b.Fatalf("failed to execute transaction: %v", err)
} }
if _, err = tracer.GetResult(); err != nil { if _, err = tracer.GetResult(); err != nil {
b.Fatal(err) b.Fatal(err)
} }
statedb.RevertToSnapshot(snap) statedb.RevertToSnapshot(snap)
} }
} }
@ -333,7 +338,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
Origin: origin, Origin: origin,
GasPrice: big.NewInt(1), GasPrice: big.NewInt(1),
} }
context := vm.BlockContext{ blockContext := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
Coinbase: common.Address{}, Coinbase: common.Address{},
@ -363,15 +368,18 @@ func TestZeroValueToNotExitCall(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }
evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
evm := vm.NewEVM(blockContext, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
msg, err := tx.AsMessage(signer, nil) msg, err := tx.AsMessage(signer, nil)
if err != nil { if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil {
if _, err = st.TransitionDb(context.Background()); err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)
} }
// Retrieve the trace result and compare against the etalon // Retrieve the trace result and compare against the etalon
res, err := tracer.GetResult() res, err := tracer.GetResult()
if err != nil { if err != nil {

View file

@ -69,7 +69,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value) tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
ret, err := env.Interpreter().Run(contract, []byte{}, false) ret, err := env.Interpreter().Run(contract, []byte{}, false, nil)
tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err) tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -59,7 +59,7 @@ func TestStoreCapture(t *testing.T) {
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
var index common.Hash var index common.Hash
logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil) logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil)
_, err := env.Interpreter().Run(contract, []byte{}, false) _, err := env.Interpreter().PreRun(contract, []byte{}, false, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -17,6 +17,7 @@
package tracers package tracers
import ( import (
"context"
"math/big" "math/big"
"testing" "testing"
@ -66,7 +67,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
Origin: from, Origin: from,
GasPrice: tx.GasPrice(), GasPrice: tx.GasPrice(),
} }
context := vm.BlockContext{ blockContext := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
Coinbase: common.Address{}, Coinbase: common.Address{},
@ -102,7 +103,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
//EnableMemory: false, //EnableMemory: false,
//EnableReturnData: false, //EnableReturnData: false,
}) })
evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer}) evm := vm.NewEVM(blockContext, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
msg, err := tx.AsMessage(signer, nil) msg, err := tx.AsMessage(signer, nil)
if err != nil { if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err) b.Fatalf("failed to prepare transaction for tracing: %v", err)
@ -113,7 +114,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
snap := statedb.Snapshot() snap := statedb.Snapshot()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
_, err = st.TransitionDb() _, err = st.TransitionDb(context.Background())
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }

View file

@ -308,6 +308,7 @@ type SealerConfig struct {
// The time interval for miner to re-create mining work. // The time interval for miner to re-create mining work.
Recommit time.Duration `hcl:"-,optional" toml:"-"` Recommit time.Duration `hcl:"-,optional" toml:"-"`
RecommitRaw string `hcl:"recommit,optional" toml:"recommit,optional"` RecommitRaw string `hcl:"recommit,optional" toml:"recommit,optional"`
CommitInterruptFlag bool `hcl:"commitinterrupt,optional" toml:"commitinterrupt,optional"`
} }
type JsonRPCConfig struct { type JsonRPCConfig struct {
@ -636,6 +637,7 @@ func DefaultConfig() *Config {
GasPrice: big.NewInt(1 * params.GWei), // geth's default GasPrice: big.NewInt(1 * params.GWei), // geth's default
ExtraData: "", ExtraData: "",
Recommit: 125 * time.Second, Recommit: 125 * time.Second,
CommitInterruptFlag: true,
}, },
Gpo: &GpoConfig{ Gpo: &GpoConfig{
Blocks: 20, Blocks: 20,
@ -928,6 +930,7 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (*
n.Miner.GasPrice = c.Sealer.GasPrice n.Miner.GasPrice = c.Sealer.GasPrice
n.Miner.GasCeil = c.Sealer.GasCeil n.Miner.GasCeil = c.Sealer.GasCeil
n.Miner.ExtraData = []byte(c.Sealer.ExtraData) n.Miner.ExtraData = []byte(c.Sealer.ExtraData)
n.Miner.CommitInterruptFlag = c.Sealer.CommitInterruptFlag
if etherbase := c.Sealer.Etherbase; etherbase != "" { if etherbase := c.Sealer.Etherbase; etherbase != "" {
if !common.IsHexAddress(etherbase) { if !common.IsHexAddress(etherbase) {

View file

@ -302,6 +302,13 @@ func (c *Command) Flags() *flagset.Flagset {
Default: c.cliConfig.Sealer.Recommit, Default: c.cliConfig.Sealer.Recommit,
Group: "Sealer", Group: "Sealer",
}) })
f.BoolFlag(&flagset.BoolFlag{
Name: "miner.interruptcommit",
Usage: "Interrupt block commit when block creation time is passed",
Value: &c.cliConfig.Sealer.CommitInterruptFlag,
Default: c.cliConfig.Sealer.CommitInterruptFlag,
Group: "Sealer",
})
// ethstats // ethstats
f.StringFlag(&flagset.StringFlag{ f.StringFlag(&flagset.StringFlag{

View file

@ -1027,7 +1027,8 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
// Execute the message. // Execute the message.
gp := new(core.GasPool).AddGas(math.MaxUint64) gp := new(core.GasPool).AddGas(math.MaxUint64)
result, err := core.ApplyMessage(evm, msg, gp) // nolint : contextcheck
result, err := core.ApplyMessage(evm, msg, gp, context.Background())
if err := vmError(); err != nil { if err := vmError(); err != nil {
return nil, err return nil, err
} }
@ -1595,13 +1596,16 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
if err != nil { if err != nil {
return nil, 0, nil, err return nil, 0, nil, err
} }
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())) // nolint : contextcheck
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), context.Background())
if err != nil { if err != nil {
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err) return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
} }
if tracer.Equal(prevTracer) { if tracer.Equal(prevTracer) {
return accessList, res.UsedGas, res.Err, nil return accessList, res.UsedGas, res.Err, nil
} }
prevTracer = tracer prevTracer = tracer
} }
} }

View file

@ -137,13 +137,14 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)} msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)}
context := core.NewEVMBlockContext(header, bc, nil) blockContext := core.NewEVMBlockContext(header, bc, nil)
txContext := core.NewEVMTxContext(msg) txContext := core.NewEVMTxContext(msg)
vmenv := vm.NewEVM(context, txContext, statedb, config, vm.Config{NoBaseFee: true}) vmenv := vm.NewEVM(blockContext, txContext, statedb, config, vm.Config{NoBaseFee: true})
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
gp := new(core.GasPool).AddGas(math.MaxUint64) gp := new(core.GasPool).AddGas(math.MaxUint64)
result, _ := core.ApplyMessage(vmenv, msg, gp) // nolint : contextcheck
result, _ := core.ApplyMessage(vmenv, msg, gp, context.Background())
res = append(res, result.Return()...) res = append(res, result.Return()...)
} }
} else { } else {
@ -151,11 +152,12 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
state := light.NewState(ctx, header, lc.Odr()) state := light.NewState(ctx, header, lc.Odr())
state.SetBalance(bankAddr, math.MaxBig256) state.SetBalance(bankAddr, math.MaxBig256)
msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)} msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)}
context := core.NewEVMBlockContext(header, lc, nil) blockContext := core.NewEVMBlockContext(header, lc, nil)
txContext := core.NewEVMTxContext(msg) txContext := core.NewEVMTxContext(msg)
vmenv := vm.NewEVM(context, txContext, state, config, vm.Config{NoBaseFee: true}) vmenv := vm.NewEVM(blockContext, txContext, state, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64) gp := new(core.GasPool).AddGas(math.MaxUint64)
result, _ := core.ApplyMessage(vmenv, msg, gp) // nolint : contextcheck
result, _ := core.ApplyMessage(vmenv, msg, gp, context.Background())
if state.Error() == nil { if state.Error() == nil {
res = append(res, result.Return()...) res = append(res, result.Return()...)
} }

View file

@ -57,14 +57,15 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.
// Assemble the transaction call message and return if the requested offset // Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer, block.BaseFee()) msg, _ := tx.AsMessage(signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg) txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil) blockContext := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil)
statedb.Prepare(tx.Hash(), idx) statedb.Prepare(tx.Hash(), idx)
if idx == txIndex { if idx == txIndex {
return msg, context, statedb, nil return msg, blockContext, statedb, nil
} }
// Not yet the searched for transaction, execute on top of the current state // Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) vmenv := vm.NewEVM(blockContext, txContext, statedb, leth.blockchain.Config(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { // nolint : contextcheck
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), context.Background()); err != nil {
return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
} }
// Ensure any modifications are committed to the state // Ensure any modifications are committed to the state

View file

@ -196,10 +196,11 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
st.SetBalance(testBankAddress, math.MaxBig256) st.SetBalance(testBankAddress, math.MaxBig256)
msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)} msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true)}
txContext := core.NewEVMTxContext(msg) txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(header, chain, nil) blockContext := core.NewEVMBlockContext(header, chain, nil)
vmenv := vm.NewEVM(context, txContext, st, config, vm.Config{NoBaseFee: true}) vmenv := vm.NewEVM(blockContext, txContext, st, config, vm.Config{NoBaseFee: true})
gp := new(core.GasPool).AddGas(math.MaxUint64) gp := new(core.GasPool).AddGas(math.MaxUint64)
result, _ := core.ApplyMessage(vmenv, msg, gp) // nolint : contextcheck
result, _ := core.ApplyMessage(vmenv, msg, gp, context.Background())
res = append(res, result.Return()...) res = append(res, result.Return()...)
if st.Error() != nil { if st.Error() != nil {
return res, st.Error() return res, st.Error()

View file

@ -212,7 +212,7 @@ var (
// Test accounts // Test accounts
testBankKey, _ = crypto.GenerateKey() testBankKey, _ = crypto.GenerateKey()
TestBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) TestBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
testBankFunds = big.NewInt(1000000000000000000) testBankFunds = big.NewInt(8000000000000000000)
testUserKey, _ = crypto.GenerateKey() testUserKey, _ = crypto.GenerateKey()
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey) testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
@ -224,6 +224,7 @@ var (
testConfig = &Config{ testConfig = &Config{
Recommit: time.Second, Recommit: time.Second,
GasCeil: params.GenesisGasLimit, GasCeil: params.GenesisGasLimit,
CommitInterruptFlag: true,
} }
) )

View file

@ -54,6 +54,7 @@ type Config struct {
GasPrice *big.Int // Minimum gas price for mining a transaction GasPrice *big.Int // Minimum gas price for mining a transaction
Recommit time.Duration // The time interval for miner to re-create mining work. Recommit time.Duration // The time interval for miner to re-create mining work.
Noverify bool // Disable remote mining solution verification(only useful in ethash). Noverify bool // Disable remote mining solution verification(only useful in ethash).
CommitInterruptFlag bool // Interrupt commit when time is up ( default = true)
} }
// Miner creates blocks and searches for proof-of-work values. // Miner creates blocks and searches for proof-of-work values.

View file

@ -31,6 +31,8 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
lru "github.com/hashicorp/golang-lru"
) )
const ( const (
@ -40,6 +42,10 @@ const (
// testGas is the gas required for contract deployment. // testGas is the gas required for contract deployment.
testGas = 144109 testGas = 144109
storageContractByteCode = "608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100a1565b60405180910390f35b610073600480360381019061006e91906100ed565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009b81610088565b82525050565b60006020820190506100b66000830184610092565b92915050565b600080fd5b6100ca81610088565b81146100d557600080fd5b50565b6000813590506100e7816100c1565b92915050565b600060208284031215610103576101026100bc565b5b6000610111848285016100d8565b9150509291505056fea2646970667358221220322c78243e61b783558509c9cc22cb8493dde6925aa5e89a08cdf6e22f279ef164736f6c63430008120033"
storageContractTxCallData = "0x6057361d0000000000000000000000000000000000000000000000000000000000000001"
storageCallTxGas = 100000
) )
func init() { func init() {
@ -167,6 +173,7 @@ func (b *testWorkerBackend) newRandomUncle() (*types.Block, error) {
return blocks[0], err return blocks[0], err
} }
// newRandomTx creates a new transaction.
func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction { func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
var tx *types.Transaction var tx *types.Transaction
@ -181,15 +188,54 @@ func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
return tx return tx
} }
func NewTestWorker(t TensingObject, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int, noempty uint32, delay uint) (*worker, *testWorkerBackend, func()) { // newRandomTxWithNonce creates a new transaction with the given nonce.
func (b *testWorkerBackend) newRandomTxWithNonce(creation bool, nonce uint64) *types.Transaction {
var tx *types.Transaction
gasPrice := big.NewInt(100 * params.InitialBaseFee)
if creation {
tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(TestBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey)
} else {
tx, _ = types.SignTx(types.NewTransaction(nonce, testUserAddress, big.NewInt(1000), params.TxGas, gasPrice, nil), types.HomesteadSigner{}, testBankKey)
}
return tx
}
// newRandomTxWithGas creates a new transactionto deploy a storage smart contract.
func (b *testWorkerBackend) newStorageCreateContractTx() (*types.Transaction, common.Address) {
var tx *types.Transaction
gasPrice := big.NewInt(10 * params.InitialBaseFee)
tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(TestBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(storageContractByteCode)), types.HomesteadSigner{}, testBankKey)
contractAddr := crypto.CreateAddress(TestBankAddress, b.txPool.Nonce(TestBankAddress))
return tx, contractAddr
}
// newStorageContractCallTx creates a new transaction to call a storage smart contract.
func (b *testWorkerBackend) newStorageContractCallTx(to common.Address, nonce uint64) *types.Transaction {
var tx *types.Transaction
gasPrice := big.NewInt(10 * params.InitialBaseFee)
tx, _ = types.SignTx(types.NewTransaction(nonce, to, nil, storageCallTxGas, gasPrice, common.FromHex(storageContractTxCallData)), types.HomesteadSigner{}, testBankKey)
return tx
}
// NewTestWorker creates a new test worker with the given parameters.
func NewTestWorker(t TensingObject, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int, noempty uint32, delay uint, opcodeDelay uint) (*worker, *testWorkerBackend, func()) {
backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks) backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks)
backend.txPool.AddLocals(pendingTxs) backend.txPool.AddLocals(pendingTxs)
var w *worker var w *worker
if delay != 0 { if delay != 0 || opcodeDelay != 0 {
//nolint:staticcheck //nolint:staticcheck
w = newWorkerWithDelay(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false, delay) w = newWorkerWithDelay(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false, delay, opcodeDelay)
} else { } else {
//nolint:staticcheck //nolint:staticcheck
w = newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false) w = newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false)
@ -203,8 +249,9 @@ func NewTestWorker(t TensingObject, chainConfig *params.ChainConfig, engine cons
return w, backend, w.close return w, backend, w.close
} }
// newWorkerWithDelay is newWorker() with extra params to induce artficial delays for tests such as commit-interrupt.
// nolint:staticcheck // nolint:staticcheck
func newWorkerWithDelay(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool, delay uint) *worker { func newWorkerWithDelay(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool, delay uint, opcodeDelay uint) *worker {
worker := &worker{ worker := &worker{
config: config, config: config,
chainConfig: chainConfig, chainConfig: chainConfig,
@ -229,6 +276,7 @@ func newWorkerWithDelay(config *Config, chainConfig *params.ChainConfig, engine
resubmitIntervalCh: make(chan time.Duration), resubmitIntervalCh: make(chan time.Duration),
resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
noempty: 1, noempty: 1,
interruptCommitFlag: config.CommitInterruptFlag,
} }
worker.profileCount = new(int32) worker.profileCount = new(int32)
// Subscribe NewTxsEvent for tx pool // Subscribe NewTxsEvent for tx pool
@ -237,6 +285,19 @@ func newWorkerWithDelay(config *Config, chainConfig *params.ChainConfig, engine
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
interruptedTxCache, err := lru.New(vm.InterruptedTxCacheSize)
if err != nil {
log.Warn("Failed to create interrupted tx cache", "err", err)
}
worker.interruptedTxCache = &vm.TxCache{
Cache: interruptedTxCache,
}
if !worker.interruptCommitFlag {
worker.noempty = 0
}
// Sanitize recommit interval if the user-specified one is too short. // Sanitize recommit interval if the user-specified one is too short.
recommit := worker.config.Recommit recommit := worker.config.Recommit
if recommit < minRecommitInterval { if recommit < minRecommitInterval {
@ -248,7 +309,7 @@ func newWorkerWithDelay(config *Config, chainConfig *params.ChainConfig, engine
worker.wg.Add(4) worker.wg.Add(4)
go worker.mainLoopWithDelay(ctx, delay) go worker.mainLoopWithDelay(ctx, delay, opcodeDelay)
go worker.newWorkLoop(ctx, recommit) go worker.newWorkLoop(ctx, recommit)
go worker.resultLoop() go worker.resultLoop()
go worker.taskLoop() go worker.taskLoop()
@ -261,8 +322,9 @@ func newWorkerWithDelay(config *Config, chainConfig *params.ChainConfig, engine
return worker return worker
} }
// mainLoopWithDelay is mainLoop() with extra params to induce artficial delays for tests such as commit-interrupt.
// nolint:gocognit // nolint:gocognit
func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint) { func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay uint) {
defer w.wg.Done() defer w.wg.Done()
defer w.txsSub.Unsubscribe() defer w.txsSub.Unsubscribe()
defer w.chainHeadSub.Unsubscribe() defer w.chainHeadSub.Unsubscribe()
@ -280,7 +342,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint) {
select { select {
case req := <-w.newWorkCh: case req := <-w.newWorkCh:
//nolint:contextcheck //nolint:contextcheck
w.commitWorkWithDelay(req.ctx, req.interrupt, req.noempty, req.timestamp, delay) w.commitWorkWithDelay(req.ctx, req.interrupt, req.noempty, req.timestamp, delay, opcodeDelay)
case req := <-w.getWorkCh: case req := <-w.getWorkCh:
//nolint:contextcheck //nolint:contextcheck
@ -342,6 +404,7 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint) {
// Note all transactions received may not be continuous with transactions // Note all transactions received may not be continuous with transactions
// already included in the current sealing block. These transactions will // already included in the current sealing block. These transactions will
// be automatically eliminated. // be automatically eliminated.
// nolint : nestif
if !w.isRunning() && w.current != nil { if !w.isRunning() && w.current != nil {
// If block is already full, abort // If block is already full, abort
if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas { if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
@ -358,8 +421,19 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint) {
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee)) txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
tcount := w.current.tcount tcount := w.current.tcount
interruptCh, stopFn := getInterruptTimer(ctx, w.current, w.chain.CurrentBlock()) var interruptCtx = context.Background()
w.commitTransactionsWithDelay(w.current, txset, nil, interruptCh, delay) stopFn := func() {}
defer func() {
stopFn()
}()
if w.interruptCommitFlag {
interruptCtx, stopFn = getInterruptTimer(ctx, w.current, w.chain.CurrentBlock())
// nolint : staticcheck
interruptCtx = vm.PutCache(interruptCtx, w.interruptedTxCache)
}
w.commitTransactionsWithDelay(w.current, txset, nil, interruptCtx)
// Only update the snapshot if any new transactions were added // Only update the snapshot if any new transactions were added
// to the pending block // to the pending block
@ -392,166 +466,8 @@ func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint) {
} }
} }
// nolint:gocognit // commitWorkWithDelay is commitWork() with extra params to induce artficial delays for tests such as commit-interrupt.
func (w *worker) commitTransactionsWithDelay(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCh chan struct{}, delay uint) bool { func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noempty bool, timestamp int64, delay uint, opcodeDelay uint) {
gasLimit := env.header.GasLimit
if env.gasPool == nil {
env.gasPool = new(core.GasPool).AddGas(gasLimit)
}
var coalescedLogs []*types.Log
initialGasLimit := env.gasPool.Gas()
initialTxs := txs.GetTxs()
var breakCause string
defer func() {
log.OnDebug(func(lg log.Logging) {
lg("commitTransactions-stats",
"initialTxsCount", initialTxs,
"initialGasLimit", initialGasLimit,
"resultTxsCount", txs.GetTxs(),
"resultGapPool", env.gasPool.Gas(),
"exitCause", breakCause)
})
}()
mainloop:
for {
// case of interrupting by timeout
select {
case <-interruptCh:
commitInterruptCounter.Inc(1)
break mainloop
default:
}
// In the following three cases, we will interrupt the execution of the transaction.
// (1) new head block event arrival, the interrupt signal is 1
// (2) worker start or restart, the interrupt signal is 1
// (3) worker recreate the sealing block with any newly arrived transactions, the interrupt signal is 2.
// For the first two cases, the semi-finished work will be discarded.
// For the third case, the semi-finished work will be submitted to the consensus engine.
if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone {
// Notify resubmit loop to increase resubmitting interval due to too frequent commits.
if atomic.LoadInt32(interrupt) == commitInterruptResubmit {
ratio := float64(gasLimit-env.gasPool.Gas()) / float64(gasLimit)
if ratio < 0.1 {
// nolint:goconst
ratio = 0.1
}
w.resubmitAdjustCh <- &intervalAdjust{
ratio: ratio,
inc: true,
}
}
// nolint:goconst
breakCause = "interrupt"
return atomic.LoadInt32(interrupt) == commitInterruptNewHead
}
// If we don't have enough gas for any further transactions then we're done
if env.gasPool.Gas() < params.TxGas {
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
// nolint:goconst
breakCause = "Not enough gas for further transactions"
break
}
// Retrieve the next transaction and abort if all done
tx := txs.Peek()
if tx == nil {
// nolint:goconst
breakCause = "all transactions has been included"
break
}
// Error may be ignored here. The error has already been checked
// during transaction acceptance is the transaction pool.
//
// We use the eip155 signer regardless of the current hf.
from, _ := types.Sender(env.signer, tx)
// Check whether the tx is replay protected. If we're not in the EIP155 hf
// phase, start ignoring the sender until we do.
if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block)
txs.Pop()
continue
}
// Start executing the transaction
env.state.Prepare(tx.Hash(), env.tcount)
var start time.Time
log.OnDebug(func(log.Logging) {
start = time.Now()
})
logs, err := w.commitTransaction(env, tx)
time.Sleep(time.Duration(delay) * time.Millisecond)
switch {
case errors.Is(err, core.ErrGasLimitReached):
// Pop the current out-of-gas transaction without shifting in the next from the account
log.Trace("Gas limit exceeded for current block", "sender", from)
txs.Pop()
case errors.Is(err, core.ErrNonceTooLow):
// New head notification data race between the transaction pool and miner, shift
log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
txs.Shift()
case errors.Is(err, core.ErrNonceTooHigh):
// Reorg notification data race between the transaction pool and miner, skip account =
log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
txs.Pop()
case errors.Is(err, nil):
// Everything ok, collect the logs and shift in the next transaction from the same account
coalescedLogs = append(coalescedLogs, logs...)
env.tcount++
txs.Shift()
log.OnDebug(func(lg log.Logging) {
lg("Committed new tx", "tx hash", tx.Hash(), "from", from, "to", tx.To(), "nonce", tx.Nonce(), "gas", tx.Gas(), "gasPrice", tx.GasPrice(), "value", tx.Value(), "time spent", time.Since(start))
})
case errors.Is(err, core.ErrTxTypeNotSupported):
// Pop the unsupported transaction without shifting in the next from the account
log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
txs.Pop()
default:
// Strange error, discard the transaction and get the next in line (note, the
// nonce-too-high clause will prevent us from executing in vain).
log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
txs.Shift()
}
}
if !w.isRunning() && len(coalescedLogs) > 0 {
// We don't push the pendingLogsEvent while we are sealing. The reason is that
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
// logs by filling in the block hash when the block was mined by the local miner. This can
// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
cpy := make([]*types.Log, len(coalescedLogs))
for i, l := range coalescedLogs {
cpy[i] = new(types.Log)
*cpy[i] = *l
}
w.pendingLogsFeed.Send(cpy)
}
// Notify resubmit loop to decrease resubmitting interval if current interval is larger
// than the user-specified one.
if interrupt != nil {
w.resubmitAdjustCh <- &intervalAdjust{inc: false}
}
return false
}
func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noempty bool, timestamp int64, delay uint) {
start := time.Now() start := time.Now()
var ( var (
@ -581,15 +497,22 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
return return
} }
var interruptCh chan struct{} // nolint : contextcheck
var interruptCtx = context.Background()
stopFn := func() {} stopFn := func() {}
defer func() { defer func() {
stopFn() stopFn()
}() }()
if !noempty { if !noempty && w.interruptCommitFlag {
interruptCh, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock()) interruptCtx, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock())
// nolint : staticcheck
interruptCtx = vm.PutCache(interruptCtx, w.interruptedTxCache)
// nolint : staticcheck
interruptCtx = context.WithValue(interruptCtx, vm.InterruptCtxDelayKey, delay)
// nolint : staticcheck
interruptCtx = context.WithValue(interruptCtx, vm.InterruptCtxOpcodeDelayKey, opcodeDelay)
} }
ctx, span := tracing.StartSpan(ctx, "commitWork") ctx, span := tracing.StartSpan(ctx, "commitWork")
@ -610,7 +533,7 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
} }
// Fill pending transactions from the txpool // Fill pending transactions from the txpool
w.fillTransactionsWithDelay(ctx, interrupt, work, interruptCh, delay) w.fillTransactionsWithDelay(ctx, interrupt, work, interruptCtx)
err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start) err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start)
if err != nil { if err != nil {
@ -626,8 +549,9 @@ func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noem
w.current = work w.current = work
} }
// fillTransactionsWithDelay is fillTransactions() with extra params to induce artficial delays for tests such as commit-interrupt.
// nolint:gocognit // nolint:gocognit
func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32, env *environment, interruptCh chan struct{}, delay uint) { func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32, env *environment, interruptCtx context.Context) {
ctx, span := tracing.StartSpan(ctx, "fillTransactions") ctx, span := tracing.StartSpan(ctx, "fillTransactions")
defer tracing.EndSpan(span) defer tracing.EndSpan(span)
@ -751,7 +675,7 @@ func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32
}) })
tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCh, delay) committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCtx)
}) })
if committed { if committed {
@ -774,7 +698,7 @@ func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32
}) })
tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCh, delay) committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCtx)
}) })
if committed { if committed {
@ -790,3 +714,176 @@ func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32
attribute.Int("len of final remote txs", remoteEnvTCount), attribute.Int("len of final remote txs", remoteEnvTCount),
) )
} }
// commitTransactionsWithDelay is commitTransactions() with extra params to induce artficial delays for tests such as commit-interrupt.
// nolint:gocognit, unparam
func (w *worker) commitTransactionsWithDelay(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCtx context.Context) bool {
gasLimit := env.header.GasLimit
if env.gasPool == nil {
env.gasPool = new(core.GasPool).AddGas(gasLimit)
}
var coalescedLogs []*types.Log
initialGasLimit := env.gasPool.Gas()
initialTxs := txs.GetTxs()
var breakCause string
defer func() {
log.OnDebug(func(lg log.Logging) {
lg("commitTransactions-stats",
"initialTxsCount", initialTxs,
"initialGasLimit", initialGasLimit,
"resultTxsCount", txs.GetTxs(),
"resultGapPool", env.gasPool.Gas(),
"exitCause", breakCause)
})
}()
mainloop:
for {
if interruptCtx != nil {
// case of interrupting by timeout
select {
case <-interruptCtx.Done():
log.Warn("Interrupt")
break mainloop
default:
}
}
// In the following three cases, we will interrupt the execution of the transaction.
// (1) new head block event arrival, the interrupt signal is 1
// (2) worker start or restart, the interrupt signal is 1
// (3) worker recreate the sealing block with any newly arrived transactions, the interrupt signal is 2.
// For the first two cases, the semi-finished work will be discarded.
// For the third case, the semi-finished work will be submitted to the consensus engine.
if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone {
// Notify resubmit loop to increase resubmitting interval due to too frequent commits.
if atomic.LoadInt32(interrupt) == commitInterruptResubmit {
ratio := float64(gasLimit-env.gasPool.Gas()) / float64(gasLimit)
if ratio < 0.1 {
// nolint:goconst
ratio = 0.1
}
w.resubmitAdjustCh <- &intervalAdjust{
ratio: ratio,
inc: true,
}
}
// nolint:goconst
breakCause = "interrupt"
return atomic.LoadInt32(interrupt) == commitInterruptNewHead
}
// If we don't have enough gas for any further transactions then we're done
if env.gasPool.Gas() < params.TxGas {
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
// nolint:goconst
breakCause = "Not enough gas for further transactions"
break
}
// Retrieve the next transaction and abort if all done
tx := txs.Peek()
if tx == nil {
// nolint:goconst
breakCause = "all transactions has been included"
break
}
// Error may be ignored here. The error has already been checked
// during transaction acceptance is the transaction pool.
//
// We use the eip155 signer regardless of the current hf.
from, _ := types.Sender(env.signer, tx)
// Check whether the tx is replay protected. If we're not in the EIP155 hf
// phase, start ignoring the sender until we do.
if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block)
txs.Pop()
continue
}
// Start executing the transaction
env.state.Prepare(tx.Hash(), env.tcount)
var start time.Time
log.OnDebug(func(log.Logging) {
start = time.Now()
})
logs, err := w.commitTransaction(env, tx, interruptCtx)
if interruptCtx != nil {
if delay := interruptCtx.Value(vm.InterruptCtxDelayKey); delay != nil {
// nolint : durationcheck
time.Sleep(time.Duration(delay.(uint)) * time.Millisecond)
}
}
switch {
case errors.Is(err, core.ErrGasLimitReached):
// Pop the current out-of-gas transaction without shifting in the next from the account
log.Trace("Gas limit exceeded for current block", "sender", from)
txs.Pop()
case errors.Is(err, core.ErrNonceTooLow):
// New head notification data race between the transaction pool and miner, shift
log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
txs.Shift()
case errors.Is(err, core.ErrNonceTooHigh):
// Reorg notification data race between the transaction pool and miner, skip account =
log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
txs.Pop()
case errors.Is(err, nil):
// Everything ok, collect the logs and shift in the next transaction from the same account
coalescedLogs = append(coalescedLogs, logs...)
env.tcount++
txs.Shift()
log.OnDebug(func(lg log.Logging) {
lg("Committed new tx", "tx hash", tx.Hash(), "from", from, "to", tx.To(), "nonce", tx.Nonce(), "gas", tx.Gas(), "gasPrice", tx.GasPrice(), "value", tx.Value(), "time spent", time.Since(start))
})
case errors.Is(err, core.ErrTxTypeNotSupported):
// Pop the unsupported transaction without shifting in the next from the account
log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
txs.Pop()
default:
// Strange error, discard the transaction and get the next in line (note, the
// nonce-too-high clause will prevent us from executing in vain).
log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
txs.Shift()
}
}
if !w.isRunning() && len(coalescedLogs) > 0 {
// We don't push the pendingLogsEvent while we are sealing. The reason is that
// when we are sealing, the worker will regenerate a sealing block every 3 seconds.
// In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
// logs by filling in the block hash when the block was mined by the local miner. This can
// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
cpy := make([]*types.Log, len(coalescedLogs))
for i, l := range coalescedLogs {
cpy[i] = new(types.Log)
*cpy[i] = *l
}
w.pendingLogsFeed.Send(cpy)
}
// Notify resubmit loop to decrease resubmitting interval if current interval is larger
// than the user-specified one.
if interrupt != nil {
w.resubmitAdjustCh <- &intervalAdjust{inc: false}
}
return false
}

View file

@ -31,6 +31,7 @@ import (
"time" "time"
mapset "github.com/deckarep/golang-set" mapset "github.com/deckarep/golang-set"
lru "github.com/hashicorp/golang-lru"
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
@ -45,6 +46,7 @@ import (
"github.com/ethereum/go-ethereum/core/blockstm" "github.com/ethereum/go-ethereum/core/blockstm"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
@ -96,7 +98,7 @@ const (
var ( var (
sealedBlocksCounter = metrics.NewRegisteredCounter("worker/sealedBlocks", nil) sealedBlocksCounter = metrics.NewRegisteredCounter("worker/sealedBlocks", nil)
sealedEmptyBlocksCounter = metrics.NewRegisteredCounter("worker/sealedEmptyBlocks", nil) sealedEmptyBlocksCounter = metrics.NewRegisteredCounter("worker/sealedEmptyBlocks", nil)
commitInterruptCounter = metrics.NewRegisteredCounter("worker/commitInterrupt", nil) txCommitInterruptCounter = metrics.NewRegisteredCounter("worker/txCommitInterrupt", nil)
) )
// environment is the worker's current environment and holds all // environment is the worker's current environment and holds all
@ -275,6 +277,8 @@ type worker struct {
resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval. resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval.
profileCount *int32 // Global count for profiling profileCount *int32 // Global count for profiling
interruptCommitFlag bool // Interrupt commit ( Default true )
interruptedTxCache *vm.TxCache
} }
//nolint:staticcheck //nolint:staticcheck
@ -303,6 +307,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
resubmitIntervalCh: make(chan time.Duration), resubmitIntervalCh: make(chan time.Duration),
resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
noempty: 1, noempty: 1,
interruptCommitFlag: config.CommitInterruptFlag,
} }
worker.profileCount = new(int32) worker.profileCount = new(int32)
// Subscribe NewTxsEvent for tx pool // Subscribe NewTxsEvent for tx pool
@ -311,6 +316,19 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
interruptedTxCache, err := lru.New(vm.InterruptedTxCacheSize)
if err != nil {
log.Warn("Failed to create interrupted tx cache", "err", err)
}
worker.interruptedTxCache = &vm.TxCache{
Cache: interruptedTxCache,
}
if !worker.interruptCommitFlag {
worker.noempty = 0
}
// Sanitize recommit interval if the user-specified one is too short. // Sanitize recommit interval if the user-specified one is too short.
recommit := worker.config.Recommit recommit := worker.config.Recommit
if recommit < minRecommitInterval { if recommit < minRecommitInterval {
@ -581,7 +599,9 @@ func (w *worker) mainLoop(ctx context.Context) {
select { select {
case req := <-w.newWorkCh: case req := <-w.newWorkCh:
//nolint:contextcheck //nolint:contextcheck
if w.isRunning() {
w.commitWork(req.ctx, req.interrupt, req.noempty, req.timestamp) w.commitWork(req.ctx, req.interrupt, req.noempty, req.timestamp)
}
case req := <-w.getWorkCh: case req := <-w.getWorkCh:
//nolint:contextcheck //nolint:contextcheck
@ -655,8 +675,22 @@ func (w *worker) mainLoop(ctx context.Context) {
txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee)) txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
tcount := w.current.tcount tcount := w.current.tcount
interruptCh, stopFn := getInterruptTimer(ctx, w.current, w.chain.CurrentBlock()) // nolint : contextcheck
w.commitTransactions(w.current, txset, nil, interruptCh) var interruptCtx = context.Background()
stopFn := func() {}
defer func() {
stopFn()
}()
if w.interruptCommitFlag {
interruptCtx, stopFn = getInterruptTimer(ctx, w.current, w.chain.CurrentBlock())
// nolint : staticcheck
interruptCtx = vm.PutCache(interruptCtx, w.interruptedTxCache)
}
w.commitTransactions(w.current, txset, nil, interruptCtx)
// Only update the snapshot if any new transactions were added // Only update the snapshot if any new transactions were added
// to the pending block // to the pending block
@ -932,10 +966,12 @@ func (w *worker) updateSnapshot(env *environment) {
w.snapshotState = env.state.Copy() w.snapshotState = env.state.Copy()
} }
func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) { func (w *worker) commitTransaction(env *environment, tx *types.Transaction, interruptCtx context.Context) ([]*types.Log, error) {
snap := env.state.Snapshot() snap := env.state.Snapshot()
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) // nolint : staticcheck
interruptCtx = vm.SetCurrentTxOnContext(interruptCtx, tx.Hash())
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig(), interruptCtx)
if err != nil { if err != nil {
env.state.RevertToSnapshot(snap) env.state.RevertToSnapshot(snap)
return nil, err return nil, err
@ -947,7 +983,7 @@ func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*
} }
//nolint:gocognit //nolint:gocognit
func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCh chan struct{}) bool { func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCtx context.Context) bool {
gasLimit := env.header.GasLimit gasLimit := env.header.GasLimit
if env.gasPool == nil { if env.gasPool == nil {
env.gasPool = new(core.GasPool).AddGas(gasLimit) env.gasPool = new(core.GasPool).AddGas(gasLimit)
@ -1019,17 +1055,20 @@ func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByP
mainloop: mainloop:
for { for {
if interruptCtx != nil {
if EnableMVHashMap { if EnableMVHashMap {
env.state.AddEmptyMVHashMap() env.state.AddEmptyMVHashMap()
} }
// case of interrupting by timeout // case of interrupting by timeout
select { select {
case <-interruptCh: case <-interruptCtx.Done():
commitInterruptCounter.Inc(1) txCommitInterruptCounter.Inc(1)
log.Warn("Tx Level Interrupt")
break mainloop break mainloop
default: default:
} }
}
// In the following three cases, we will interrupt the execution of the transaction. // In the following three cases, we will interrupt the execution of the transaction.
// (1) new head block event arrival, the interrupt signal is 1 // (1) new head block event arrival, the interrupt signal is 1
@ -1088,7 +1127,7 @@ mainloop:
start = time.Now() start = time.Now()
}) })
logs, err := w.commitTransaction(env, tx) logs, err := w.commitTransaction(env, tx, interruptCtx)
switch { switch {
case errors.Is(err, core.ErrGasLimitReached): case errors.Is(err, core.ErrGasLimitReached):
@ -1379,7 +1418,7 @@ func startProfiler(profile string, filepath string, number uint64) (func() error
// be customized with the plugin in the future. // be customized with the plugin in the future.
// //
//nolint:gocognit //nolint:gocognit
func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *environment, interruptCh chan struct{}) { func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *environment, interruptCtx context.Context) {
ctx, span := tracing.StartSpan(ctx, "fillTransactions") ctx, span := tracing.StartSpan(ctx, "fillTransactions")
defer tracing.EndSpan(span) defer tracing.EndSpan(span)
@ -1503,7 +1542,7 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
}) })
tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
committed = w.commitTransactions(env, txs, interrupt, interruptCh) committed = w.commitTransactions(env, txs, interrupt, interruptCtx)
}) })
if committed { if committed {
@ -1526,7 +1565,7 @@ func (w *worker) fillTransactions(ctx context.Context, interrupt *int32, env *en
}) })
tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) { tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
committed = w.commitTransactions(env, txs, interrupt, interruptCh) committed = w.commitTransactions(env, txs, interrupt, interruptCtx)
}) })
if committed { if committed {
@ -1551,10 +1590,22 @@ func (w *worker) generateWork(ctx context.Context, params *generateParams) (*typ
} }
defer work.discard() defer work.discard()
interruptCh, stopFn := getInterruptTimer(ctx, work, w.chain.CurrentBlock()) // nolint : contextcheck
defer stopFn() var interruptCtx = context.Background()
w.fillTransactions(ctx, nil, work, interruptCh) stopFn := func() {}
defer func() {
stopFn()
}()
if w.interruptCommitFlag {
interruptCtx, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock())
// nolint : staticcheck
interruptCtx = vm.PutCache(interruptCtx, w.interruptedTxCache)
}
w.fillTransactions(ctx, nil, work, interruptCtx)
return w.engine.FinalizeAndAssemble(ctx, w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts) return w.engine.FinalizeAndAssemble(ctx, w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts)
} }
@ -1592,15 +1643,18 @@ func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool,
return return
} }
var interruptCh chan struct{} // nolint : contextcheck
var interruptCtx = context.Background()
stopFn := func() {} stopFn := func() {}
defer func() { defer func() {
stopFn() stopFn()
}() }()
if !noempty { if !noempty && w.interruptCommitFlag {
interruptCh, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock()) interruptCtx, stopFn = getInterruptTimer(ctx, work, w.chain.CurrentBlock())
// nolint : staticcheck
interruptCtx = vm.PutCache(interruptCtx, w.interruptedTxCache)
} }
ctx, span := tracing.StartSpan(ctx, "commitWork") ctx, span := tracing.StartSpan(ctx, "commitWork")
@ -1621,7 +1675,7 @@ func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool,
} }
// Fill pending transactions from the txpool // Fill pending transactions from the txpool
w.fillTransactions(ctx, interrupt, work, interruptCh) w.fillTransactions(ctx, interrupt, work, interruptCtx)
err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start) err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start)
if err != nil { if err != nil {
@ -1637,28 +1691,25 @@ func (w *worker) commitWork(ctx context.Context, interrupt *int32, noempty bool,
w.current = work w.current = work
} }
func getInterruptTimer(ctx context.Context, work *environment, current *types.Block) (chan struct{}, func()) { func getInterruptTimer(ctx context.Context, work *environment, current *types.Block) (context.Context, func()) {
delay := time.Until(time.Unix(int64(work.header.Time), 0)) delay := time.Until(time.Unix(int64(work.header.Time), 0))
timeoutTimer := time.NewTimer(delay) interruptCtx, cancel := context.WithTimeout(context.Background(), delay)
stopFn := func() {
timeoutTimer.Stop()
}
blockNumber := current.NumberU64() + 1 blockNumber := current.NumberU64() + 1
interruptCh := make(chan struct{})
go func() { go func() {
select { select {
case <-timeoutTimer.C: case <-interruptCtx.Done():
if interruptCtx.Err() != context.Canceled {
log.Info("Commit Interrupt. Pre-committing the current block", "block", blockNumber) log.Info("Commit Interrupt. Pre-committing the current block", "block", blockNumber)
cancel()
close(interruptCh) }
case <-ctx.Done(): // nothing to do case <-ctx.Done(): // nothing to do
} }
}() }()
return interruptCh, stopFn return interruptCtx, cancel
} }
// commit runs any post-transaction state modifications, assembles the final block // commit runs any post-transaction state modifications, assembles the final block

View file

@ -19,7 +19,6 @@ package miner
import ( import (
"math/big" "math/big"
"os" "os"
"sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
@ -43,21 +42,18 @@ import (
"github.com/ethereum/go-ethereum/tests/bor/mocks" "github.com/ethereum/go-ethereum/tests/bor/mocks"
) )
// nolint : paralleltest
func TestGenerateBlockAndImportEthash(t *testing.T) { func TestGenerateBlockAndImportEthash(t *testing.T) {
t.Parallel()
testGenerateBlockAndImport(t, false, false) testGenerateBlockAndImport(t, false, false)
} }
// nolint : paralleltest
func TestGenerateBlockAndImportClique(t *testing.T) { func TestGenerateBlockAndImportClique(t *testing.T) {
t.Parallel()
testGenerateBlockAndImport(t, true, false) testGenerateBlockAndImport(t, true, false)
} }
// nolint : paralleltest
func TestGenerateBlockAndImportBor(t *testing.T) { func TestGenerateBlockAndImportBor(t *testing.T) {
t.Parallel()
testGenerateBlockAndImport(t, false, true) testGenerateBlockAndImport(t, false, true)
} }
@ -90,7 +86,7 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool, isBor bool) {
chainConfig.LondonBlock = big.NewInt(0) chainConfig.LondonBlock = big.NewInt(0)
w, b, _ := NewTestWorker(t, chainConfig, engine, db, 0, 0, 0) w, b, _ := NewTestWorker(t, chainConfig, engine, db, 0, 0, 0, 0)
defer w.close() defer w.close()
// This test chain imports the mined blocks. // This test chain imports the mined blocks.
@ -196,7 +192,7 @@ func TestEmptyWorkClique(t *testing.T) {
func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
defer engine.Close() defer engine.Close()
w, _, _ := NewTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, 0, 0) w, _, _ := NewTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, 0, 0, 0)
defer w.close() defer w.close()
var ( var (
@ -250,7 +246,7 @@ func TestStreamUncleBlock(t *testing.T) {
ethash := ethash.NewFaker() ethash := ethash.NewFaker()
defer ethash.Close() defer ethash.Close()
w, b, _ := NewTestWorker(t, ethashChainConfig, ethash, rawdb.NewMemoryDatabase(), 1, 0, 0) w, b, _ := NewTestWorker(t, ethashChainConfig, ethash, rawdb.NewMemoryDatabase(), 1, 0, 0, 0)
defer w.close() defer w.close()
var taskCh = make(chan struct{}) var taskCh = make(chan struct{})
@ -312,7 +308,7 @@ func TestRegenerateMiningBlockClique(t *testing.T) {
func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
defer engine.Close() defer engine.Close()
w, b, _ := NewTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, 0, 0) w, b, _ := NewTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, 0, 0, 0)
defer w.close() defer w.close()
var taskCh = make(chan struct{}, 3) var taskCh = make(chan struct{}, 3)
@ -383,7 +379,7 @@ func TestAdjustIntervalClique(t *testing.T) {
func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
defer engine.Close() defer engine.Close()
w, _, _ := NewTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, 0, 0) w, _, _ := NewTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, 0, 0, 0)
defer w.close() defer w.close()
w.skipSealHook = func(task *task) bool { w.skipSealHook = func(task *task) bool {
@ -491,7 +487,7 @@ func TestGetSealingWorkPostMerge(t *testing.T) {
func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, postMerge bool) { func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, postMerge bool) {
defer engine.Close() defer engine.Close()
w, b, _ := NewTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, 0, 0) w, b, _ := NewTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0, 0, 0, 0)
defer w.close() defer w.close()
w.setExtra([]byte{0x01, 0x02}) w.setExtra([]byte{0x01, 0x02})
@ -627,23 +623,41 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
} }
} }
// nolint : paralleltest
// TestCommitInterruptExperimentBor tests the commit interrupt experiment for bor consensus by inducing an artificial delay at transaction level.
func TestCommitInterruptExperimentBor(t *testing.T) { func TestCommitInterruptExperimentBor(t *testing.T) {
t.Parallel()
// with 1 sec block time and 200 millisec tx delay we should get 5 txs per block // with 1 sec block time and 200 millisec tx delay we should get 5 txs per block
testCommitInterruptExperimentBor(t, 200, 5) testCommitInterruptExperimentBor(t, 200, 5, 0)
time.Sleep(2 * time.Second)
// with 1 sec block time and 100 millisec tx delay we should get 10 txs per block // with 1 sec block time and 100 millisec tx delay we should get 10 txs per block
testCommitInterruptExperimentBor(t, 100, 10) testCommitInterruptExperimentBor(t, 100, 10, 0)
} }
func testCommitInterruptExperimentBor(t *testing.T, delay uint, txCount int) { // nolint : paralleltest
t.Helper() // TestCommitInterruptExperimentBorContract tests the commit interrupt experiment for bor consensus by inducing an artificial delay at OPCODE level.
func TestCommitInterruptExperimentBorContract(t *testing.T) {
// pre-calculated number of OPCODES = 123. 7*123=861 < 1000, 1 tx is possible but 2 tx per block will not be possible.
testCommitInterruptExperimentBorContract(t, 0, 1, 7)
time.Sleep(2 * time.Second)
// pre-calculated number of OPCODES = 123. 2*123=246 < 1000, 4 tx is possible but 5 tx per block will not be possible. But 3 happen due to other overheads.
testCommitInterruptExperimentBorContract(t, 0, 3, 2)
time.Sleep(2 * time.Second)
// pre-calculated number of OPCODES = 123. 3*123=369 < 1000, 2 tx is possible but 3 tx per block will not be possible.
testCommitInterruptExperimentBorContract(t, 0, 2, 3)
}
// nolint : thelper
// testCommitInterruptExperimentBorContract is a helper function for testing the commit interrupt experiment for bor consensus.
func testCommitInterruptExperimentBorContract(t *testing.T, delay uint, txCount int, opcodeDelay uint) {
var ( var (
engine consensus.Engine engine consensus.Engine
chainConfig *params.ChainConfig chainConfig *params.ChainConfig
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
ctrl *gomock.Controller ctrl *gomock.Controller
txInTxpool = 100
txs = make([]*types.Transaction, 0, txInTxpool)
) )
chainConfig = params.BorUnittestChainConfig chainConfig = params.BorUnittestChainConfig
@ -651,38 +665,91 @@ func testCommitInterruptExperimentBor(t *testing.T, delay uint, txCount int) {
log.Root().SetHandler(log.LvlFilterHandler(4, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) log.Root().SetHandler(log.LvlFilterHandler(4, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
engine, ctrl = getFakeBorFromConfig(t, chainConfig) engine, ctrl = getFakeBorFromConfig(t, chainConfig)
w, b, _ := NewTestWorker(t, chainConfig, engine, db, 0, 1, delay, opcodeDelay)
defer func() { defer func() {
w.close()
engine.Close() engine.Close()
db.Close()
ctrl.Finish() ctrl.Finish()
}() }()
w, b, _ := NewTestWorker(t, chainConfig, engine, db, 0, 1, delay) // nonce 0 tx
defer w.close() tx, addr := b.newStorageCreateContractTx()
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
wg.Done()
for {
tx := b.newRandomTx(false)
if err := b.TxPool().AddRemote(tx); err != nil { if err := b.TxPool().AddRemote(tx); err != nil {
t.Log(err) t.Fatal(err)
} }
time.Sleep(20 * time.Millisecond) time.Sleep(4 * time.Second)
}
}()
wg.Wait() // nonce starts from 1 because we already have one tx
initNonce := uint64(1)
for i := 0; i < txInTxpool; i++ {
tx := b.newStorageContractCallTx(addr, initNonce+uint64(i))
txs = append(txs, tx)
}
if err := b.TxPool().AddRemotes(txs); err != nil {
t.Fatal(err)
}
// Start mining! // Start mining!
w.start() w.start()
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
w.stop() w.stop()
assert.Equal(t, txCount, w.chain.CurrentBlock().Transactions().Len()) currentBlockNumber := w.current.header.Number.Uint64()
assert.Check(t, txCount >= w.chain.GetBlockByNumber(currentBlockNumber-1).Transactions().Len())
assert.Check(t, 0 < w.chain.GetBlockByNumber(currentBlockNumber-1).Transactions().Len()+1)
}
// nolint : thelper
// testCommitInterruptExperimentBor is a helper function for testing the commit interrupt experiment for bor consensus.
func testCommitInterruptExperimentBor(t *testing.T, delay uint, txCount int, opcodeDelay uint) {
var (
engine consensus.Engine
chainConfig *params.ChainConfig
db = rawdb.NewMemoryDatabase()
ctrl *gomock.Controller
txInTxpool = 100
txs = make([]*types.Transaction, 0, txInTxpool)
)
chainConfig = params.BorUnittestChainConfig
log.Root().SetHandler(log.LvlFilterHandler(4, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
engine, ctrl = getFakeBorFromConfig(t, chainConfig)
w, b, _ := NewTestWorker(t, chainConfig, engine, db, 0, 1, delay, opcodeDelay)
defer func() {
w.close()
engine.Close()
db.Close()
ctrl.Finish()
}()
// nonce starts from 0 because have no txs yet
initNonce := uint64(0)
for i := 0; i < txInTxpool; i++ {
tx := b.newRandomTxWithNonce(false, initNonce+uint64(i))
txs = append(txs, tx)
}
if err := b.TxPool().AddRemotes(txs); err != nil {
t.Fatal(err)
}
// Start mining!
w.start()
time.Sleep(5 * time.Second)
w.stop()
currentBlockNumber := w.current.header.Number.Uint64()
assert.Check(t, txCount >= w.chain.GetBlockByNumber(currentBlockNumber-1).Transactions().Len())
assert.Check(t, 0 < w.chain.GetBlockByNumber(currentBlockNumber-1).Transactions().Len())
} }
func BenchmarkBorMining(b *testing.B) { func BenchmarkBorMining(b *testing.B) {
@ -716,7 +783,7 @@ func BenchmarkBorMining(b *testing.B) {
chainConfig.LondonBlock = big.NewInt(0) chainConfig.LondonBlock = big.NewInt(0)
w, back, _ := NewTestWorker(b, chainConfig, engine, db, 0, 0, 0) w, back, _ := NewTestWorker(b, chainConfig, engine, db, 0, 0, 0, 0)
defer w.close() defer w.close()
// This test chain imports the mined blocks. // This test chain imports the mined blocks.
@ -822,7 +889,7 @@ func BenchmarkBorMiningBlockSTMMetadata(b *testing.B) {
chainConfig.LondonBlock = big.NewInt(0) chainConfig.LondonBlock = big.NewInt(0)
w, back, _ := NewTestWorker(b, chainConfig, engine, db, 0, 0, 0) w, back, _ := NewTestWorker(b, chainConfig, engine, db, 0, 0, 0, 0)
defer w.close() defer w.close()
// This test chain imports the mined blocks. // This test chain imports the mined blocks.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,16 @@
[Unit]
Description=bor
StartLimitIntervalSec=500
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/bor bootnode -nodekey /var/lib/bor/config/nodekey -addr ":30303" -verbosity 5
Type=simple
KillSignal=SIGINT
User=bor
TimeoutStopSec=120
[Install]
WantedBy=multi-user.target

View file

@ -21,13 +21,14 @@ import (
) )
const ( const (
GitCommit = ""
VersionMajor = 0 // Major version component of the current release VersionMajor = 0 // Major version component of the current release
VersionMinor = 3 // Minor version component of the current release VersionMinor = 3 // Minor version component of the current release
VersionPatch = 8 // Patch version component of the current release VersionPatch = 9 // Patch version component of the current release
VersionMeta = "beta" // Version metadata to append to the version string VersionMeta = "beta" // Version metadata to append to the version string
) )
var GitCommit string
// Version holds the textual version string. // Version holds the textual version string.
var Version = func() string { var Version = func() string {
return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch) return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
@ -42,13 +43,13 @@ var VersionWithMeta = func() string {
return v return v
}() }()
// VersionWithCommitDetails holds the textual version string including the metadata and Git Details. // VersionWithMetaCommitDetails holds the textual version string including the metadata and Git Details.
var VersionWithMetaCommitDetails = func() string { var VersionWithMetaCommitDetails = func() string {
v := Version v := Version
if VersionMeta != "" { if VersionMeta != "" {
v += "-" + VersionMeta v += "-" + VersionMeta
} }
v_git := fmt.Sprintf("Version : %s\nGitCommit : %s\n", v, GitCommit) v_git := fmt.Sprintf("Version: %s\nGitCommit: %s", v, GitCommit)
return v_git return v_git
}() }()

View file

@ -577,90 +577,112 @@ func TestFetchStateSyncEvents_2(t *testing.T) {
require.Equal(t, uint64(6), lastStateID.Uint64()) require.Equal(t, uint64(6), lastStateID.Uint64())
} }
// Abs returns the absolute value of x.
func Abs(x int) int {
if x < 0 {
return -x
}
return x
}
func TestOutOfTurnSigning(t *testing.T) { func TestOutOfTurnSigning(t *testing.T) {
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase()) log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
chain := init.ethereum.BlockChain() fdlimit.Raise(2048)
engine := init.ethereum.Engine()
_bor := engine.(*bor.Bor)
defer _bor.Close() // 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()
}
_, heimdallSpan := loadSpanFromFile(t) // Create an Ethash network based off of the Ropsten config
proposer := valset.NewValidator(addr, 10) // Generate a batch of accounts to seal and fund with
heimdallSpan.ValidatorSet.Validators = append(heimdallSpan.ValidatorSet.Validators, proposer) genesis := InitGenesis(t, faucets, "./testdata/genesis_2val.json", 8)
// add the block producer var (
h, ctrl := getMockedHeimdallClient(t, heimdallSpan) stacks []*node.Node
defer ctrl.Finish() 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()
h.EXPECT().Close().AnyTimes() 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())
}
spanner := getMockedSpanner(t, heimdallSpan.ValidatorSet.Validators) // Iterate over all the nodes and start mining
_bor.SetSpanner(spanner) time.Sleep(3 * time.Second)
_bor.SetHeimdallClient(h) for _, node := range nodes {
if err := node.StartMining(1); err != nil {
db := init.ethereum.ChainDb() panic(err)
block := init.genesis.ToBlock(db)
setDifficulty := func(header *types.Header) {
if IsSprintStart(header.Number.Uint64()) {
header.Difficulty = big.NewInt(int64(len(heimdallSpan.ValidatorSet.Validators)))
} }
} }
for i := uint64(1); i < spanSize; i++ { for {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, heimdallSpan.ValidatorSet.Validators, setDifficulty)
insertNewBlock(t, chain, block) // for block 1 to 8, the primary validator is node0
blockHeaderVal0 := nodes[0].BlockChain().CurrentHeader()
blockHeaderVal1 := nodes[1].BlockChain().CurrentHeader()
// we remove peer connection between node0 and node1
if blockHeaderVal0.Number.Uint64() == 1 {
stacks[0].Server().RemovePeer(enodes[1])
} }
// insert spanSize-th block if blockHeaderVal1.Number.Uint64() == 9 {
// This account is one the out-of-turn validators for 1st (0-indexed) span break
signer := "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd9"
signerKey, _ := hex.DecodeString(signer)
newKey, _ := crypto.HexToECDSA(signer)
newAddr := crypto.PubkeyToAddress(newKey.PublicKey)
expectedSuccessionNumber := 2
parentTime := block.Time()
setParentTime := func(header *types.Header) {
header.Time = parentTime + 1
} }
const turn = 1
setDifficulty = func(header *types.Header) {
header.Difficulty = big.NewInt(int64(len(heimdallSpan.ValidatorSet.Validators)) - turn)
} }
block = buildNextBlock(t, _bor, chain, block, signerKey, init.genesis.Config.Bor, nil, heimdallSpan.ValidatorSet.Validators, setParentTime, setDifficulty) // function to get timestamp difference between two nodes of the same blockNumber
_, err := chain.InsertChain([]*types.Block{block}) timeDiff := func(blockNum int) int {
require.Equal(t, blockHeaderVal0 := nodes[0].BlockChain().GetHeaderByNumber(uint64(blockNum))
bor.BlockTooSoonError{Number: spanSize, Succession: expectedSuccessionNumber}, blockHeaderVal1 := nodes[1].BlockChain().GetHeaderByNumber(uint64(blockNum))
*err.(*bor.BlockTooSoonError))
expectedDifficulty := uint64(len(heimdallSpan.ValidatorSet.Validators) - expectedSuccessionNumber - turn) // len(validators) - succession return Abs(int(blockHeaderVal0.Time - blockHeaderVal1.Time))
header := block.Header() }
diff := bor.CalcProducerDelay(header.Number.Uint64(), expectedSuccessionNumber, init.genesis.Config.Bor) // check block 10 miner ; expected author is node1 signer
header.Time += diff blockHeaderVal0 := nodes[0].BlockChain().GetHeaderByNumber(3)
blockHeaderVal1 := nodes[1].BlockChain().GetHeaderByNumber(3)
sign(t, header, signerKey, init.genesis.Config.Bor) // check both nodes have the same block 10
assert.Equal(t, blockHeaderVal0.Difficulty, common.Big2)
block = types.NewBlockWithHeader(header) // check node0 has block mined by node1
assert.Equal(t, blockHeaderVal1.Difficulty, common.Big1)
_, err = chain.InsertChain([]*types.Block{block}) // node seperated at block 1, so at block 3, node0 should be at 3 seconds, and node1 should be at 5( 1 + 2*2 ) seconds
require.NotNil(t, err) assert.Equal(t, timeDiff(3), 2)
require.Equal(t,
bor.WrongDifficultyError{Number: spanSize, Expected: expectedDifficulty, Actual: 3, Signer: newAddr.Bytes()},
*err.(*bor.WrongDifficultyError))
header.Difficulty = new(big.Int).SetUint64(expectedDifficulty) // check block 10 miner ; expected author is node1 signer
sign(t, header, signerKey, init.genesis.Config.Bor) blockHeaderVal0 = nodes[0].BlockChain().GetHeaderByNumber(7)
block = types.NewBlockWithHeader(header) blockHeaderVal1 = nodes[1].BlockChain().GetHeaderByNumber(7)
_, err = chain.InsertChain([]*types.Block{block}) // check both nodes have the same block 10
require.Nil(t, err) assert.Equal(t, blockHeaderVal0.Difficulty, common.Big2)
// check node0 has block mined by node1
assert.Equal(t, blockHeaderVal1.Difficulty, common.Big1)
// node seperated at block 1, so at block 7, node0 should be at 7 seconds, and node1 should be at 13( 1 + 2*6 ) seconds
assert.Equal(t, timeDiff(7), 6)
} }
func TestSignerNotFound(t *testing.T) { func TestSignerNotFound(t *testing.T) {
@ -1099,11 +1121,18 @@ func TestJaipurFork(t *testing.T) {
res, _ := loadSpanFromFile(t) res, _ := loadSpanFromFile(t)
spanner := getMockedSpanner(t, res.Result.ValidatorSet.Validators) currentValidators := []*valset.Validator{valset.NewValidator(addr, 10)}
spanner := getMockedSpanner(t, currentValidators)
_bor.SetSpanner(spanner) _bor.SetSpanner(spanner)
for i := uint64(1); i < sprintSize; i++ { for i := uint64(1); i < sprintSize; i++ {
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, res.Result.ValidatorSet.Validators) if IsSpanEnd(i) {
currentValidators = res.Result.ValidatorSet.Validators
}
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor, nil, currentValidators)
insertNewBlock(t, chain, block) insertNewBlock(t, chain, block)
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock.Uint64()-1 { if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock.Uint64()-1 {

View file

@ -257,7 +257,7 @@ func (b *blockGen) addTxWithChain(bc *core.BlockChain, statedb *state.StateDB, t
statedb.Prepare(tx.Hash(), len(b.txs)) statedb.Prepare(tx.Hash(), len(b.txs))
receipt, err := core.ApplyTransaction(bc.Config(), bc, &b.header.Coinbase, b.gasPool, statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) receipt, err := core.ApplyTransaction(bc.Config(), bc, &b.header.Coinbase, b.gasPool, statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -303,7 +303,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
// Execute the message. // Execute the message.
snapshot := statedb.Snapshot() snapshot := statedb.Snapshot()
_, _, err = evm.Call(sender, *msg.To(), msg.Data(), msg.Gas(), msg.Value()) _, _, err = evm.Call(sender, *msg.To(), msg.Data(), msg.Gas(), msg.Value(), nil)
if err != nil { if err != nil {
b.Error(err) b.Error(err)
return return

View file

@ -232,7 +232,8 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
snapshot := statedb.Snapshot() snapshot := statedb.Snapshot()
gaspool := new(core.GasPool) gaspool := new(core.GasPool)
gaspool.AddGas(block.GasLimit()) gaspool.AddGas(block.GasLimit())
if _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
if _, err := core.ApplyMessage(evm, msg, gaspool, nil); err != nil {
statedb.RevertToSnapshot(snapshot) statedb.RevertToSnapshot(snapshot)
} }