mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
commit
a3bd485bd6
129 changed files with 6198 additions and 1769 deletions
|
|
@ -32,12 +32,12 @@ var (
|
|||
// have any code associated with it (i.e. suicided).
|
||||
ErrNoCode = errors.New("no contract code at given address")
|
||||
|
||||
// This error is raised when attempting to perform a pending state action
|
||||
// ErrNoPendingState is raised when attempting to perform a pending state action
|
||||
// on a backend that doesn't implement PendingContractCaller.
|
||||
ErrNoPendingState = errors.New("backend does not support pending state")
|
||||
|
||||
// This error is returned by WaitDeployed if contract creation leaves an
|
||||
// empty contract behind.
|
||||
// ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
|
||||
// an empty contract behind.
|
||||
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
|
||||
)
|
||||
|
||||
|
|
@ -47,7 +47,8 @@ type ContractCaller interface {
|
|||
// CodeAt returns the code of the given account. This is needed to differentiate
|
||||
// between contract internal errors and the local chain being out of sync.
|
||||
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
|
||||
// ContractCall executes an Ethereum contract call with the specified data as the
|
||||
|
||||
// CallContract executes an Ethereum contract call with the specified data as the
|
||||
// input.
|
||||
CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error)
|
||||
}
|
||||
|
|
@ -58,6 +59,7 @@ type ContractCaller interface {
|
|||
type PendingContractCaller interface {
|
||||
// PendingCodeAt returns the code of the given account in the pending state.
|
||||
PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
|
||||
|
||||
// PendingCallContract executes an Ethereum contract call against the pending state.
|
||||
PendingCallContract(ctx context.Context, call XDPoSChain.CallMsg) ([]byte, error)
|
||||
}
|
||||
|
|
@ -67,19 +69,31 @@ type PendingContractCaller interface {
|
|||
// used when the user does not provide some needed values, but rather leaves it up
|
||||
// to the transactor to decide.
|
||||
type ContractTransactor interface {
|
||||
// HeaderByNumber returns a block header from the current canonical chain. If
|
||||
// number is nil, the latest known header is returned.
|
||||
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
||||
|
||||
// PendingCodeAt returns the code of the given account in the pending state.
|
||||
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
|
||||
|
||||
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
||||
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
||||
|
||||
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
||||
// execution of a transaction.
|
||||
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
||||
|
||||
// SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
|
||||
// a timely execution of a transaction.
|
||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
||||
|
||||
// EstimateGas tries to estimate the gas needed to execute a specific
|
||||
// transaction based on the current pending state of the backend blockchain.
|
||||
// There is no guarantee that this is the true gas limit requirement as other
|
||||
// transactions may be added or removed by miners, but it should provide a basis
|
||||
// for setting a reasonable default.
|
||||
EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (gas uint64, err error)
|
||||
|
||||
// SendTransaction injects the transaction into the pending pool for execution.
|
||||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,6 +256,19 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
|
|||
return receipt, nil
|
||||
}
|
||||
|
||||
// HeaderByNumber returns a block header from the current canonical chain. If number is
|
||||
// nil, the latest known header is returned.
|
||||
func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
if block == nil || block.Cmp(b.pendingBlock.Number()) == 0 {
|
||||
return b.blockchain.CurrentHeader(), nil
|
||||
}
|
||||
|
||||
return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
|
||||
}
|
||||
|
||||
// PendingCodeAt returns the code associated with an account in the pending state.
|
||||
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||
b.mu.Lock()
|
||||
|
|
@ -300,8 +313,17 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
|
|||
}
|
||||
|
||||
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
|
||||
// chain doens't have miners, we just return a gas price of 1 for any call.
|
||||
// chain doesn't have miners, we just return a gas price of 1 for any call.
|
||||
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||
if b.pendingBlock.Header().BaseFee != nil {
|
||||
return b.pendingBlock.Header().BaseFee, nil
|
||||
}
|
||||
return big.NewInt(1), nil
|
||||
}
|
||||
|
||||
// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated
|
||||
// chain doesn't have miners, we just return a gas tip of 1 for any call.
|
||||
func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||
return big.NewInt(1), nil
|
||||
}
|
||||
|
||||
|
|
@ -358,10 +380,38 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call
|
|||
// callContract implements common code between normal and pending contract calls.
|
||||
// state is modified during execution, make sure to copy it if necessary.
|
||||
func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (ret []byte, usedGas uint64, failed bool, err error) {
|
||||
// Ensure message is initialized properly.
|
||||
if call.GasPrice == nil {
|
||||
call.GasPrice = big.NewInt(1)
|
||||
// Gas prices post 1559 need to be initialized
|
||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||
return nil, 0, false, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||
}
|
||||
head := b.blockchain.CurrentHeader()
|
||||
if !b.blockchain.Config().IsEIP1559(head.Number) {
|
||||
// If there's no basefee, then it must be a non-1559 execution
|
||||
if call.GasPrice == nil {
|
||||
call.GasPrice = new(big.Int)
|
||||
}
|
||||
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
||||
} else {
|
||||
// A basefee is provided, necessitating 1559-type execution
|
||||
if call.GasPrice != nil {
|
||||
// User specified the legacy gas field, convert to 1559 gas typing
|
||||
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
||||
} else {
|
||||
// User specified 1559 gas feilds (or none), use those
|
||||
if call.GasFeeCap == nil {
|
||||
call.GasFeeCap = new(big.Int)
|
||||
}
|
||||
if call.GasTipCap == nil {
|
||||
call.GasTipCap = new(big.Int)
|
||||
}
|
||||
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
|
||||
call.GasPrice = new(big.Int)
|
||||
if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 {
|
||||
call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, head.BaseFee), call.GasFeeCap)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure message is initialized properly.
|
||||
if call.Gas == 0 {
|
||||
call.Gas = 50000000
|
||||
}
|
||||
|
|
@ -384,7 +434,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal
|
|||
evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{})
|
||||
vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{NoBaseFee: true})
|
||||
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||
owner := common.Address{}
|
||||
ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
|
||||
|
|
@ -522,9 +572,11 @@ type callMsg struct {
|
|||
|
||||
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
||||
func (m callMsg) Nonce() uint64 { return 0 }
|
||||
func (m callMsg) CheckNonce() bool { return false }
|
||||
func (m callMsg) IsFake() bool { return true }
|
||||
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
|
||||
func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
|
||||
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
||||
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
||||
|
|
@ -554,7 +606,11 @@ func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*t
|
|||
}
|
||||
|
||||
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
|
||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
||||
if number == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
|
||||
}
|
||||
|
||||
func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
|
||||
|
|
|
|||
|
|
@ -53,11 +53,15 @@ type TransactOpts struct {
|
|||
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
||||
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
||||
|
||||
Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
|
||||
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
||||
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
||||
Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
|
||||
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
|
||||
GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
||||
GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle)
|
||||
GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate)
|
||||
|
||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||
|
||||
NoSend bool // Do all transact steps but do not send the transaction
|
||||
}
|
||||
|
||||
// FilterOpts is the collection of options to fine tune filtering for events
|
||||
|
|
@ -184,57 +188,158 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error)
|
|||
return c.transact(opts, &c.address, nil)
|
||||
}
|
||||
|
||||
// transact executes an actual transaction invocation, first deriving any missing
|
||||
// authorization fields, and then scheduling the transaction for execution.
|
||||
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||
var err error
|
||||
|
||||
// Ensure a valid value field and resolve the account nonce
|
||||
func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) {
|
||||
// Normalize value
|
||||
value := opts.Value
|
||||
if value == nil {
|
||||
value = new(big.Int)
|
||||
}
|
||||
var nonce uint64
|
||||
if opts.Nonce == nil {
|
||||
nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
|
||||
// Estimate TipCap
|
||||
gasTipCap := opts.GasTipCap
|
||||
if gasTipCap == nil {
|
||||
tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
nonce = opts.Nonce.Uint64()
|
||||
gasTipCap = tip
|
||||
}
|
||||
// Figure out the gas allowance and gas price values
|
||||
// Estimate FeeCap
|
||||
gasFeeCap := opts.GasFeeCap
|
||||
if gasFeeCap == nil {
|
||||
gasFeeCap = new(big.Int).Add(
|
||||
gasTipCap,
|
||||
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
|
||||
)
|
||||
}
|
||||
if gasFeeCap.Cmp(gasTipCap) < 0 {
|
||||
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
|
||||
}
|
||||
// Estimate GasLimit
|
||||
gasLimit := opts.GasLimit
|
||||
if opts.GasLimit == 0 {
|
||||
var err error
|
||||
gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// create the transaction
|
||||
nonce, err := c.getNonce(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseTx := &types.DynamicFeeTx{
|
||||
To: contract,
|
||||
Nonce: nonce,
|
||||
GasFeeCap: gasFeeCap,
|
||||
GasTipCap: gasTipCap,
|
||||
Gas: gasLimit,
|
||||
Value: value,
|
||||
Data: input,
|
||||
}
|
||||
return types.NewTx(baseTx), nil
|
||||
}
|
||||
|
||||
func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||
if opts.GasFeeCap != nil || opts.GasTipCap != nil {
|
||||
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but EIP-1559 is not active yet")
|
||||
}
|
||||
// Normalize value
|
||||
value := opts.Value
|
||||
if value == nil {
|
||||
value = new(big.Int)
|
||||
}
|
||||
// Estimate GasPrice
|
||||
gasPrice := opts.GasPrice
|
||||
if gasPrice == nil {
|
||||
gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
||||
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to suggest gas price: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
gasPrice = price
|
||||
}
|
||||
// Estimate GasLimit
|
||||
gasLimit := opts.GasLimit
|
||||
if gasLimit == 0 {
|
||||
// Gas estimation cannot succeed without code for method invocations
|
||||
if contract != nil {
|
||||
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
|
||||
return nil, err
|
||||
} else if len(code) == 0 {
|
||||
return nil, ErrNoCode
|
||||
}
|
||||
}
|
||||
// If the contract surely has code (or code is not needed), estimate the transaction
|
||||
msg := XDPoSChain.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
|
||||
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
||||
if opts.GasLimit == 0 {
|
||||
var err error
|
||||
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Create the transaction, sign it and schedule it for execution
|
||||
var rawTx *types.Transaction
|
||||
if contract == nil {
|
||||
rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input)
|
||||
} else {
|
||||
rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
|
||||
// create the transaction
|
||||
nonce, err := c.getNonce(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baseTx := &types.LegacyTx{
|
||||
To: contract,
|
||||
Nonce: nonce,
|
||||
GasPrice: gasPrice,
|
||||
Gas: gasLimit,
|
||||
Value: value,
|
||||
Data: input,
|
||||
}
|
||||
return types.NewTx(baseTx), nil
|
||||
}
|
||||
|
||||
func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) {
|
||||
if contract != nil {
|
||||
// Gas estimation cannot succeed without code for method invocations.
|
||||
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
|
||||
return 0, err
|
||||
} else if len(code) == 0 {
|
||||
return 0, ErrNoCode
|
||||
}
|
||||
}
|
||||
msg := XDPoSChain.CallMsg{
|
||||
From: opts.From,
|
||||
To: contract,
|
||||
GasPrice: gasPrice,
|
||||
GasTipCap: gasTipCap,
|
||||
GasFeeCap: gasFeeCap,
|
||||
Value: value,
|
||||
Data: input,
|
||||
}
|
||||
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
||||
}
|
||||
|
||||
func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
|
||||
if opts.Nonce == nil {
|
||||
return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
|
||||
} else {
|
||||
return opts.Nonce.Uint64(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// transact executes an actual transaction invocation, first deriving any missing
|
||||
// authorization fields, and then scheduling the transaction for execution.
|
||||
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
|
||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||
}
|
||||
// Create the transaction
|
||||
var (
|
||||
rawTx *types.Transaction
|
||||
err error
|
||||
)
|
||||
if opts.GasPrice != nil {
|
||||
rawTx, err = c.createLegacyTx(opts, contract, input)
|
||||
} else {
|
||||
// Only query for basefee if gasPrice not specified
|
||||
if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != nil {
|
||||
return nil, errHead
|
||||
} else if head.BaseFee != nil {
|
||||
rawTx, err = c.createDynamicTx(opts, contract, input, head)
|
||||
} else {
|
||||
// Chain is not London ready -> use legacy transaction
|
||||
rawTx, err = c.createLegacyTx(opts, contract, input)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Sign the transaction and schedule it for execution
|
||||
if opts.Signer == nil {
|
||||
return nil, errors.New("no signer to authorize the transaction with")
|
||||
}
|
||||
|
|
@ -242,6 +347,9 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.NoSend {
|
||||
return signedTx, nil
|
||||
}
|
||||
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
177
accounts/abi/bind/base_test.go
Normal file
177
accounts/abi/bind/base_test.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package bind_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi"
|
||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil }
|
||||
|
||||
type mockTransactor struct {
|
||||
baseFee *big.Int
|
||||
gasTipCap *big.Int
|
||||
gasPrice *big.Int
|
||||
suggestGasTipCapCalled bool
|
||||
suggestGasPriceCalled bool
|
||||
}
|
||||
|
||||
func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||
return &types.Header{BaseFee: mt.baseFee}, nil
|
||||
}
|
||||
|
||||
func (mt *mockTransactor) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
||||
return []byte{1}, nil
|
||||
}
|
||||
|
||||
func (mt *mockTransactor) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||
mt.suggestGasPriceCalled = true
|
||||
return mt.gasPrice, nil
|
||||
}
|
||||
|
||||
func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||
mt.suggestGasTipCapCalled = true
|
||||
return mt.gasTipCap, nil
|
||||
}
|
||||
|
||||
func (mt *mockTransactor) EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (gas uint64, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockCaller struct {
|
||||
codeAtBlockNumber *big.Int
|
||||
callContractBlockNumber *big.Int
|
||||
pendingCodeAtCalled bool
|
||||
pendingCallContractCalled bool
|
||||
}
|
||||
|
||||
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
mc.codeAtBlockNumber = blockNumber
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
|
||||
func (mc *mockCaller) CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
mc.callContractBlockNumber = blockNumber
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||
mc.pendingCodeAtCalled = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mc *mockCaller) PendingCallContract(ctx context.Context, call XDPoSChain.CallMsg) ([]byte, error) {
|
||||
mc.pendingCallContractCalled = true
|
||||
return nil, nil
|
||||
}
|
||||
func TestPassingBlockNumber(t *testing.T) {
|
||||
|
||||
mc := &mockCaller{}
|
||||
|
||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
||||
Methods: map[string]abi.Method{
|
||||
"something": {
|
||||
Name: "something",
|
||||
Outputs: abi.Arguments{},
|
||||
},
|
||||
},
|
||||
}, mc, nil, nil)
|
||||
|
||||
bc.Call(&bind.CallOpts{}, nil, "something")
|
||||
|
||||
bc.Call(&bind.CallOpts{}, nil, "something")
|
||||
|
||||
if mc.callContractBlockNumber != nil {
|
||||
t.Fatalf("CallContract() was passed a block number when it should not have been")
|
||||
}
|
||||
|
||||
if mc.codeAtBlockNumber != nil {
|
||||
t.Fatalf("CodeAt() was passed a block number when it should not have been")
|
||||
}
|
||||
|
||||
bc.Call(&bind.CallOpts{Pending: true}, nil, "something")
|
||||
|
||||
if !mc.pendingCallContractCalled {
|
||||
t.Fatalf("CallContract() was not passed the block number")
|
||||
}
|
||||
|
||||
if !mc.pendingCodeAtCalled {
|
||||
t.Fatalf("CodeAt() was not passed the block number")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransactGasFee(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// GasTipCap and GasFeeCap
|
||||
// When opts.GasTipCap and opts.GasFeeCap are nil
|
||||
mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)}
|
||||
bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
|
||||
opts := &bind.TransactOpts{Signer: mockSign}
|
||||
tx, err := bc.Transact(opts, "")
|
||||
assert.Nil(err)
|
||||
assert.Equal(big.NewInt(5), tx.GasTipCap())
|
||||
assert.Equal(big.NewInt(205), tx.GasFeeCap())
|
||||
assert.Nil(opts.GasTipCap)
|
||||
assert.Nil(opts.GasFeeCap)
|
||||
assert.True(mt.suggestGasTipCapCalled)
|
||||
|
||||
// Second call to Transact should use latest suggested GasTipCap
|
||||
mt.gasTipCap = big.NewInt(6)
|
||||
mt.suggestGasTipCapCalled = false
|
||||
tx, err = bc.Transact(opts, "")
|
||||
assert.Nil(err)
|
||||
assert.Equal(big.NewInt(6), tx.GasTipCap())
|
||||
assert.Equal(big.NewInt(206), tx.GasFeeCap())
|
||||
assert.True(mt.suggestGasTipCapCalled)
|
||||
|
||||
// GasPrice
|
||||
// When opts.GasPrice is nil
|
||||
mt = &mockTransactor{gasPrice: big.NewInt(5)}
|
||||
bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
|
||||
opts = &bind.TransactOpts{Signer: mockSign}
|
||||
tx, err = bc.Transact(opts, "")
|
||||
assert.Nil(err)
|
||||
assert.Equal(big.NewInt(5), tx.GasPrice())
|
||||
assert.Nil(opts.GasPrice)
|
||||
assert.True(mt.suggestGasPriceCalled)
|
||||
|
||||
// Second call to Transact should use latest suggested GasPrice
|
||||
mt.gasPrice = big.NewInt(6)
|
||||
mt.suggestGasPriceCalled = false
|
||||
tx, err = bc.Transact(opts, "")
|
||||
assert.Nil(err)
|
||||
assert.Equal(big.NewInt(6), tx.GasPrice())
|
||||
assert.True(mt.suggestGasPriceCalled)
|
||||
}
|
||||
|
|
@ -229,7 +229,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy an interaction tester contract and call a transaction on it
|
||||
_, _, interactor, err := DeployInteractor(auth, sim, "Deploy string")
|
||||
|
|
@ -270,7 +270,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy a tuple tester contract and execute a structured call on it
|
||||
_, _, getter, err := DeployGetter(auth, sim)
|
||||
|
|
@ -302,7 +302,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy a tuple tester contract and execute a structured call on it
|
||||
_, _, tupler, err := DeployTupler(auth, sim)
|
||||
|
|
@ -344,7 +344,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy a slice tester contract and execute a n array call on it
|
||||
_, _, slicer, err := DeploySlicer(auth, sim)
|
||||
|
|
@ -378,7 +378,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy a default method invoker contract and execute its default method
|
||||
_, _, defaulter, err := DeployDefaulter(auth, sim)
|
||||
|
|
@ -447,7 +447,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy a funky gas pattern contract
|
||||
_, _, limiter, err := DeployFunkyGasPattern(auth, sim)
|
||||
|
|
@ -482,7 +482,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy a sender tester contract and execute a structured call on it
|
||||
_, _, callfrom, err := DeployCallFrom(auth, sim)
|
||||
|
|
@ -542,7 +542,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy a underscorer tester contract and execute a structured call on it
|
||||
_, _, underscorer, err := DeployUnderscorer(auth, sim)
|
||||
|
|
@ -612,7 +612,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
// Deploy an eventer contract
|
||||
_, _, eventer, err := DeployEventer(auth, sim)
|
||||
|
|
@ -761,7 +761,7 @@ var bindTests = []struct {
|
|||
// Generate a new random account and a funded simulator
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
sim := backends.NewXDCSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000, params.TestXDPoSMockChainConfig)
|
||||
|
||||
//deploy the test contract
|
||||
_, _, testContract, err := DeployDeeplyNestedArray(auth, sim)
|
||||
|
|
|
|||
|
|
@ -53,15 +53,20 @@ var waitDeployedTests = map[string]struct {
|
|||
}
|
||||
|
||||
func TestWaitDeployed(t *testing.T) {
|
||||
config := *params.TestXDPoSMockChainConfig
|
||||
config.Eip1559Block = big.NewInt(0)
|
||||
for name, test := range waitDeployedTests {
|
||||
backend := backends.NewXDCSimulatedBackend(
|
||||
core.GenesisAlloc{
|
||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
|
||||
}, 10000000, params.TestXDPoSMockChainConfig,
|
||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(100000000000000000)},
|
||||
}, 10000000, &config,
|
||||
)
|
||||
|
||||
// Create the transaction.
|
||||
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))
|
||||
// Create the transaction
|
||||
head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
|
||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
|
||||
// Wait for it to get mined in the background.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||
|
|
@ -261,12 +262,12 @@ var (
|
|||
TxPoolJournalFlag = cli.StringFlag{
|
||||
Name: "txpool.journal",
|
||||
Usage: "Disk journal for local transaction to survive node restarts",
|
||||
Value: core.DefaultTxPoolConfig.Journal,
|
||||
Value: txpool.DefaultConfig.Journal,
|
||||
}
|
||||
TxPoolRejournalFlag = cli.DurationFlag{
|
||||
Name: "txpool.rejournal",
|
||||
Usage: "Time interval to regenerate the local transaction journal",
|
||||
Value: core.DefaultTxPoolConfig.Rejournal,
|
||||
Value: txpool.DefaultConfig.Rejournal,
|
||||
}
|
||||
TxPoolPriceLimitFlag = cli.Uint64Flag{
|
||||
Name: "txpool.pricelimit",
|
||||
|
|
@ -1016,8 +1017,7 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
|
|||
// If we are running the light client, apply another group
|
||||
// settings for gas oracle.
|
||||
if light {
|
||||
cfg.Blocks = ethconfig.LightClientGPO.Blocks
|
||||
cfg.Percentile = ethconfig.LightClientGPO.Percentile
|
||||
*cfg = ethconfig.LightClientGPO
|
||||
}
|
||||
if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
|
||||
cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
|
||||
|
|
@ -1033,7 +1033,7 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func setTxPool(ctx *cli.Context, cfg *core.TxPoolConfig) {
|
||||
func setTxPool(ctx *cli.Context, cfg *txpool.Config) {
|
||||
if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) {
|
||||
cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ var BerlinBlock = big.NewInt(16832700)
|
|||
var LondonBlock = big.NewInt(16832700)
|
||||
var MergeBlock = big.NewInt(16832700)
|
||||
var ShanghaiBlock = big.NewInt(16832700)
|
||||
var Eip1559Block = big.NewInt(9999999999)
|
||||
var Eip1559Block = big.NewInt(23580000)
|
||||
|
||||
var TIPXDCXTestnet = big.NewInt(0)
|
||||
var IsTestnet bool = false
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import "math/big"
|
|||
|
||||
var MinGasPrice50x = big.NewInt(12500000000)
|
||||
var GasPrice50x = big.NewInt(12500000000)
|
||||
var BaseFee = big.NewInt(12500000000)
|
||||
|
||||
func GetGasFee(blockNumber, gas uint64) *big.Int {
|
||||
fee := new(big.Int).SetUint64(gas)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
|
|
@ -241,6 +242,10 @@ func (x *XDPoS_v1) verifyCascadingFields(chain consensus.ChainReader, header *ty
|
|||
if parent.Time.Uint64()+x.config.Period > header.Time.Uint64() {
|
||||
return utils.ErrInvalidTimestamp
|
||||
}
|
||||
// Verify the header's EIP-1559 attributes.
|
||||
if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if number%x.config.Epoch != 0 {
|
||||
return x.verifySeal(chain, header, parents, fullVerify)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
|
|
@ -62,7 +61,7 @@ func getM1M2(masternodes []common.Address, validators []int64, currentHeader *ty
|
|||
func sigHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewKeccak256()
|
||||
|
||||
err := rlp.Encode(hasher, []interface{}{
|
||||
enc := []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
|
|
@ -78,10 +77,11 @@ func sigHash(header *types.Header) (hash common.Hash) {
|
|||
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
log.Debug("Fail to encode", err)
|
||||
}
|
||||
if header.BaseFee != nil {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
rlp.Encode(hasher, enc)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
func sigHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewKeccak256()
|
||||
|
||||
err := rlp.Encode(hasher, []interface{}{
|
||||
enc := []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
|
|
@ -38,10 +38,11 @@ func sigHash(header *types.Header) (hash common.Hash) {
|
|||
header.Nonce,
|
||||
header.Validators,
|
||||
header.Penalties,
|
||||
})
|
||||
if err != nil {
|
||||
log.Debug("Fail to encode", err)
|
||||
}
|
||||
if header.BaseFee != nil {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
rlp.Encode(hasher, enc)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
|
@ -94,7 +95,10 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
|
|||
if header.UncleHash != utils.UncleHash {
|
||||
return utils.ErrInvalidUncleHash
|
||||
}
|
||||
|
||||
// Verify the header's EIP-1559 attributes.
|
||||
if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil {
|
||||
return err
|
||||
}
|
||||
if header.Difficulty.Cmp(big.NewInt(1)) != 0 {
|
||||
return utils.ErrInvalidDifficulty
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ type SignerFn func(accounts.Account, []byte) ([]byte, error)
|
|||
func sigHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewKeccak256()
|
||||
|
||||
rlp.Encode(hasher, []interface{}{
|
||||
enc := []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
|
|
@ -163,7 +163,11 @@ func sigHash(header *types.Header) (hash common.Hash) {
|
|||
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
}
|
||||
if header.BaseFee != nil {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
rlp.Encode(hasher, enc)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
|
|
@ -253,6 +254,10 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
|
|||
if header.GasUsed > header.GasLimit {
|
||||
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
|
||||
}
|
||||
// Verify the header's EIP-1559 attributes.
|
||||
if err := eip1559.VerifyEip1559Header(chain.Config(), header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify that the gas limit remains within allowed bounds
|
||||
diff := int64(parent.GasLimit) - int64(header.GasLimit)
|
||||
|
|
|
|||
62
consensus/misc/eip1559/eip1559.go
Normal file
62
consensus/misc/eip1559/eip1559.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package eip1559
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
// VerifyEip1559Header verifies some header attributes which were changed in EIP-1559,
|
||||
// - gas limit check
|
||||
// - basefee check
|
||||
func VerifyEip1559Header(config *params.ChainConfig, header *types.Header) error {
|
||||
if !config.IsEIP1559(header.Number) {
|
||||
if header.BaseFee != nil {
|
||||
return fmt.Errorf("invalid baseFee: have %s, want <nil>",
|
||||
header.BaseFee)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify the header is not malformed
|
||||
if header.BaseFee == nil {
|
||||
return fmt.Errorf("header is missing baseFee")
|
||||
}
|
||||
|
||||
// Verify the baseFee is correct based on the current header.
|
||||
expectedBaseFee := CalcBaseFee(config, header)
|
||||
if header.BaseFee.Cmp(expectedBaseFee) != 0 {
|
||||
return fmt.Errorf("invalid baseFee: have %s, want %s",
|
||||
header.BaseFee, expectedBaseFee)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CalcBaseFee calculates the basefee of the header.
|
||||
func CalcBaseFee(config *params.ChainConfig, header *types.Header) *big.Int {
|
||||
// If the current block is the first EIP-1559 block, return the InitialBaseFee.
|
||||
if config.IsEIP1559(header.Number) {
|
||||
return new(big.Int).Set(common.BaseFee)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ import (
|
|||
randomizeContract "github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
|
|
@ -60,7 +61,7 @@ type RewardLog struct {
|
|||
var TxSignMu sync.RWMutex
|
||||
|
||||
// Send tx sign for block number to smart contract blockSigner.
|
||||
func CreateTransactionSign(chainConfig *params.ChainConfig, pool *core.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database, eb common.Address) error {
|
||||
func CreateTransactionSign(chainConfig *params.ChainConfig, pool *txpool.TxPool, manager *accounts.Manager, block *types.Block, chainDb ethdb.Database, eb common.Address) error {
|
||||
TxSignMu.Lock()
|
||||
defer TxSignMu.Unlock()
|
||||
if chainConfig.XDPoS != nil {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
|||
return func(i int, gen *BlockGen) {
|
||||
toaddr := common.Address{}
|
||||
data := make([]byte, nbytes)
|
||||
gas, _ := IntrinsicGas(data, nil, false, false)
|
||||
gas, _ := IntrinsicGas(data, nil, false, false, false)
|
||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
|
||||
gen.AddTx(tx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,15 +60,15 @@ var (
|
|||
CheckpointCh = make(chan int)
|
||||
ErrNoGenesis = errors.New("Genesis not found in chain")
|
||||
|
||||
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
|
||||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
||||
blockReorgInvalidatedTx = metrics.NewRegisteredMeter("chain/reorg/invalidTx", nil)
|
||||
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
|
||||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
||||
)
|
||||
|
||||
const (
|
||||
bodyCacheLimit = 256
|
||||
blockCacheLimit = 256
|
||||
receiptsCacheLimit = 32
|
||||
maxFutureBlocks = 256
|
||||
maxTimeFutureBlocks = 30
|
||||
badBlockLimit = 10
|
||||
|
|
@ -142,6 +142,7 @@ type BlockChain struct {
|
|||
|
||||
bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies
|
||||
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] // Cache for the most recent block bodies in RLP encoded format
|
||||
receiptsCache *lru.Cache[common.Hash, types.Receipts] // Cache for the most recent block receipts
|
||||
blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks
|
||||
resultProcess *lru.Cache[common.Hash, *ResultProcessBlock] // Cache for processed blocks
|
||||
calculatingBlock *lru.Cache[common.Hash, *CalculatedBlock] // Cache for processing blocks
|
||||
|
|
@ -196,6 +197,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
quit: make(chan struct{}),
|
||||
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
||||
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
|
||||
receiptsCache: lru.NewCache[common.Hash, types.Receipts](receiptsCacheLimit),
|
||||
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
||||
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
||||
resultProcess: lru.NewCache[common.Hash, *ResultProcessBlock](blockCacheLimit),
|
||||
|
|
@ -397,6 +399,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
|||
// Clear out any stale content from the caches
|
||||
bc.bodyCache.Purge()
|
||||
bc.bodyRLPCache.Purge()
|
||||
bc.receiptsCache.Purge()
|
||||
bc.blockCache.Purge()
|
||||
bc.futureBlocks.Purge()
|
||||
bc.blocksHashCache.Purge()
|
||||
|
|
@ -809,7 +812,19 @@ func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
|
|||
|
||||
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
|
||||
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
return GetBlockReceipts(bc.db, hash, GetBlockNumber(bc.db, hash))
|
||||
if receipts, ok := bc.receiptsCache.Get(hash); ok {
|
||||
return receipts
|
||||
}
|
||||
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig)
|
||||
if receipts == nil {
|
||||
return nil
|
||||
}
|
||||
bc.receiptsCache.Add(hash, receipts)
|
||||
return receipts
|
||||
}
|
||||
|
||||
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
|
||||
|
|
@ -1029,49 +1044,6 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
|
|||
}
|
||||
}
|
||||
|
||||
// SetReceiptsData computes all the non-consensus fields of the receipts
|
||||
func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error {
|
||||
signer := types.MakeSigner(config, block.Number())
|
||||
|
||||
transactions, logIndex := block.Transactions(), uint(0)
|
||||
if len(transactions) != len(receipts) {
|
||||
return errors.New("transaction and receipt count mismatch")
|
||||
}
|
||||
|
||||
for j := 0; j < len(receipts); j++ {
|
||||
// The transaction hash can be retrieved from the transaction itself
|
||||
receipts[j].TxHash = transactions[j].Hash()
|
||||
|
||||
// block location fields
|
||||
receipts[j].BlockHash = block.Hash()
|
||||
receipts[j].BlockNumber = block.Number()
|
||||
receipts[j].TransactionIndex = uint(j)
|
||||
|
||||
// The contract address can be derived from the transaction itself
|
||||
if transactions[j].To() == nil {
|
||||
// Deriving the signer is expensive, only do if it's actually needed
|
||||
from, _ := types.Sender(signer, transactions[j])
|
||||
receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce())
|
||||
}
|
||||
// The used gas can be calculated based on previous receipts
|
||||
if j == 0 {
|
||||
receipts[j].GasUsed = receipts[j].CumulativeGasUsed
|
||||
} else {
|
||||
receipts[j].GasUsed = receipts[j].CumulativeGasUsed - receipts[j-1].CumulativeGasUsed
|
||||
}
|
||||
// The derived log fields can simply be set from the block and transaction
|
||||
for k := 0; k < len(receipts[j].Logs); k++ {
|
||||
receipts[j].Logs[k].BlockNumber = block.NumberU64()
|
||||
receipts[j].Logs[k].BlockHash = block.Hash()
|
||||
receipts[j].Logs[k].TxHash = receipts[j].TxHash
|
||||
receipts[j].Logs[k].TxIndex = uint(j)
|
||||
receipts[j].Logs[k].Index = logIndex
|
||||
logIndex++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertReceiptChain attempts to complete an already existing header chain with
|
||||
// transaction and receipt data.
|
||||
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
||||
|
|
@ -1100,22 +1072,23 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
||||
return 0, nil
|
||||
}
|
||||
blockHash, blockNumber := block.Hash(), block.NumberU64()
|
||||
// Short circuit if the owner header is unknown
|
||||
if !bc.HasHeader(block.Hash(), block.NumberU64()) {
|
||||
return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4])
|
||||
if !bc.HasHeader(blockHash, blockNumber) {
|
||||
return i, fmt.Errorf("containing header #%d [%x…] unknown", blockNumber, blockHash.Bytes()[:4])
|
||||
}
|
||||
// Skip if the entire data is already known
|
||||
if bc.HasBlock(block.Hash(), block.NumberU64()) {
|
||||
if bc.HasBlock(blockHash, blockNumber) {
|
||||
stats.ignored++
|
||||
continue
|
||||
}
|
||||
// Compute all the non-consensus fields of the receipts
|
||||
if err := SetReceiptsData(bc.chainConfig, block, receipts); err != nil {
|
||||
if err := receipts.DeriveFields(bc.chainConfig, blockHash, blockNumber, block.BaseFee(), block.Transactions()); err != nil {
|
||||
return i, fmt.Errorf("failed to set receipts data: %v", err)
|
||||
}
|
||||
// Write all the data out into the database
|
||||
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
|
||||
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
|
||||
rawdb.WriteBody(batch, blockHash, blockNumber, block.Body())
|
||||
if err := WriteBlockReceipts(batch, blockHash, blockNumber, receipts); err != nil {
|
||||
return i, fmt.Errorf("failed to write block receipts: %v", err)
|
||||
}
|
||||
if err := WriteTxLookupEntries(batch, block); err != nil {
|
||||
|
|
@ -1482,7 +1455,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
|||
defer close(abort)
|
||||
|
||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
||||
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
|
||||
SenderCacher.RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
|
||||
|
||||
// Iterate over the blocks and insert when the verifier permits
|
||||
for i, block := range chain {
|
||||
|
|
@ -2131,6 +2104,25 @@ func countTransactions(chain []*types.Block) (c int) {
|
|||
return c
|
||||
}
|
||||
|
||||
// collectLogs collects the logs that were generated or removed during
|
||||
// the processing of a block. These logs are later announced as deleted or reborn.
|
||||
func (bc *BlockChain) collectLogs(b *types.Block, removed bool) []*types.Log {
|
||||
receipts := rawdb.ReadRawReceipts(bc.db, b.Hash(), b.NumberU64())
|
||||
if err := receipts.DeriveFields(bc.chainConfig, b.Hash(), b.NumberU64(), b.BaseFee(), b.Transactions()); err != nil {
|
||||
log.Error("Failed to derive block receipts fields", "hash", b.Hash(), "number", b.NumberU64(), "err", err)
|
||||
}
|
||||
|
||||
var logs []*types.Log
|
||||
for _, receipt := range receipts {
|
||||
for _, log := range receipt.Logs {
|
||||
l := *log
|
||||
l.Removed = removed
|
||||
logs = append(logs, &l)
|
||||
}
|
||||
}
|
||||
return logs
|
||||
}
|
||||
|
||||
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
|
||||
// to be part of the new canonical chain and accumulates potential missing transactions and post an
|
||||
// event about them
|
||||
|
|
@ -2141,20 +2133,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||
commonBlock *types.Block
|
||||
deletedTxs types.Transactions
|
||||
deletedLogs []*types.Log
|
||||
// collectLogs collects the logs that were generated during the
|
||||
// processing of the block that corresponds with the given hash.
|
||||
// These logs are later announced as deleted.
|
||||
collectLogs = func(h common.Hash) {
|
||||
// Coalesce logs and set 'Removed'.
|
||||
receipts := GetBlockReceipts(bc.db, h, bc.hc.GetBlockNumber(h))
|
||||
for _, receipt := range receipts {
|
||||
for _, log := range receipt.Logs {
|
||||
del := *log
|
||||
del.Removed = true
|
||||
deletedLogs = append(deletedLogs, &del)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
log.Warn("Reorg", "oldBlock hash", oldBlock.Hash().Hex(), "number", oldBlock.NumberU64(), "newBlock hash", newBlock.Hash().Hex(), "number", newBlock.NumberU64())
|
||||
|
||||
|
|
@ -2164,8 +2142,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||
for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) {
|
||||
oldChain = append(oldChain, oldBlock)
|
||||
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
||||
|
||||
collectLogs(oldBlock.Hash())
|
||||
if logs := bc.collectLogs(oldBlock, true); len(logs) > 0 {
|
||||
deletedLogs = append(deletedLogs, logs...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// reduce new chain and append new chain blocks for inserting later on
|
||||
|
|
@ -2189,7 +2168,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||
oldChain = append(oldChain, oldBlock)
|
||||
newChain = append(newChain, newBlock)
|
||||
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
||||
collectLogs(oldBlock.Hash())
|
||||
if logs := bc.collectLogs(oldBlock, true); len(logs) > 0 {
|
||||
deletedLogs = append(deletedLogs, logs...)
|
||||
}
|
||||
|
||||
oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1)
|
||||
if oldBlock == nil {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
|
|
@ -555,10 +556,11 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
gendb = rawdb.NewMemoryDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
funds = big.NewInt(1000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
|
|
@ -569,7 +571,7 @@ func TestFastVsFullChains(t *testing.T) {
|
|||
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
||||
if i%3 == 2 {
|
||||
for j := 0; j < i%4+1; j++ {
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -643,8 +645,12 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
gendb = rawdb.NewMemoryDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
||||
funds = big.NewInt(1000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
genesis = gspec.MustCommit(gendb)
|
||||
)
|
||||
height := uint64(1024)
|
||||
|
|
@ -730,9 +736,9 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
Config: params.TestChainConfig,
|
||||
GasLimit: 3141592,
|
||||
Alloc: GenesisAlloc{
|
||||
addr1: {Balance: big.NewInt(1000000)},
|
||||
addr2: {Balance: big.NewInt(1000000)},
|
||||
addr3: {Balance: big.NewInt(1000000)},
|
||||
addr1: {Balance: big.NewInt(1000000000000000)},
|
||||
addr2: {Balance: big.NewInt(1000000000000000)},
|
||||
addr3: {Balance: big.NewInt(1000000000000000)},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
|
|
@ -742,8 +748,8 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
// Create two transactions shared between the chains:
|
||||
// - postponed: transaction included at a later block in the forked chain
|
||||
// - swapped: transaction included at the same block number in the forked chain
|
||||
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
||||
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
||||
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1)
|
||||
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1)
|
||||
|
||||
// Create two transactions that will be dropped by the forked chain:
|
||||
// - pastDrop: transaction dropped retroactively from a past block
|
||||
|
|
@ -759,13 +765,13 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
||||
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2)
|
||||
|
||||
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
|
||||
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
|
||||
|
||||
case 2:
|
||||
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
||||
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2)
|
||||
|
||||
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
|
||||
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
|
||||
|
|
@ -784,18 +790,18 @@ func TestChainTxReorgs(t *testing.T) {
|
|||
chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
|
||||
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
|
||||
|
||||
case 2:
|
||||
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
|
||||
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
|
||||
|
||||
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
|
||||
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
|
||||
|
||||
case 3:
|
||||
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
|
||||
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
||||
}
|
||||
})
|
||||
|
|
@ -840,7 +846,7 @@ func TestLogReorgs(t *testing.T) {
|
|||
db = rawdb.NewMemoryDatabase()
|
||||
// this code generates a log
|
||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
genesis = gspec.MustCommit(db)
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
|
@ -852,7 +858,7 @@ func TestLogReorgs(t *testing.T) {
|
|||
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
||||
if i == 1 {
|
||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
|
||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, code), signer, key1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tx: %v", err)
|
||||
}
|
||||
|
|
@ -886,7 +892,7 @@ func TestLogReorgs(t *testing.T) {
|
|||
// addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
// gspec = &Genesis{
|
||||
// Config: params.TestChainConfig,
|
||||
// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}},
|
||||
// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
|
||||
// }
|
||||
// genesis = gspec.MustCommit(db)
|
||||
// signer = types.LatestSigner(gspec.Config)
|
||||
|
|
@ -901,7 +907,7 @@ func TestLogReorgs(t *testing.T) {
|
|||
// }
|
||||
//
|
||||
// replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) {
|
||||
// tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1)
|
||||
// tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, nil), signer, key1)
|
||||
// if i == 2 {
|
||||
// gen.OffsetTime(-9)
|
||||
// }
|
||||
|
|
@ -1107,8 +1113,8 @@ func TestEIP155Transition(t *testing.T) {
|
|||
}
|
||||
})
|
||||
_, err := blockchain.InsertChain(blocks)
|
||||
if err != types.ErrInvalidChainId {
|
||||
t.Error("expected error:", types.ErrInvalidChainId)
|
||||
if have, want := err, types.ErrInvalidChainId; !errors.Is(have, want) {
|
||||
t.Errorf("have %v, want %v", have, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1188,7 +1194,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
|||
engine := ethash.NewFaker()
|
||||
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
||||
|
|
@ -1204,7 +1210,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
|||
// Import the canonical and fork chain side by side, verifying the current block
|
||||
// and current header consistency
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
if err != nil {
|
||||
|
|
@ -1233,7 +1239,7 @@ func TestTrieForkGC(t *testing.T) {
|
|||
engine := ethash.NewFaker()
|
||||
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
|
||||
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
||||
|
|
@ -1248,7 +1254,7 @@ func TestTrieForkGC(t *testing.T) {
|
|||
}
|
||||
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
if err != nil {
|
||||
|
|
@ -1279,7 +1285,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
|||
engine := ethash.NewFaker()
|
||||
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||
|
||||
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
||||
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
||||
|
|
@ -1287,7 +1293,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
|||
|
||||
// Import the shared chain and the original canonical one
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||
if err != nil {
|
||||
|
|
@ -1431,8 +1437,9 @@ func TestEIP2718Transition(t *testing.T) {
|
|||
// A sender who makes transactions, has some funds
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{
|
||||
// funds = big.NewInt(math.MaxInt64)
|
||||
funds = big.NewInt(1000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{
|
||||
ChainId: new(big.Int).SetBytes([]byte("eip1559")),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
|
|
@ -1458,7 +1465,7 @@ func TestEIP2718Transition(t *testing.T) {
|
|||
byte(vm.SLOAD),
|
||||
},
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
Balance: big.NewInt(50000000000),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -1475,7 +1482,7 @@ func TestEIP2718Transition(t *testing.T) {
|
|||
Nonce: 0,
|
||||
To: &aa,
|
||||
Gas: 30000,
|
||||
GasPrice: big.NewInt(1),
|
||||
GasPrice: b.header.BaseFee,
|
||||
AccessList: types.AccessList{{
|
||||
Address: aa,
|
||||
StorageKeys: []common.Hash{{0}},
|
||||
|
|
@ -1499,7 +1506,8 @@ func TestEIP2718Transition(t *testing.T) {
|
|||
block := chain.GetBlockByNumber(1)
|
||||
|
||||
// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
|
||||
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929
|
||||
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
|
||||
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
|
||||
if block.GasUsed() != expected {
|
||||
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
|
|
@ -127,6 +127,11 @@ func (b *BlockGen) Number() *big.Int {
|
|||
return new(big.Int).Set(b.header.Number)
|
||||
}
|
||||
|
||||
// BaseFee returns the EIP-1559 base fee of the block being generated.
|
||||
func (b *BlockGen) BaseFee() *big.Int {
|
||||
return new(big.Int).Set(b.header.BaseFee)
|
||||
}
|
||||
|
||||
// AddUncheckedReceipt forcefully adds a receipts to the block without a
|
||||
// backing transaction.
|
||||
//
|
||||
|
|
@ -265,7 +270,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
|
|||
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
|
||||
}
|
||||
|
||||
return &types.Header{
|
||||
header := &types.Header{
|
||||
Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())),
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
|
|
@ -279,6 +284,10 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
|
|||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||
Time: time,
|
||||
}
|
||||
|
||||
header.BaseFee = eip1559.CalcBaseFee(chain.Config(), header)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
// newCanonical creates a chain database, and injects a deterministic canonical
|
||||
|
|
@ -324,3 +333,18 @@ func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethd
|
|||
})
|
||||
return blocks
|
||||
}
|
||||
|
||||
type fakeChainReader struct {
|
||||
config *params.ChainConfig
|
||||
}
|
||||
|
||||
// Config returns the chain configuration.
|
||||
func (cr *fakeChainReader) Config() *params.ChainConfig {
|
||||
return cr.config
|
||||
}
|
||||
|
||||
func (cr *fakeChainReader) CurrentHeader() *types.Header { return nil }
|
||||
func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header { return nil }
|
||||
func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil }
|
||||
func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil }
|
||||
func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil }
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
|
@ -33,7 +33,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
|
||||
// Generate a common prefix for both pro-forkers and non-forkers
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
gspec := new(Genesis)
|
||||
gspec := &Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}
|
||||
genesis := gspec.MustCommit(db)
|
||||
prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ var (
|
|||
// ErrKnownBlock is returned when a block to import is already known locally.
|
||||
ErrKnownBlock = errors.New("block already known")
|
||||
|
||||
// ErrGasLimitReached is returned by the gas pool if the amount of gas required
|
||||
// by a transaction is higher than what's left in the block.
|
||||
ErrGasLimitReached = errors.New("gas limit reached")
|
||||
|
||||
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
||||
ErrBlacklistedHash = errors.New("blacklisted hash")
|
||||
|
||||
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
|
||||
// one present in the local chain.
|
||||
ErrNonceTooLow = errors.New("nonce too low")
|
||||
|
||||
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
||||
// next one expected based on the local chain.
|
||||
ErrNonceTooHigh = errors.New("nonce too high")
|
||||
|
|
@ -41,16 +41,51 @@ var (
|
|||
// maximum allowed value and would become invalid if incremented.
|
||||
ErrNonceMax = errors.New("nonce has max value")
|
||||
|
||||
ErrNotXDPoS = errors.New("XDPoS not found in config")
|
||||
// ErrGasLimitReached is returned by the gas pool if the amount of gas required
|
||||
// by a transaction is higher than what's left in the block.
|
||||
ErrGasLimitReached = errors.New("gas limit reached")
|
||||
|
||||
ErrNotFoundM1 = errors.New("list M1 not found ")
|
||||
// ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger
|
||||
// than init code size limit.
|
||||
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||
|
||||
ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2")
|
||||
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
||||
// is higher than the balance of the user's account.
|
||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||
|
||||
// ErrGasUintOverflow is returned when calculating gas usage.
|
||||
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||
|
||||
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
|
||||
// than required to start the invocation.
|
||||
ErrIntrinsicGas = errors.New("intrinsic gas too low")
|
||||
|
||||
// ErrTxTypeNotSupported is returned if a transaction is not supported in the
|
||||
// current network configuration.
|
||||
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported
|
||||
|
||||
// ErrGasUintOverflow is returned when calculating gas usage.
|
||||
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
||||
// ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a
|
||||
// transaction with a tip higher than the total fee cap.
|
||||
ErrTipAboveFeeCap = errors.New("max priority fee per gas higher than max fee per gas")
|
||||
|
||||
// ErrTipVeryHigh is a sanity error to avoid extremely big numbers specified
|
||||
// in the tip field.
|
||||
ErrTipVeryHigh = errors.New("max priority fee per gas higher than 2^256-1")
|
||||
|
||||
// ErrFeeCapVeryHigh is a sanity error to avoid extremely big numbers specified
|
||||
// in the fee cap field.
|
||||
ErrFeeCapVeryHigh = errors.New("max fee per gas higher than 2^256-1")
|
||||
|
||||
// ErrFeeCapTooLow is returned if the transaction fee cap is less than the
|
||||
// the base fee of the block.
|
||||
ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee")
|
||||
|
||||
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
|
||||
ErrSenderNoEOA = errors.New("sender not an eoa")
|
||||
|
||||
ErrNotXDPoS = errors.New("XDPoS not found in config")
|
||||
|
||||
ErrNotFoundM1 = errors.New("list M1 not found ")
|
||||
|
||||
ErrStopPreparingBlock = errors.New("stop calculating a block not verified by M2")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, auth
|
|||
// If we don't have an explicit author (i.e. not mining), extract from the header
|
||||
var (
|
||||
beneficiary common.Address
|
||||
baseFee *big.Int
|
||||
random common.Hash
|
||||
)
|
||||
if author == nil {
|
||||
|
|
@ -38,6 +39,9 @@ func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, auth
|
|||
} else {
|
||||
beneficiary = *author
|
||||
}
|
||||
if header.BaseFee != nil {
|
||||
baseFee = new(big.Int).Set(header.BaseFee)
|
||||
}
|
||||
// since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random
|
||||
random = crypto.Keccak256Hash(header.Number.Bytes())
|
||||
return vm.BlockContext{
|
||||
|
|
@ -48,6 +52,7 @@ func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, auth
|
|||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: new(big.Int).Set(header.Time),
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
BaseFee: baseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
Random: &random,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
var _ = (*genesisSpecMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
|
|
@ -29,6 +30,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
|||
Number math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
}
|
||||
var enc Genesis
|
||||
enc.Config = g.Config
|
||||
|
|
@ -48,9 +50,11 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
|||
enc.Number = math.HexOrDecimal64(g.Number)
|
||||
enc.GasUsed = math.HexOrDecimal64(g.GasUsed)
|
||||
enc.ParentHash = g.ParentHash
|
||||
enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
|
|
@ -65,6 +69,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
|||
Number *math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
}
|
||||
var dec Genesis
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -112,5 +117,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
|||
if dec.ParentHash != nil {
|
||||
g.ParentHash = *dec.ParentHash
|
||||
}
|
||||
if dec.BaseFee != nil {
|
||||
g.BaseFee = (*big.Int)(dec.BaseFee)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
var _ = (*genesisAccountMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (g GenesisAccount) MarshalJSON() ([]byte, error) {
|
||||
type GenesisAccount struct {
|
||||
Code hexutil.Bytes `json:"code,omitempty"`
|
||||
|
|
@ -36,6 +37,7 @@ func (g GenesisAccount) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
|
||||
type GenesisAccount struct {
|
||||
Code *hexutil.Bytes `json:"code,omitempty"`
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ type Genesis struct {
|
|||
Number uint64 `json:"number"`
|
||||
GasUsed uint64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *big.Int `json:"baseFeePerGas"`
|
||||
}
|
||||
|
||||
// GenesisAlloc specifies the initial state that is part of the genesis block.
|
||||
|
|
@ -96,6 +97,7 @@ type genesisSpecMarshaling struct {
|
|||
GasUsed math.HexOrDecimal64
|
||||
Number math.HexOrDecimal64
|
||||
Difficulty *math.HexOrDecimal256
|
||||
BaseFee *math.HexOrDecimal256
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount
|
||||
}
|
||||
|
||||
|
|
@ -253,6 +255,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
|||
Extra: g.ExtraData,
|
||||
GasLimit: g.GasLimit,
|
||||
GasUsed: g.GasUsed,
|
||||
BaseFee: g.BaseFee,
|
||||
Difficulty: g.Difficulty,
|
||||
MixDigest: g.Mixhash,
|
||||
Coinbase: g.Coinbase,
|
||||
|
|
@ -264,6 +267,13 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
|||
if g.Difficulty == nil {
|
||||
head.Difficulty = params.GenesisDifficulty
|
||||
}
|
||||
if g.Config != nil && g.Config.IsEIP1559(common.Big0) {
|
||||
if g.BaseFee != nil {
|
||||
head.BaseFee = g.BaseFee
|
||||
} else {
|
||||
head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee)
|
||||
}
|
||||
}
|
||||
statedb.Commit(false)
|
||||
statedb.Database().TrieDB().Commit(root, true)
|
||||
|
||||
|
|
@ -308,7 +318,10 @@ func (g *Genesis) MustCommit(db ethdb.Database) *types.Block {
|
|||
|
||||
// GenesisBlockForTesting creates and writes a block in which addr has the given wei balance.
|
||||
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
|
||||
g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}}
|
||||
g := Genesis{
|
||||
Alloc: GenesisAlloc{addr: {Balance: balance}},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
return g.MustCommit(db)
|
||||
}
|
||||
|
||||
|
|
@ -365,6 +378,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
|
|||
Config: &config,
|
||||
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...),
|
||||
GasLimit: 6283185,
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Difficulty: big.NewInt(1),
|
||||
Alloc: map[common.Address]GenesisAccount{
|
||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
)
|
||||
|
||||
// Implement our EthTest Manager
|
||||
type TestManager struct {
|
||||
// stateManager *StateManager
|
||||
eventMux *event.TypeMux
|
||||
|
||||
db ethdb.Database
|
||||
txPool *TxPool
|
||||
blockChain *BlockChain
|
||||
Blocks []*types.Block
|
||||
}
|
||||
|
||||
func (tm *TestManager) IsListening() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (tm *TestManager) IsMining() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (tm *TestManager) PeerCount() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (tm *TestManager) Peers() *list.List {
|
||||
return list.New()
|
||||
}
|
||||
|
||||
func (tm *TestManager) BlockChain() *BlockChain {
|
||||
return tm.blockChain
|
||||
}
|
||||
|
||||
func (tm *TestManager) TxPool() *TxPool {
|
||||
return tm.txPool
|
||||
}
|
||||
|
||||
// func (tm *TestManager) StateManager() *StateManager {
|
||||
// return tm.stateManager
|
||||
// }
|
||||
|
||||
func (tm *TestManager) EventMux() *event.TypeMux {
|
||||
return tm.eventMux
|
||||
}
|
||||
|
||||
// func (tm *TestManager) KeyManager() *crypto.KeyManager {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (tm *TestManager) Db() ethdb.Database {
|
||||
return tm.db
|
||||
}
|
||||
|
||||
func NewTestManager() *TestManager {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
|
||||
testManager := &TestManager{}
|
||||
testManager.eventMux = new(event.TypeMux)
|
||||
testManager.db = db
|
||||
// testManager.txPool = NewTxPool(testManager)
|
||||
// testManager.blockChain = NewBlockChain(testManager)
|
||||
// testManager.stateManager = NewStateManager(testManager)
|
||||
|
||||
return testManager
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
|
|
@ -36,15 +37,6 @@ func WriteCanonicalHash(db ethdb.KeyValueWriter, hash common.Hash, number uint64
|
|||
}
|
||||
}
|
||||
|
||||
// WriteHeaderNumber stores the hash->number mapping.
|
||||
func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
key := headerNumberKey(hash)
|
||||
enc := encodeBlockNumber(number)
|
||||
if err := db.Put(key, enc); err != nil {
|
||||
log.Crit("Failed to store hash to number mapping", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadHeaderNumber returns the header number assigned to a hash.
|
||||
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
|
||||
data, _ := db.Get(headerNumberKey(hash))
|
||||
|
|
@ -55,6 +47,15 @@ func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
|
|||
return &number
|
||||
}
|
||||
|
||||
// WriteHeaderNumber stores the hash->number mapping.
|
||||
func WriteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
key := headerNumberKey(hash)
|
||||
enc := encodeBlockNumber(number)
|
||||
if err := db.Put(key, enc); err != nil {
|
||||
log.Crit("Failed to store hash to number mapping", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteHeadBlockHash stores the head block's hash.
|
||||
func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
|
||||
|
|
@ -62,6 +63,26 @@ func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
|||
}
|
||||
}
|
||||
|
||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
data, _ := db.Get(headerKey(number, hash))
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadHeader retrieves the block header corresponding to the hash.
|
||||
func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header {
|
||||
data := ReadHeaderRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
|
||||
log.Error("Invalid block header RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
// WriteHeader stores a block header into the database and also stores the hash-
|
||||
// to-number mapping.
|
||||
func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) {
|
||||
|
|
@ -193,6 +214,9 @@ func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Rec
|
|||
receipts := make(types.Receipts, len(storageReceipts))
|
||||
for i, storageReceipt := range storageReceipts {
|
||||
receipts[i] = (*types.Receipt)(storageReceipt)
|
||||
receipts[i].BlockHash = hash
|
||||
receipts[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||
receipts[i].TransactionIndex = uint(i)
|
||||
}
|
||||
return receipts
|
||||
}
|
||||
|
|
@ -215,7 +239,14 @@ func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *para
|
|||
log.Error("Missing body but have receipt", "hash", hash, "number", number)
|
||||
return nil
|
||||
}
|
||||
if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil {
|
||||
header := ReadHeader(db, hash, number)
|
||||
var baseFee *big.Int
|
||||
if header == nil {
|
||||
baseFee = big.NewInt(0)
|
||||
} else {
|
||||
baseFee = header.BaseFee
|
||||
}
|
||||
if err := receipts.DeriveFields(config, hash, number, baseFee, body.Transactions); err != nil {
|
||||
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ import (
|
|||
|
||||
// The fields below define the low level database schema prefixing.
|
||||
var (
|
||||
// headBlockKey tracks the latest known full block's hash.
|
||||
headBlockKey = []byte("LastBlock")
|
||||
|
||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
)
|
||||
|
||||
// senderCacher is a concurrent tranaction sender recoverer anc cacher.
|
||||
var senderCacher = newTxSenderCacher(runtime.NumCPU())
|
||||
// SenderCacher is a concurrent transaction sender recoverer and cacher.
|
||||
var SenderCacher = newTxSenderCacher(runtime.NumCPU())
|
||||
|
||||
// txSenderCacherRequest is a request for recovering transaction senders with a
|
||||
// specific signature scheme and caching it into the transactions themselves.
|
||||
|
|
@ -67,10 +67,10 @@ func (cacher *txSenderCacher) cache() {
|
|||
}
|
||||
}
|
||||
|
||||
// recover recovers the senders from a batch of transactions and caches them
|
||||
// Recover recovers the senders from a batch of transactions and caches them
|
||||
// back into the same data structures. There is no validation being done, nor
|
||||
// any reaction to invalid signatures. That is up to calling code later.
|
||||
func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transaction) {
|
||||
func (cacher *txSenderCacher) Recover(signer types.Signer, txs []*types.Transaction) {
|
||||
// If there's nothing to recover, abort
|
||||
if len(txs) == 0 {
|
||||
return
|
||||
|
|
@ -89,10 +89,10 @@ func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transact
|
|||
}
|
||||
}
|
||||
|
||||
// recoverFromBlocks recovers the senders from a batch of blocks and caches them
|
||||
// RecoverFromBlocks recovers the senders from a batch of blocks and caches them
|
||||
// back into the same data structures. There is no validation being done, nor
|
||||
// any reaction to invalid signatures. That is up to calling code later.
|
||||
func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*types.Block) {
|
||||
func (cacher *txSenderCacher) RecoverFromBlocks(signer types.Signer, blocks []*types.Block) {
|
||||
count := 0
|
||||
for _, block := range blocks {
|
||||
count += len(block.Transactions())
|
||||
|
|
@ -101,5 +101,5 @@ func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*t
|
|||
for _, block := range blocks {
|
||||
txs = append(txs, block.Transactions()...)
|
||||
}
|
||||
cacher.recover(signer, txs)
|
||||
cacher.Recover(signer, txs)
|
||||
}
|
||||
|
|
@ -643,7 +643,6 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
func (s *StateDB) Prepare(thash common.Hash, ti int) {
|
||||
s.thash = thash
|
||||
s.txIndex = ti
|
||||
s.accessList = newAccessList()
|
||||
}
|
||||
|
||||
// DeleteSuicides flags the suicided objects for deletion so that it
|
||||
|
|
@ -728,6 +727,9 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
|
|||
//
|
||||
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
|
||||
func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
|
||||
// Clear out any leftover from previous executions
|
||||
s.accessList = newAccessList()
|
||||
|
||||
s.AddAddressToAccessList(sender)
|
||||
if dst != nil {
|
||||
s.AddAddressToAccessList(*dst)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
|
|||
}
|
||||
}
|
||||
statedb.Prepare(tx.Hash(), i)
|
||||
receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, blockHash, tx, usedGas, vmenv)
|
||||
receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
|
@ -198,7 +198,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
|||
}
|
||||
}
|
||||
statedb.Prepare(tx.Hash(), i)
|
||||
receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, blockHash, tx, usedGas, vmenv)
|
||||
receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
|
@ -220,7 +220,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
|||
return receipts, allLogs, *usedGas, nil
|
||||
}
|
||||
|
||||
func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, gp *GasPool, statedb *state.StateDB, coinbaseOwner common.Address, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) {
|
||||
func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, gp *GasPool, statedb *state.StateDB, coinbaseOwner common.Address, blockNumber, baseFee *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) {
|
||||
to := tx.To()
|
||||
if to != nil {
|
||||
if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) {
|
||||
|
|
@ -246,7 +246,8 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
|||
balanceFee = value
|
||||
}
|
||||
}
|
||||
msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber)
|
||||
// msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber)
|
||||
msg, err := tx.AsMessage(types.MakeSigner(config, blockNumber), balanceFee, blockNumber, baseFee)
|
||||
if err != nil {
|
||||
return nil, 0, err, false
|
||||
}
|
||||
|
|
@ -465,7 +466,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
|||
blockContext := NewEVMBlockContext(header, bc, author)
|
||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg)
|
||||
coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author)
|
||||
return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.Hash(), tx, usedGas, vmenv)
|
||||
return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv)
|
||||
}
|
||||
|
||||
func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) {
|
||||
|
|
|
|||
290
core/state_processor_test.go
Normal file
290
core/state_processor_test.go
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// TestStateProcessorErrors tests the output from the 'core' errors
|
||||
// as defined in core/error.go. These errors are generated when the
|
||||
// blockchain imports bad blocks, meaning blocks which have valid headers but
|
||||
// contain invalid transactions
|
||||
func TestStateProcessorErrors(t *testing.T) {
|
||||
var (
|
||||
config = ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(0),
|
||||
Eip1559Block: big.NewInt(0),
|
||||
Ethash: new(params.EthashConfig),
|
||||
}
|
||||
signer = types.LatestSigner(config)
|
||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
)
|
||||
var makeTx = func(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction {
|
||||
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
|
||||
signedTx, err := types.SignTx(tx, signer, testKey)
|
||||
if err != nil {
|
||||
t.Fatalf("fail to sign tx: %v, err: %v", tx, err)
|
||||
}
|
||||
return signedTx
|
||||
}
|
||||
var mkDynamicTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *big.Int) *types.Transaction {
|
||||
tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
|
||||
Nonce: nonce,
|
||||
GasTipCap: gasTipCap,
|
||||
GasFeeCap: gasFeeCap,
|
||||
Gas: gasLimit,
|
||||
To: &to,
|
||||
Value: big.NewInt(0),
|
||||
}), signer, testKey)
|
||||
return tx
|
||||
}
|
||||
{ // Tests against a 'recent' chain definition
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: config,
|
||||
Alloc: GenesisAlloc{
|
||||
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
|
||||
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||
Nonce: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||
tooBigNumber := new(big.Int).Set(bigNumber)
|
||||
tooBigNumber.Add(tooBigNumber, common.Big1)
|
||||
for i, tt := range []struct {
|
||||
txs []*types.Transaction
|
||||
want string
|
||||
}{
|
||||
{ // ErrNonceTooLow
|
||||
txs: []*types.Transaction{
|
||||
makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
|
||||
makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
|
||||
},
|
||||
want: "nonce too low: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1",
|
||||
},
|
||||
{ // ErrNonceTooHigh
|
||||
txs: []*types.Transaction{
|
||||
makeTx(100, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(875000000), nil),
|
||||
},
|
||||
want: "nonce too high: address xdc71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0",
|
||||
},
|
||||
{ // ErrGasLimitReached
|
||||
txs: []*types.Transaction{
|
||||
makeTx(0, common.Address{}, big.NewInt(0), 21000000, big.NewInt(875000000), nil),
|
||||
},
|
||||
want: "gas limit reached",
|
||||
},
|
||||
{ // ErrInsufficientFundsForTransfer
|
||||
txs: []*types.Transaction{
|
||||
makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil),
|
||||
},
|
||||
want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7",
|
||||
},
|
||||
{ // ErrInsufficientFunds
|
||||
txs: []*types.Transaction{
|
||||
makeTx(0, common.Address{}, big.NewInt(0), params.TxGas, big.NewInt(900000000000000000), nil),
|
||||
},
|
||||
want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 18900000000000000000000",
|
||||
},
|
||||
// ErrGasUintOverflow
|
||||
// One missing 'core' error is ErrGasUintOverflow: "gas uint64 overflow",
|
||||
// In order to trigger that one, we'd have to allocate a _huge_ chunk of data, such that the
|
||||
// multiplication len(data) +gas_per_byte overflows uint64. Not testable at the moment
|
||||
{ // ErrIntrinsicGas
|
||||
txs: []*types.Transaction{
|
||||
makeTx(0, common.Address{}, big.NewInt(0), params.TxGas-1000, big.NewInt(875000000), nil),
|
||||
},
|
||||
want: "intrinsic gas too low: have 20000, want 21000",
|
||||
},
|
||||
{ // ErrGasLimitReached
|
||||
txs: []*types.Transaction{
|
||||
makeTx(0, common.Address{}, big.NewInt(0), params.TxGas*1000, big.NewInt(875000000), nil),
|
||||
},
|
||||
want: "gas limit reached",
|
||||
},
|
||||
{ // ErrFeeCapTooLow
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(0), big.NewInt(0)),
|
||||
},
|
||||
want: "fee cap less than block base fee: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0 baseFee: 875000000",
|
||||
},
|
||||
{ // ErrTipVeryHigh
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicTx(0, common.Address{}, params.TxGas, tooBigNumber, big.NewInt(1)),
|
||||
},
|
||||
want: "max priority fee per gas higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas bit length: 257",
|
||||
},
|
||||
{ // ErrFeeCapVeryHigh
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), tooBigNumber),
|
||||
},
|
||||
want: "max fee per gas higher than 2^256-1: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas bit length: 257",
|
||||
},
|
||||
{ // ErrTipAboveFeeCap
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(2), big.NewInt(1)),
|
||||
},
|
||||
want: "max priority fee per gas higher than max fee per gas: address xdc71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas: 2, maxFeePerGas: 1",
|
||||
},
|
||||
{ // ErrInsufficientFunds
|
||||
// Available balance: 1000000000000000000
|
||||
// Effective cost: 18375000021000
|
||||
// FeeCap * gas: 1050000000000000000
|
||||
// This test is designed to have the effective cost be covered by the balance, but
|
||||
// the extended requirement on FeeCap*gas < balance to fail
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicTx(0, common.Address{}, params.TxGas, big.NewInt(1), big.NewInt(50000000000000)),
|
||||
},
|
||||
want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1050000000000000000",
|
||||
},
|
||||
{ // Another ErrInsufficientFunds, this one to ensure that feecap/tip of max u256 is allowed
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber),
|
||||
},
|
||||
want: "insufficient funds for gas * price + value: address xdc71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
|
||||
},
|
||||
}[8:] {
|
||||
block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||
if err == nil {
|
||||
t.Fatal("block imported without errors")
|
||||
}
|
||||
if have, want := err.Error(), tt.want; have != want {
|
||||
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// One final error is ErrTxTypeNotSupported. For this, we need an older chain
|
||||
{
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
},
|
||||
Alloc: GenesisAlloc{
|
||||
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
|
||||
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||
Nonce: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
for i, tt := range []struct {
|
||||
txs []*types.Transaction
|
||||
want string
|
||||
}{
|
||||
{ // ErrTxTypeNotSupported
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
||||
},
|
||||
want: "transaction type not supported",
|
||||
},
|
||||
} {
|
||||
block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||
if err == nil {
|
||||
t.Fatal("block imported without errors")
|
||||
}
|
||||
if have, want := err.Error(), tt.want; have != want {
|
||||
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be
|
||||
// valid, and no proper post-state can be made. But from the perspective of the blockchain, the block is sufficiently
|
||||
// valid to be considered for import:
|
||||
// - valid pow (fake), ancestry, difficulty, gaslimit etc
|
||||
func GenerateBadBlock(t *testing.T, parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
|
||||
header := &types.Header{
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
Difficulty: engine.CalcDifficulty(&fakeChainReader{config}, parent.Time().Uint64()+10, &types.Header{
|
||||
Number: parent.Number(),
|
||||
Time: parent.Time(),
|
||||
Difficulty: parent.Difficulty(),
|
||||
UncleHash: parent.UncleHash(),
|
||||
}),
|
||||
GasLimit: parent.GasLimit(),
|
||||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||
Time: new(big.Int).SetUint64(parent.Time().Uint64() + 10),
|
||||
UncleHash: types.EmptyUncleHash,
|
||||
}
|
||||
if config.IsEIP1559(header.Number) {
|
||||
header.BaseFee = common.BaseFee
|
||||
}
|
||||
var receipts []*types.Receipt
|
||||
// The post-state result doesn't need to be correct (this is a bad block), but we do need something there
|
||||
// Preferably something unique. So let's use a combo of blocknum + txhash
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write(header.Number.Bytes())
|
||||
var cumulativeGas uint64
|
||||
for _, tx := range txs {
|
||||
txh := tx.Hash()
|
||||
hasher.Write(txh[:])
|
||||
receipt := types.NewReceipt(nil, false, cumulativeGas+tx.Gas())
|
||||
receipt.TxHash = tx.Hash()
|
||||
receipt.GasUsed = tx.Gas()
|
||||
receipts = append(receipts, receipt)
|
||||
cumulativeGas += tx.Gas()
|
||||
}
|
||||
header.Root = common.BytesToHash(hasher.Sum(nil))
|
||||
// Assemble and return the final block for sealing
|
||||
return types.NewBlock(header, txs, nil, receipts)
|
||||
}
|
||||
|
|
@ -23,12 +23,16 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
cmath "github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
var emptyCodeHash = crypto.Keccak256Hash(nil)
|
||||
|
||||
var (
|
||||
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
|
||||
)
|
||||
|
|
@ -57,6 +61,8 @@ type StateTransition struct {
|
|||
msg Message
|
||||
gas uint64
|
||||
gasPrice *big.Int
|
||||
gasFeeCap *big.Int
|
||||
gasTipCap *big.Int
|
||||
initialGas uint64
|
||||
value *big.Int
|
||||
data []byte
|
||||
|
|
@ -71,18 +77,20 @@ type Message interface {
|
|||
To() *common.Address
|
||||
|
||||
GasPrice() *big.Int
|
||||
GasFeeCap() *big.Int
|
||||
GasTipCap() *big.Int
|
||||
Gas() uint64
|
||||
Value() *big.Int
|
||||
|
||||
Nonce() uint64
|
||||
CheckNonce() bool
|
||||
IsFake() bool
|
||||
Data() []byte
|
||||
BalanceTokenFee() *big.Int
|
||||
AccessList() types.AccessList
|
||||
}
|
||||
|
||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool) (uint64, error) {
|
||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool, isEIP3860 bool) (uint64, error) {
|
||||
// Set the starting gas for the raw transaction
|
||||
var gas uint64
|
||||
if isContractCreation && isHomestead {
|
||||
|
|
@ -90,8 +98,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
|
|||
} else {
|
||||
gas = params.TxGas
|
||||
}
|
||||
dataLen := uint64(len(data))
|
||||
// Bump the required gas by the amount of transactional data
|
||||
if len(data) > 0 {
|
||||
if dataLen > 0 {
|
||||
// Zero and non-zero bytes are priced differently
|
||||
var nz uint64
|
||||
for _, byt := range data {
|
||||
|
|
@ -105,11 +114,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
|
|||
}
|
||||
gas += nz * params.TxDataNonZeroGas
|
||||
|
||||
z := uint64(len(data)) - nz
|
||||
z := dataLen - nz
|
||||
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas += z * params.TxDataZeroGas
|
||||
|
||||
if isContractCreation && isEIP3860 {
|
||||
lenWords := toWordSize(dataLen)
|
||||
if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
gas += lenWords * params.InitCodeWordGas
|
||||
}
|
||||
}
|
||||
if accessList != nil {
|
||||
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||
|
|
@ -118,16 +135,27 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
|
|||
return gas, nil
|
||||
}
|
||||
|
||||
// toWordSize returns the ceiled word size required for init code payment calculation.
|
||||
func toWordSize(size uint64) uint64 {
|
||||
if size > math.MaxUint64-31 {
|
||||
return math.MaxUint64/32 + 1
|
||||
}
|
||||
|
||||
return (size + 31) / 32
|
||||
}
|
||||
|
||||
// NewStateTransition initialises and returns a new state transition object.
|
||||
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
|
||||
return &StateTransition{
|
||||
gp: gp,
|
||||
evm: evm,
|
||||
msg: msg,
|
||||
gasPrice: msg.GasPrice(),
|
||||
value: msg.Value(),
|
||||
data: msg.Data(),
|
||||
state: evm.StateDB,
|
||||
gp: gp,
|
||||
evm: evm,
|
||||
msg: msg,
|
||||
gasPrice: msg.GasPrice(),
|
||||
gasFeeCap: msg.GasFeeCap(),
|
||||
gasTipCap: msg.GasTipCap(),
|
||||
value: msg.Value(),
|
||||
data: msg.Data(),
|
||||
state: evm.StateDB,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,15 +199,18 @@ func (st *StateTransition) to() vm.AccountRef {
|
|||
}
|
||||
|
||||
func (st *StateTransition) buyGas() error {
|
||||
var (
|
||||
state = st.state
|
||||
balanceTokenFee = st.balanceTokenFee()
|
||||
from = st.from()
|
||||
)
|
||||
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
|
||||
mgval := new(big.Int).SetUint64(st.msg.Gas())
|
||||
mgval = mgval.Mul(mgval, st.gasPrice)
|
||||
balanceTokenFee := st.balanceTokenFee()
|
||||
if balanceTokenFee == nil {
|
||||
if state.GetBalance(from.Address()).Cmp(mgval) < 0 {
|
||||
return errInsufficientBalanceForGas
|
||||
balanceCheck := mgval
|
||||
if st.gasFeeCap != nil {
|
||||
balanceCheck = new(big.Int).SetUint64(st.msg.Gas())
|
||||
balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap)
|
||||
balanceCheck.Add(balanceCheck, st.value)
|
||||
}
|
||||
if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
|
||||
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
|
||||
}
|
||||
} else if balanceTokenFee.Cmp(mgval) < 0 {
|
||||
return errInsufficientBalanceForGas
|
||||
|
|
@ -191,25 +222,55 @@ func (st *StateTransition) buyGas() error {
|
|||
|
||||
st.initialGas = st.msg.Gas()
|
||||
if balanceTokenFee == nil {
|
||||
state.SubBalance(from.Address(), mgval)
|
||||
st.state.SubBalance(st.msg.From(), mgval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *StateTransition) preCheck() error {
|
||||
// Make sure this transaction's nonce is correct
|
||||
if st.msg.CheckNonce() {
|
||||
// Only check transactions that are not fake
|
||||
msg := st.msg
|
||||
if !msg.IsFake() {
|
||||
// Make sure this transaction's nonce is correct.
|
||||
stNonce := st.state.GetNonce(st.from().Address())
|
||||
if msgNonce := st.msg.Nonce(); stNonce < msgNonce {
|
||||
stNonce := st.state.GetNonce(msg.From())
|
||||
if msgNonce := msg.Nonce(); stNonce < msgNonce {
|
||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
|
||||
st.msg.From().Hex(), msgNonce, stNonce)
|
||||
msg.From().Hex(), msgNonce, stNonce)
|
||||
} else if stNonce > msgNonce {
|
||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
|
||||
st.msg.From().Hex(), msgNonce, stNonce)
|
||||
msg.From().Hex(), msgNonce, stNonce)
|
||||
} else if stNonce+1 < stNonce {
|
||||
return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
|
||||
st.msg.From().Hex(), stNonce)
|
||||
msg.From().Hex(), stNonce)
|
||||
}
|
||||
// Make sure the sender is an EOA
|
||||
if codeHash := st.state.GetCodeHash(msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) {
|
||||
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
|
||||
msg.From().Hex(), codeHash)
|
||||
}
|
||||
}
|
||||
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
|
||||
if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) {
|
||||
// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
|
||||
if !st.evm.Config.NoBaseFee || st.gasFeeCap.BitLen() > 0 || st.gasTipCap.BitLen() > 0 {
|
||||
if l := st.gasFeeCap.BitLen(); l > 256 {
|
||||
return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
|
||||
msg.From().Hex(), l)
|
||||
}
|
||||
if l := st.gasTipCap.BitLen(); l > 256 {
|
||||
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
|
||||
msg.From().Hex(), l)
|
||||
}
|
||||
if st.gasFeeCap.Cmp(st.gasTipCap) < 0 {
|
||||
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
|
||||
msg.From().Hex(), st.gasTipCap, st.gasFeeCap)
|
||||
}
|
||||
// This will panic if baseFee is nil, but basefee presence is verified
|
||||
// as part of header validation.
|
||||
if st.gasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 {
|
||||
return fmt.Errorf("%w: address %v, maxFeePerGas: %s baseFee: %s", ErrFeeCapTooLow,
|
||||
msg.From().Hex(), st.gasFeeCap, st.evm.Context.BaseFee)
|
||||
}
|
||||
}
|
||||
}
|
||||
return st.buyGas()
|
||||
|
|
@ -241,16 +302,20 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
|||
|
||||
// Check clauses 1-3, buy gas if everything is correct
|
||||
if err = st.preCheck(); err != nil {
|
||||
return
|
||||
return nil, 0, false, err, nil
|
||||
}
|
||||
msg := st.msg
|
||||
sender := st.from() // err checked in preCheck
|
||||
|
||||
homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
|
||||
contractCreation := msg.To() == nil
|
||||
var (
|
||||
msg = st.msg
|
||||
sender = st.from() // err checked in preCheck
|
||||
rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber)
|
||||
homestead = rules.IsHomestead
|
||||
eip3529 = rules.IsEIP1559
|
||||
contractCreation = msg.To() == nil
|
||||
)
|
||||
|
||||
// Pay intrinsic gas
|
||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead)
|
||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, rules.IsEIP1559)
|
||||
if err != nil {
|
||||
return nil, 0, false, err, nil
|
||||
}
|
||||
|
|
@ -259,7 +324,12 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
|||
}
|
||||
st.gas -= gas
|
||||
|
||||
if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber); rules.IsEIP1559 {
|
||||
// Check whether the init code size has been exceeded.
|
||||
if rules.IsEIP1559 && contractCreation && len(st.data) > params.MaxInitCodeSize {
|
||||
return nil, 0, false, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil
|
||||
}
|
||||
|
||||
if rules.IsEIP1559 {
|
||||
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||
}
|
||||
|
||||
|
|
@ -293,22 +363,32 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
|||
return nil, 0, false, vmerr, nil
|
||||
}
|
||||
}
|
||||
st.refundGas()
|
||||
if !eip3529 {
|
||||
// Before EIP-3529: refunds were capped to gasUsed / 2
|
||||
st.refundGas(params.RefundQuotient)
|
||||
} else {
|
||||
// After EIP-3529: refunds are capped to gasUsed / 5
|
||||
st.refundGas(params.RefundQuotientEIP3529)
|
||||
}
|
||||
|
||||
if st.evm.Context.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 {
|
||||
if (owner != common.Address{}) {
|
||||
st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
||||
}
|
||||
} else {
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
||||
effectiveTip := st.gasPrice
|
||||
if st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber) {
|
||||
effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
|
||||
}
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
|
||||
}
|
||||
|
||||
return ret, st.gasUsed(), vmerr != nil, nil, vmerr
|
||||
}
|
||||
|
||||
func (st *StateTransition) refundGas() {
|
||||
// Apply refund counter, capped to half of the used gas.
|
||||
refund := st.gasUsed() / 2
|
||||
func (st *StateTransition) refundGas(refundQuotient uint64) {
|
||||
// Apply refund counter, capped to a refund quotient
|
||||
refund := st.gasUsed() / refundQuotient
|
||||
if refund > st.state.GetRefund() {
|
||||
refund = st.state.GetRefund()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,11 @@ type callMsg struct {
|
|||
|
||||
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
||||
func (m callMsg) Nonce() uint64 { return 0 }
|
||||
func (m callMsg) CheckNonce() bool { return false }
|
||||
func (m callMsg) IsFake() bool { return true }
|
||||
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
|
||||
func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
|
||||
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
|
||||
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
||||
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -40,23 +40,23 @@ type devNull struct{}
|
|||
func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil }
|
||||
func (*devNull) Close() error { return nil }
|
||||
|
||||
// txJournal is a rotating log of transactions with the aim of storing locally
|
||||
// journal is a rotating log of transactions with the aim of storing locally
|
||||
// created transactions to allow non-executed ones to survive node restarts.
|
||||
type txJournal struct {
|
||||
type journal struct {
|
||||
path string // Filesystem path to store the transactions at
|
||||
writer io.WriteCloser // Output stream to write new transactions into
|
||||
}
|
||||
|
||||
// newTxJournal creates a new transaction journal to
|
||||
func newTxJournal(path string) *txJournal {
|
||||
return &txJournal{
|
||||
func newTxJournal(path string) *journal {
|
||||
return &journal{
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
// load parses a transaction journal dump from disk, loading its contents into
|
||||
// the specified pool.
|
||||
func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
|
||||
func (journal *journal) load(add func([]*types.Transaction) []error) error {
|
||||
// Skip the parsing if the journal file doens't exist at all
|
||||
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
|
||||
return nil
|
||||
|
|
@ -116,7 +116,7 @@ func (journal *txJournal) load(add func([]*types.Transaction) []error) error {
|
|||
}
|
||||
|
||||
// insert adds the specified transaction to the local disk journal.
|
||||
func (journal *txJournal) insert(tx *types.Transaction) error {
|
||||
func (journal *journal) insert(tx *types.Transaction) error {
|
||||
if journal.writer == nil {
|
||||
return errNoActiveJournal
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ func (journal *txJournal) insert(tx *types.Transaction) error {
|
|||
|
||||
// rotate regenerates the transaction journal based on the current contents of
|
||||
// the transaction pool.
|
||||
func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error {
|
||||
func (journal *journal) rotate(all map[common.Address]types.Transactions) error {
|
||||
// Close the current journal (if any is open)
|
||||
if journal.writer != nil {
|
||||
if err := journal.writer.Close(); err != nil {
|
||||
|
|
@ -168,7 +168,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
|
|||
}
|
||||
|
||||
// close flushes the transaction journal contents to disk and closes the file.
|
||||
func (journal *txJournal) close() error {
|
||||
func (journal *journal) close() error {
|
||||
var err error
|
||||
|
||||
if journal.writer != nil {
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
|
|
@ -75,7 +76,7 @@ type blockChainLending interface {
|
|||
GetBlock(hash common.Hash, number uint64) *types.Block
|
||||
LendingStateAt(block *types.Block) (*lendingstate.LendingStateDB, error)
|
||||
StateAt(root common.Hash) (*state.StateDB, error)
|
||||
SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
Engine() consensus.Engine
|
||||
// GetHeader returns the hash corresponding to their hash.
|
||||
GetHeader(common.Hash, uint64) *types.Header
|
||||
|
|
@ -124,7 +125,7 @@ type LendingPool struct {
|
|||
|
||||
txFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
chainHeadCh chan ChainHeadEvent
|
||||
chainHeadCh chan core.ChainHeadEvent
|
||||
chainHeadSub event.Subscription
|
||||
signer types.LendingSigner
|
||||
mu sync.RWMutex
|
||||
|
|
@ -161,7 +162,7 @@ func NewLendingPool(chainconfig *params.ChainConfig, chain blockChainLending) *L
|
|||
queue: make(map[common.Address]*lendingtxList),
|
||||
beats: make(map[common.Address]time.Time),
|
||||
all: make(map[common.Hash]*types.LendingTransaction),
|
||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||
}
|
||||
pool.locals = newLendingAccountSet(pool.signer)
|
||||
pool.reset(nil, chain.CurrentBlock())
|
||||
|
|
@ -334,7 +335,7 @@ func (pool *LendingPool) Stop() {
|
|||
|
||||
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
|
||||
// starts sending event to the given channel.
|
||||
func (pool *LendingPool) SubscribeTxPreEvent(ch chan<- LendingTxPreEvent) event.Subscription {
|
||||
func (pool *LendingPool) SubscribeTxPreEvent(ch chan<- core.LendingTxPreEvent) event.Subscription {
|
||||
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
|
|
@ -514,7 +515,7 @@ func (pool *LendingPool) validateTopupLending(cloneStateDb *state.StateDB, clone
|
|||
func (pool *LendingPool) validateBalance(cloneStateDb *state.StateDB, cloneLendingStateDb *lendingstate.LendingStateDB, tx *types.LendingTransaction, collateralToken common.Address) error {
|
||||
XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS)
|
||||
if !ok {
|
||||
return ErrNotXDPoS
|
||||
return core.ErrNotXDPoS
|
||||
}
|
||||
XDCXServ := XDPoSEngine.GetXDCXService()
|
||||
lendingServ := XDPoSEngine.GetLendingService()
|
||||
|
|
@ -641,10 +642,10 @@ func (pool *LendingPool) validateTx(tx *types.LendingTransaction, local bool) er
|
|||
}
|
||||
// Ensure the transaction adheres to nonce lending
|
||||
if pool.currentLendingState.GetNonce(from.Hash()) > tx.Nonce() {
|
||||
return ErrNonceTooLow
|
||||
return core.ErrNonceTooLow
|
||||
}
|
||||
if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
||||
return ErrNonceTooHigh
|
||||
return core.ErrNonceTooHigh
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -778,7 +779,7 @@ func (pool *LendingPool) promoteTx(addr common.Address, hash common.Hash, tx *ty
|
|||
pool.beats[addr] = time.Now()
|
||||
pool.pendingState.SetNonce(addr.Hash(), tx.Nonce()+1)
|
||||
|
||||
go pool.txFeed.Send(LendingTxPreEvent{tx})
|
||||
go pool.txFeed.Send(core.LendingTxPreEvent{Tx: tx})
|
||||
}
|
||||
|
||||
// AddLocal enqueues a single transaction into the pool if it is valid, marking
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
|
|
@ -48,30 +49,30 @@ func (h *nonceHeap) Pop() interface{} {
|
|||
return x
|
||||
}
|
||||
|
||||
// txSortedMap is a nonce->transaction hash map with a heap based index to allow
|
||||
// sortedMap is a nonce->transaction hash map with a heap based index to allow
|
||||
// iterating over the contents in a nonce-incrementing way.
|
||||
type txSortedMap struct {
|
||||
type sortedMap struct {
|
||||
items map[uint64]*types.Transaction // Hash map storing the transaction data
|
||||
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
|
||||
cache types.Transactions // Cache of the transactions already sorted
|
||||
}
|
||||
|
||||
// newTxSortedMap creates a new nonce-sorted transaction map.
|
||||
func newTxSortedMap() *txSortedMap {
|
||||
return &txSortedMap{
|
||||
// newSortedMap creates a new nonce-sorted transaction map.
|
||||
func newSortedMap() *sortedMap {
|
||||
return &sortedMap{
|
||||
items: make(map[uint64]*types.Transaction),
|
||||
index: new(nonceHeap),
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves the current transactions associated with the given nonce.
|
||||
func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
|
||||
func (m *sortedMap) Get(nonce uint64) *types.Transaction {
|
||||
return m.items[nonce]
|
||||
}
|
||||
|
||||
// Put inserts a new transaction into the map, also updating the map's nonce
|
||||
// index. If a transaction already exists with the same nonce, it's overwritten.
|
||||
func (m *txSortedMap) Put(tx *types.Transaction) {
|
||||
func (m *sortedMap) Put(tx *types.Transaction) {
|
||||
nonce := tx.Nonce()
|
||||
if m.items[nonce] == nil {
|
||||
heap.Push(m.index, nonce)
|
||||
|
|
@ -82,7 +83,7 @@ func (m *txSortedMap) Put(tx *types.Transaction) {
|
|||
// Forward removes all transactions from the map with a nonce lower than the
|
||||
// provided threshold. Every removed transaction is returned for any post-removal
|
||||
// maintenance.
|
||||
func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
|
||||
func (m *sortedMap) Forward(threshold uint64) types.Transactions {
|
||||
var removed types.Transactions
|
||||
|
||||
// Pop off heap items until the threshold is reached
|
||||
|
|
@ -103,7 +104,7 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
|
|||
// Filter, as opposed to 'filter', re-initialises the heap after the operation is done.
|
||||
// If you want to do several consecutive filterings, it's therefore better to first
|
||||
// do a .filter(func1) followed by .Filter(func2) or reheap()
|
||||
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||
func (m *sortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||
removed := m.filter(filter)
|
||||
// If transactions were removed, the heap and cache are ruined
|
||||
if len(removed) > 0 {
|
||||
|
|
@ -112,7 +113,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
|
|||
return removed
|
||||
}
|
||||
|
||||
func (m *txSortedMap) reheap() {
|
||||
func (m *sortedMap) reheap() {
|
||||
*m.index = make([]uint64, 0, len(m.items))
|
||||
for nonce := range m.items {
|
||||
*m.index = append(*m.index, nonce)
|
||||
|
|
@ -123,7 +124,7 @@ func (m *txSortedMap) reheap() {
|
|||
|
||||
// filter is identical to Filter, but **does not** regenerate the heap. This method
|
||||
// should only be used if followed immediately by a call to Filter or reheap()
|
||||
func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||
func (m *sortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||
var removed types.Transactions
|
||||
|
||||
// Collect all the transactions to filter out
|
||||
|
|
@ -141,7 +142,7 @@ func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transac
|
|||
|
||||
// Cap places a hard limit on the number of items, returning all transactions
|
||||
// exceeding that limit.
|
||||
func (m *txSortedMap) Cap(threshold int) types.Transactions {
|
||||
func (m *sortedMap) Cap(threshold int) types.Transactions {
|
||||
// Short circuit if the number of items is under the limit
|
||||
if len(m.items) <= threshold {
|
||||
return nil
|
||||
|
|
@ -166,7 +167,7 @@ func (m *txSortedMap) Cap(threshold int) types.Transactions {
|
|||
|
||||
// Remove deletes a transaction from the maintained map, returning whether the
|
||||
// transaction was found.
|
||||
func (m *txSortedMap) Remove(nonce uint64) bool {
|
||||
func (m *sortedMap) Remove(nonce uint64) bool {
|
||||
// Short circuit if no transaction is present
|
||||
_, ok := m.items[nonce]
|
||||
if !ok {
|
||||
|
|
@ -192,7 +193,7 @@ func (m *txSortedMap) Remove(nonce uint64) bool {
|
|||
// Note, all transactions with nonces lower than start will also be returned to
|
||||
// prevent getting into and invalid state. This is not something that should ever
|
||||
// happen but better to be self correcting than failing!
|
||||
func (m *txSortedMap) Ready(start uint64) types.Transactions {
|
||||
func (m *sortedMap) Ready(start uint64) types.Transactions {
|
||||
// Short circuit if no transactions are available
|
||||
if m.index.Len() == 0 || (*m.index)[0] > start {
|
||||
return nil
|
||||
|
|
@ -210,11 +211,11 @@ func (m *txSortedMap) Ready(start uint64) types.Transactions {
|
|||
}
|
||||
|
||||
// Len returns the length of the transaction map.
|
||||
func (m *txSortedMap) Len() int {
|
||||
func (m *sortedMap) Len() int {
|
||||
return len(m.items)
|
||||
}
|
||||
|
||||
func (m *txSortedMap) flatten() types.Transactions {
|
||||
func (m *sortedMap) flatten() types.Transactions {
|
||||
// If the sorting was not cached yet, create and cache it
|
||||
if m.cache == nil {
|
||||
m.cache = make(types.Transactions, 0, len(m.items))
|
||||
|
|
@ -229,7 +230,7 @@ func (m *txSortedMap) flatten() types.Transactions {
|
|||
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||
// sorted internal representation. The result of the sorting is cached in case
|
||||
// it's requested again before any modifications are made to the contents.
|
||||
func (m *txSortedMap) Flatten() types.Transactions {
|
||||
func (m *sortedMap) Flatten() types.Transactions {
|
||||
// Copy the cache to prevent accidental modifications
|
||||
cache := m.flatten()
|
||||
txs := make(types.Transactions, len(cache))
|
||||
|
|
@ -239,36 +240,36 @@ func (m *txSortedMap) Flatten() types.Transactions {
|
|||
|
||||
// LastElement returns the last element of a flattened list, thus, the
|
||||
// transaction with the highest nonce
|
||||
func (m *txSortedMap) LastElement() *types.Transaction {
|
||||
func (m *sortedMap) LastElement() *types.Transaction {
|
||||
cache := m.flatten()
|
||||
return cache[len(cache)-1]
|
||||
}
|
||||
|
||||
// txList is a "list" of transactions belonging to an account, sorted by account
|
||||
// list is a "list" of transactions belonging to an account, sorted by account
|
||||
// nonce. The same type can be used both for storing contiguous transactions for
|
||||
// the executable/pending queue; and for storing gapped transactions for the non-
|
||||
// executable/future queue, with minor behavioral changes.
|
||||
type txList struct {
|
||||
strict bool // Whether nonces are strictly continuous or not
|
||||
txs *txSortedMap // Heap indexed sorted hash map of the transactions
|
||||
type list struct {
|
||||
strict bool // Whether nonces are strictly continuous or not
|
||||
txs *sortedMap // Heap indexed sorted hash map of the transactions
|
||||
|
||||
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
|
||||
gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
|
||||
}
|
||||
|
||||
// newTxList create a new transaction list for maintaining nonce-indexable fast,
|
||||
// newList create a new transaction list for maintaining nonce-indexable fast,
|
||||
// gapped, sortable transaction lists.
|
||||
func newTxList(strict bool) *txList {
|
||||
return &txList{
|
||||
func newList(strict bool) *list {
|
||||
return &list{
|
||||
strict: strict,
|
||||
txs: newTxSortedMap(),
|
||||
txs: newSortedMap(),
|
||||
costcap: new(big.Int),
|
||||
}
|
||||
}
|
||||
|
||||
// Overlaps returns whether the transaction specified has the same nonce as one
|
||||
// already contained within the list.
|
||||
func (l *txList) Overlaps(tx *types.Transaction) bool {
|
||||
func (l *list) Overlaps(tx *types.Transaction) bool {
|
||||
return l.txs.Get(tx.Nonce()) != nil
|
||||
}
|
||||
|
||||
|
|
@ -277,22 +278,30 @@ func (l *txList) Overlaps(tx *types.Transaction) bool {
|
|||
//
|
||||
// If the new transaction is accepted into the list, the lists' cost and gas
|
||||
// thresholds are also potentially updated.
|
||||
func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
|
||||
func (l *list) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) {
|
||||
// If there's an older better transaction, abort
|
||||
old := l.txs.Get(tx.Nonce())
|
||||
if old != nil && old.IsSpecialTransaction() {
|
||||
return false, nil
|
||||
}
|
||||
if old != nil {
|
||||
// threshold = oldGP * (100 + priceBump) / 100
|
||||
if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 {
|
||||
return false, nil
|
||||
}
|
||||
// thresholdFeeCap = oldFC * (100 + priceBump) / 100
|
||||
a := big.NewInt(100 + int64(priceBump))
|
||||
a = a.Mul(a, old.GasPrice())
|
||||
aFeeCap := new(big.Int).Mul(a, old.GasFeeCap())
|
||||
aTip := a.Mul(a, old.GasTipCap())
|
||||
|
||||
// thresholdTip = oldTip * (100 + priceBump) / 100
|
||||
b := big.NewInt(100)
|
||||
threshold := a.Div(a, b)
|
||||
// Have to ensure that the new gas price is higher than the old gas
|
||||
// price as well as checking the percentage threshold to ensure that
|
||||
thresholdFeeCap := aFeeCap.Div(aFeeCap, b)
|
||||
thresholdTip := aTip.Div(aTip, b)
|
||||
|
||||
// Have to ensure that either the new fee cap or tip is higher than the
|
||||
// old ones as well as checking the percentage threshold to ensure that
|
||||
// this is accurate for low (Wei-level) gas price replacements
|
||||
if old.GasPriceCmp(tx) >= 0 || tx.GasPriceIntCmp(threshold) < 0 {
|
||||
if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -310,7 +319,7 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
|
|||
// Forward removes all transactions from the list with a nonce lower than the
|
||||
// provided threshold. Every removed transaction is returned for any post-removal
|
||||
// maintenance.
|
||||
func (l *txList) Forward(threshold uint64) types.Transactions {
|
||||
func (l *list) Forward(threshold uint64) types.Transactions {
|
||||
return l.txs.Forward(threshold)
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +332,7 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
|
|||
// a point in calculating all the costs or if the balance covers all. If the threshold
|
||||
// is lower than the costgas cap, the caps will be reset to a new high after removing
|
||||
// the newly invalidated transactions.
|
||||
func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[common.Address]*big.Int, number *big.Int) (types.Transactions, types.Transactions) {
|
||||
func (l *list) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[common.Address]*big.Int, number *big.Int) (types.Transactions, types.Transactions) {
|
||||
// If all transactions are below the threshold, short circuit
|
||||
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
|
||||
return nil, nil
|
||||
|
|
@ -362,14 +371,14 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[co
|
|||
|
||||
// Cap places a hard limit on the number of items, returning all transactions
|
||||
// exceeding that limit.
|
||||
func (l *txList) Cap(threshold int) types.Transactions {
|
||||
func (l *list) Cap(threshold int) types.Transactions {
|
||||
return l.txs.Cap(threshold)
|
||||
}
|
||||
|
||||
// Remove deletes a transaction from the maintained list, returning whether the
|
||||
// transaction was found, and also returning any transaction invalidated due to
|
||||
// the deletion (strict mode only).
|
||||
func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
|
||||
func (l *list) Remove(tx *types.Transaction) (bool, types.Transactions) {
|
||||
// Remove the transaction from the set
|
||||
nonce := tx.Nonce()
|
||||
if removed := l.txs.Remove(nonce); !removed {
|
||||
|
|
@ -389,172 +398,206 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
|
|||
// Note, all transactions with nonces lower than start will also be returned to
|
||||
// prevent getting into and invalid state. This is not something that should ever
|
||||
// happen but better to be self correcting than failing!
|
||||
func (l *txList) Ready(start uint64) types.Transactions {
|
||||
func (l *list) Ready(start uint64) types.Transactions {
|
||||
return l.txs.Ready(start)
|
||||
}
|
||||
|
||||
// Len returns the length of the transaction list.
|
||||
func (l *txList) Len() int {
|
||||
func (l *list) Len() int {
|
||||
return l.txs.Len()
|
||||
}
|
||||
|
||||
// Empty returns whether the list of transactions is empty or not.
|
||||
func (l *txList) Empty() bool {
|
||||
func (l *list) Empty() bool {
|
||||
return l.Len() == 0
|
||||
}
|
||||
|
||||
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||
// sorted internal representation. The result of the sorting is cached in case
|
||||
// it's requested again before any modifications are made to the contents.
|
||||
func (l *txList) Flatten() types.Transactions {
|
||||
func (l *list) Flatten() types.Transactions {
|
||||
return l.txs.Flatten()
|
||||
}
|
||||
|
||||
// LastElement returns the last element of a flattened list, thus, the
|
||||
// transaction with the highest nonce
|
||||
func (l *txList) LastElement() *types.Transaction {
|
||||
func (l *list) LastElement() *types.Transaction {
|
||||
return l.txs.LastElement()
|
||||
}
|
||||
|
||||
// priceHeap is a heap.Interface implementation over transactions for retrieving
|
||||
// price-sorted transactions to discard when the pool fills up.
|
||||
type priceHeap []*types.Transaction
|
||||
// price-sorted transactions to discard when the pool fills up. If baseFee is set
|
||||
// then the heap is sorted based on the effective tip based on the given base fee.
|
||||
// If baseFee is nil then the sorting is based on gasFeeCap.
|
||||
type priceHeap struct {
|
||||
baseFee *big.Int // heap should always be re-sorted after baseFee is changed
|
||||
list []*types.Transaction
|
||||
}
|
||||
|
||||
func (h priceHeap) Len() int { return len(h) }
|
||||
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
func (h *priceHeap) Len() int { return len(h.list) }
|
||||
func (h *priceHeap) Swap(i, j int) { h.list[i], h.list[j] = h.list[j], h.list[i] }
|
||||
|
||||
func (h priceHeap) Less(i, j int) bool {
|
||||
// Sort primarily by price, returning the cheaper one
|
||||
switch h[i].GasPriceCmp(h[j]) {
|
||||
func (h *priceHeap) Less(i, j int) bool {
|
||||
switch h.cmp(h.list[i], h.list[j]) {
|
||||
case -1:
|
||||
return true
|
||||
case 1:
|
||||
return false
|
||||
default:
|
||||
return h.list[i].Nonce() > h.list[j].Nonce()
|
||||
}
|
||||
// If the prices match, stabilize via nonces (high nonce is worse)
|
||||
return h[i].Nonce() > h[j].Nonce()
|
||||
}
|
||||
|
||||
func (h *priceHeap) cmp(a, b *types.Transaction) int {
|
||||
if h.baseFee != nil {
|
||||
// Compare effective tips if baseFee is specified
|
||||
if c := a.EffectiveGasTipCmp(b, h.baseFee); c != 0 {
|
||||
return c
|
||||
}
|
||||
}
|
||||
// Compare fee caps if baseFee is not specified or effective tips are equal
|
||||
if c := a.GasFeeCapCmp(b); c != 0 {
|
||||
return c
|
||||
}
|
||||
// Compare tips if effective tips and fee caps are equal
|
||||
return a.GasTipCapCmp(b)
|
||||
}
|
||||
|
||||
func (h *priceHeap) Push(x interface{}) {
|
||||
*h = append(*h, x.(*types.Transaction))
|
||||
tx := x.(*types.Transaction)
|
||||
h.list = append(h.list, tx)
|
||||
}
|
||||
|
||||
func (h *priceHeap) Pop() interface{} {
|
||||
old := *h
|
||||
old := h.list
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
*h = old[0 : n-1]
|
||||
old[n-1] = nil
|
||||
h.list = old[0 : n-1]
|
||||
return x
|
||||
}
|
||||
|
||||
// txPricedList is a price-sorted heap to allow operating on transactions pool
|
||||
// pricedList is a price-sorted heap to allow operating on transactions pool
|
||||
// contents in a price-incrementing way. It's built opon the all transactions
|
||||
// in txpool but only interested in the remote part. It means only remote transactions
|
||||
// will be considered for tracking, sorting, eviction, etc.
|
||||
type txPricedList struct {
|
||||
all *txLookup // Pointer to the map of all transactions
|
||||
remotes *priceHeap // Heap of prices of all the stored **remote** transactions
|
||||
stales int64 // Number of stale price points to (re-heap trigger)
|
||||
reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list
|
||||
//
|
||||
// Two heaps are used for sorting: the urgent heap (based on effective tip in the next
|
||||
// block) and the floating heap (based on gasFeeCap). Always the bigger heap is chosen for
|
||||
// eviction. Transactions evicted from the urgent heap are first demoted into the floating heap.
|
||||
// In some cases (during a congestion, when blocks are full) the urgent heap can provide
|
||||
// better candidates for inclusion while in other cases (at the top of the baseFee peak)
|
||||
// the floating heap is better. When baseFee is decreasing they behave similarly.
|
||||
type pricedList struct {
|
||||
all *lookup // Pointer to the map of all transactions
|
||||
urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions
|
||||
stales int64 // Number of stale price points to (re-heap trigger)
|
||||
reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list
|
||||
}
|
||||
|
||||
// newTxPricedList creates a new price-sorted transaction heap.
|
||||
func newTxPricedList(all *txLookup) *txPricedList {
|
||||
return &txPricedList{
|
||||
all: all,
|
||||
remotes: new(priceHeap),
|
||||
const (
|
||||
// urgentRatio : floatingRatio is the capacity ratio of the two queues
|
||||
urgentRatio = 4
|
||||
floatingRatio = 1
|
||||
)
|
||||
|
||||
// newPricedList creates a new price-sorted transaction heap.
|
||||
func newPricedList(all *lookup) *pricedList {
|
||||
return &pricedList{
|
||||
all: all,
|
||||
}
|
||||
}
|
||||
|
||||
// Put inserts a new transaction into the heap.
|
||||
func (l *txPricedList) Put(tx *types.Transaction, local bool) {
|
||||
func (l *pricedList) Put(tx *types.Transaction, local bool) {
|
||||
if local {
|
||||
return
|
||||
}
|
||||
heap.Push(l.remotes, tx)
|
||||
// Insert every new transaction to the urgent heap first; Discard will balance the heaps
|
||||
heap.Push(&l.urgent, tx)
|
||||
}
|
||||
|
||||
// Removed notifies the prices transaction list that an old transaction dropped
|
||||
// from the pool. The list will just keep a counter of stale objects and update
|
||||
// the heap if a large enough ratio of transactions go stale.
|
||||
func (l *txPricedList) Removed(count int) {
|
||||
func (l *pricedList) Removed(count int) {
|
||||
// Bump the stale counter, but exit if still too low (< 25%)
|
||||
stales := atomic.AddInt64(&l.stales, int64(count))
|
||||
if int(stales) <= len(*l.remotes)/4 {
|
||||
if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 {
|
||||
return
|
||||
}
|
||||
// Seems we've reached a critical number of stale transactions, reheap
|
||||
l.Reheap()
|
||||
}
|
||||
|
||||
// Cap finds all the transactions below the given price threshold, drops them
|
||||
// from the priced list and returns them for further removal from the entire pool.
|
||||
//
|
||||
// Note: only remote transactions will be considered for eviction.
|
||||
func (l *txPricedList) Cap(threshold *big.Int) types.Transactions {
|
||||
drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
|
||||
for len(*l.remotes) > 0 {
|
||||
// Discard stale transactions if found during cleanup
|
||||
cheapest := (*l.remotes)[0]
|
||||
if l.all.GetRemote(cheapest.Hash()) == nil { // Removed or migrated
|
||||
heap.Pop(l.remotes)
|
||||
l.stales--
|
||||
continue
|
||||
}
|
||||
// Stop the discards if we've reached the threshold
|
||||
if cheapest.GasPriceIntCmp(threshold) >= 0 {
|
||||
break
|
||||
}
|
||||
heap.Pop(l.remotes)
|
||||
drop = append(drop, cheapest)
|
||||
}
|
||||
return drop
|
||||
}
|
||||
|
||||
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
|
||||
// lowest priced (remote) transaction currently being tracked.
|
||||
func (l *txPricedList) Underpriced(tx *types.Transaction) bool {
|
||||
func (l *pricedList) Underpriced(tx *types.Transaction) bool {
|
||||
// Note: with two queues, being underpriced is defined as being worse than the worst item
|
||||
// in all non-empty queues if there is any. If both queues are empty then nothing is underpriced.
|
||||
return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) &&
|
||||
(l.underpricedFor(&l.floating, tx) || len(l.floating.list) == 0) &&
|
||||
(len(l.urgent.list) != 0 || len(l.floating.list) != 0)
|
||||
}
|
||||
|
||||
// underpricedFor checks whether a transaction is cheaper than (or as cheap as) the
|
||||
// lowest priced (remote) transaction in the given heap.
|
||||
func (l *pricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool {
|
||||
// Discard stale price points if found at the heap start
|
||||
for len(*l.remotes) > 0 {
|
||||
head := []*types.Transaction(*l.remotes)[0]
|
||||
for len(h.list) > 0 {
|
||||
head := h.list[0]
|
||||
if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated
|
||||
atomic.AddInt64(&l.stales, -1)
|
||||
heap.Pop(l.remotes)
|
||||
heap.Pop(h)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
// Check if the transaction is underpriced or not
|
||||
if len(*l.remotes) == 0 {
|
||||
if len(h.list) == 0 {
|
||||
return false // There is no remote transaction at all.
|
||||
}
|
||||
// If the remote transaction is even cheaper than the
|
||||
// cheapest one tracked locally, reject it.
|
||||
cheapest := []*types.Transaction(*l.remotes)[0]
|
||||
return cheapest.GasPriceCmp(tx) >= 0
|
||||
return h.cmp(h.list[0], tx) >= 0
|
||||
}
|
||||
|
||||
// Discard finds a number of most underpriced transactions, removes them from the
|
||||
// priced list and returns them for further removal from the entire pool.
|
||||
//
|
||||
// Note local transaction won't be considered for eviction.
|
||||
func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) {
|
||||
func (l *pricedList) Discard(slots int, force bool) (types.Transactions, bool) {
|
||||
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
|
||||
for len(*l.remotes) > 0 && slots > 0 {
|
||||
// Discard stale transactions if found during cleanup
|
||||
tx := heap.Pop(l.remotes).(*types.Transaction)
|
||||
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
|
||||
atomic.AddInt64(&l.stales, -1)
|
||||
continue
|
||||
for slots > 0 {
|
||||
if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 {
|
||||
// Discard stale transactions if found during cleanup
|
||||
tx := heap.Pop(&l.urgent).(*types.Transaction)
|
||||
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
|
||||
atomic.AddInt64(&l.stales, -1)
|
||||
continue
|
||||
}
|
||||
// Non stale transaction found, move to floating heap
|
||||
heap.Push(&l.floating, tx)
|
||||
} else {
|
||||
if len(l.floating.list) == 0 {
|
||||
// Stop if both heaps are empty
|
||||
break
|
||||
}
|
||||
// Discard stale transactions if found during cleanup
|
||||
tx := heap.Pop(&l.floating).(*types.Transaction)
|
||||
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
|
||||
atomic.AddInt64(&l.stales, -1)
|
||||
continue
|
||||
}
|
||||
// Non stale transaction found, discard it
|
||||
drop = append(drop, tx)
|
||||
slots -= numSlots(tx)
|
||||
}
|
||||
// Non stale transaction found, discard it
|
||||
drop = append(drop, tx)
|
||||
slots -= numSlots(tx)
|
||||
}
|
||||
// If we still can't make enough room for the new transaction
|
||||
if slots > 0 && !force {
|
||||
for _, tx := range drop {
|
||||
heap.Push(l.remotes, tx)
|
||||
heap.Push(&l.urgent, tx)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
|
@ -562,16 +605,35 @@ func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool)
|
|||
}
|
||||
|
||||
// Reheap forcibly rebuilds the heap based on the current remote transaction set.
|
||||
func (l *txPricedList) Reheap() {
|
||||
func (l *pricedList) Reheap() {
|
||||
l.reheapMu.Lock()
|
||||
defer l.reheapMu.Unlock()
|
||||
reheap := make(priceHeap, 0, l.all.RemoteCount())
|
||||
|
||||
start := time.Now()
|
||||
atomic.StoreInt64(&l.stales, 0)
|
||||
l.remotes = &reheap
|
||||
l.urgent.list = make([]*types.Transaction, 0, l.all.RemoteCount())
|
||||
l.all.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool {
|
||||
*l.remotes = append(*l.remotes, tx)
|
||||
l.urgent.list = append(l.urgent.list, tx)
|
||||
return true
|
||||
}, false, true) // Only iterate remotes
|
||||
heap.Init(l.remotes)
|
||||
heap.Init(&l.urgent)
|
||||
|
||||
// balance out the two heaps by moving the worse half of transactions into the
|
||||
// floating heap
|
||||
// Note: Discard would also do this before the first eviction but Reheap can do
|
||||
// is more efficiently. Also, Underpriced would work suboptimally the first time
|
||||
// if the floating queue was empty.
|
||||
floatingCount := len(l.urgent.list) * floatingRatio / (urgentRatio + floatingRatio)
|
||||
l.floating.list = make([]*types.Transaction, floatingCount)
|
||||
for i := 0; i < floatingCount; i++ {
|
||||
l.floating.list[i] = heap.Pop(&l.urgent).(*types.Transaction)
|
||||
}
|
||||
heap.Init(&l.floating)
|
||||
reheapTimer.Update(time.Since(start))
|
||||
}
|
||||
|
||||
// SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not
|
||||
// necessary to call right before SetBaseFee when processing a new block.
|
||||
func (l *pricedList) SetBaseFee(baseFee *big.Int) {
|
||||
l.urgent.baseFee = baseFee
|
||||
l.Reheap()
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
// Tests that transactions can be added to strict lists and list contents and
|
||||
// nonce boundaries are correctly maintained.
|
||||
func TestStrictTxListAdd(t *testing.T) {
|
||||
func TestStrictListAdd(t *testing.T) {
|
||||
// Generate a list of transactions to insert
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
||||
|
|
@ -36,9 +36,9 @@ func TestStrictTxListAdd(t *testing.T) {
|
|||
txs[i] = transaction(uint64(i), 0, key)
|
||||
}
|
||||
// Insert the transactions in a random order
|
||||
list := newTxList(true)
|
||||
list := newList(true)
|
||||
for _, v := range rand.Perm(len(txs)) {
|
||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
||||
list.Add(txs[v], DefaultConfig.PriceBump)
|
||||
}
|
||||
// Verify internal state
|
||||
if len(list.txs.items) != len(txs) {
|
||||
|
|
@ -51,7 +51,7 @@ func TestStrictTxListAdd(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkTxListAdd(t *testing.B) {
|
||||
func BenchmarkListAdd(t *testing.B) {
|
||||
// Generate a list of transactions to insert
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
||||
|
|
@ -60,11 +60,11 @@ func BenchmarkTxListAdd(t *testing.B) {
|
|||
txs[i] = transaction(uint64(i), 0, key)
|
||||
}
|
||||
// Insert the transactions in a random order
|
||||
list := newTxList(true)
|
||||
priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit))
|
||||
list := newList(true)
|
||||
priceLimit := big.NewInt(int64(DefaultConfig.PriceLimit))
|
||||
t.ResetTimer()
|
||||
for _, v := range rand.Perm(len(txs)) {
|
||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
||||
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump, nil, nil)
|
||||
list.Add(txs[v], DefaultConfig.PriceBump)
|
||||
list.Filter(priceLimit, DefaultConfig.PriceBump, nil, nil)
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
|
@ -23,18 +23,18 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
)
|
||||
|
||||
// txNoncer is a tiny virtual state database to manage the executable nonces of
|
||||
// noncer is a tiny virtual state database to manage the executable nonces of
|
||||
// accounts in the pool, falling back to reading from a real state database if
|
||||
// an account is unknown.
|
||||
type txNoncer struct {
|
||||
type noncer struct {
|
||||
fallback *state.StateDB
|
||||
nonces map[common.Address]uint64
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// newTxNoncer creates a new virtual state database to track the pool nonces.
|
||||
func newTxNoncer(statedb *state.StateDB) *txNoncer {
|
||||
return &txNoncer{
|
||||
// newNoncer creates a new virtual state database to track the pool nonces.
|
||||
func newNoncer(statedb *state.StateDB) *noncer {
|
||||
return &noncer{
|
||||
fallback: statedb.Copy(),
|
||||
nonces: make(map[common.Address]uint64),
|
||||
}
|
||||
|
|
@ -42,21 +42,23 @@ func newTxNoncer(statedb *state.StateDB) *txNoncer {
|
|||
|
||||
// get returns the current nonce of an account, falling back to a real state
|
||||
// database if the account is unknown.
|
||||
func (txn *txNoncer) get(addr common.Address) uint64 {
|
||||
func (txn *noncer) get(addr common.Address) uint64 {
|
||||
// We use mutex for get operation is the underlying
|
||||
// state will mutate db even for read access.
|
||||
txn.lock.Lock()
|
||||
defer txn.lock.Unlock()
|
||||
|
||||
if _, ok := txn.nonces[addr]; !ok {
|
||||
txn.nonces[addr] = txn.fallback.GetNonce(addr)
|
||||
if nonce := txn.fallback.GetNonce(addr); nonce != 0 {
|
||||
txn.nonces[addr] = nonce
|
||||
}
|
||||
}
|
||||
return txn.nonces[addr]
|
||||
}
|
||||
|
||||
// set inserts a new virtual nonce into the virtual state database to be returned
|
||||
// whenever the pool requests it instead of reaching into the real state database.
|
||||
func (txn *txNoncer) set(addr common.Address, nonce uint64) {
|
||||
func (txn *noncer) set(addr common.Address, nonce uint64) {
|
||||
txn.lock.Lock()
|
||||
defer txn.lock.Unlock()
|
||||
|
||||
|
|
@ -65,15 +67,25 @@ func (txn *txNoncer) set(addr common.Address, nonce uint64) {
|
|||
|
||||
// setIfLower updates a new virtual nonce into the virtual state database if the
|
||||
// the new one is lower.
|
||||
func (txn *txNoncer) setIfLower(addr common.Address, nonce uint64) {
|
||||
func (txn *noncer) setIfLower(addr common.Address, nonce uint64) {
|
||||
txn.lock.Lock()
|
||||
defer txn.lock.Unlock()
|
||||
|
||||
if _, ok := txn.nonces[addr]; !ok {
|
||||
txn.nonces[addr] = txn.fallback.GetNonce(addr)
|
||||
if nonce := txn.fallback.GetNonce(addr); nonce != 0 {
|
||||
txn.nonces[addr] = nonce
|
||||
}
|
||||
}
|
||||
if txn.nonces[addr] <= nonce {
|
||||
return
|
||||
}
|
||||
txn.nonces[addr] = nonce
|
||||
}
|
||||
|
||||
// setAll sets the nonces for all accounts to the given map.
|
||||
func (txn *noncer) setAll(all map[common.Address]uint64) {
|
||||
txn.lock.Lock()
|
||||
defer txn.lock.Unlock()
|
||||
|
||||
txn.nonces = all
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
|
|
@ -84,7 +85,7 @@ type blockChainXDCx interface {
|
|||
GetBlock(hash common.Hash, number uint64) *types.Block
|
||||
OrderStateAt(block *types.Block) (*tradingstate.TradingStateDB, error)
|
||||
StateAt(root common.Hash) (*state.StateDB, error)
|
||||
SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
Engine() consensus.Engine
|
||||
// GetHeader returns the hash corresponding to their hash.
|
||||
GetHeader(common.Hash, uint64) *types.Header
|
||||
|
|
@ -133,7 +134,7 @@ type OrderPool struct {
|
|||
|
||||
txFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
chainHeadCh chan ChainHeadEvent
|
||||
chainHeadCh chan core.ChainHeadEvent
|
||||
chainHeadSub event.Subscription
|
||||
signer types.OrderSigner
|
||||
mu sync.RWMutex
|
||||
|
|
@ -170,7 +171,7 @@ func NewOrderPool(chainconfig *params.ChainConfig, chain blockChainXDCx) *OrderP
|
|||
queue: make(map[common.Address]*ordertxList),
|
||||
beats: make(map[common.Address]time.Time),
|
||||
all: make(map[common.Hash]*types.OrderTransaction),
|
||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||
}
|
||||
pool.locals = newOrderAccountSet(pool.signer)
|
||||
pool.reset(nil, chain.CurrentBlock())
|
||||
|
|
@ -340,7 +341,7 @@ func (pool *OrderPool) Stop() {
|
|||
|
||||
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
|
||||
// starts sending event to the given channel.
|
||||
func (pool *OrderPool) SubscribeTxPreEvent(ch chan<- OrderTxPreEvent) event.Subscription {
|
||||
func (pool *OrderPool) SubscribeTxPreEvent(ch chan<- core.OrderTxPreEvent) event.Subscription {
|
||||
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
|
|
@ -464,7 +465,7 @@ func (pool *OrderPool) validateOrder(tx *types.OrderTransaction) error {
|
|||
if orderType == OrderTypeLimit {
|
||||
XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS)
|
||||
if !ok {
|
||||
return ErrNotXDPoS
|
||||
return core.ErrNotXDPoS
|
||||
}
|
||||
XDCXServ := XDPoSEngine.GetXDCXService()
|
||||
if XDCXServ == nil {
|
||||
|
|
@ -550,10 +551,10 @@ func (pool *OrderPool) validateTx(tx *types.OrderTransaction, local bool) error
|
|||
}
|
||||
// Ensure the transaction adheres to nonce ordering
|
||||
if pool.currentOrderState.GetNonce(from.Hash()) > tx.Nonce() {
|
||||
return ErrNonceTooLow
|
||||
return core.ErrNonceTooLow
|
||||
}
|
||||
if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
||||
return ErrNonceTooHigh
|
||||
return core.ErrNonceTooHigh
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -603,7 +604,7 @@ func (pool *OrderPool) add(tx *types.OrderTransaction, local bool) (bool, error)
|
|||
pool.journalTx(from, tx)
|
||||
|
||||
log.Debug("Pooled new executable transaction", "hash", hash, "useraddress", tx.UserAddress().Hex(), "nonce", tx.Nonce(), "status", tx.Status(), "orderid", tx.OrderID())
|
||||
go pool.txFeed.Send(OrderTxPreEvent{tx})
|
||||
go pool.txFeed.Send(core.OrderTxPreEvent{Tx: tx})
|
||||
return old != nil, nil
|
||||
|
||||
}
|
||||
|
|
@ -690,7 +691,7 @@ func (pool *OrderPool) promoteTx(addr common.Address, hash common.Hash, tx *type
|
|||
pool.beats[addr] = time.Now()
|
||||
pool.pendingState.SetNonce(addr.Hash(), tx.Nonce()+1)
|
||||
log.Debug("promoteTx txFeed.Send", "addr", tx.UserAddress().Hex(), "nonce", tx.Nonce(), "ohash", tx.OrderHash().Hex(), "status", tx.Status(), "orderid", tx.OrderID())
|
||||
go pool.txFeed.Send(OrderTxPreEvent{tx})
|
||||
go pool.txFeed.Send(core.OrderTxPreEvent{Tx: tx})
|
||||
}
|
||||
|
||||
// AddLocal enqueues a single transaction into the pool if it is valid, marking
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
package txpool
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -29,6 +29,8 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
|
|
@ -62,10 +64,6 @@ var (
|
|||
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
||||
ErrInvalidSender = errors.New("invalid sender")
|
||||
|
||||
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
|
||||
// one present in the local chain.
|
||||
ErrNonceTooLow = errors.New("nonce too low")
|
||||
|
||||
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
|
||||
// configured for the transaction pool.
|
||||
ErrUnderpriced = errors.New("transaction underpriced")
|
||||
|
|
@ -78,14 +76,6 @@ var (
|
|||
// with a different one without the required price bump.
|
||||
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
||||
|
||||
// ErrInsufficientFunds is returned if the total cost of executing a transaction
|
||||
// is higher than the balance of the user's account.
|
||||
ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
|
||||
|
||||
// ErrIntrinsicGas is returned if the transaction is specified to use less gas
|
||||
// than required to start the invocation.
|
||||
ErrIntrinsicGas = errors.New("intrinsic gas too low")
|
||||
|
||||
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
||||
// maximum allowance of the current block.
|
||||
ErrGasLimit = errors.New("exceeds block gas limit")
|
||||
|
|
@ -134,10 +124,21 @@ var (
|
|||
underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil)
|
||||
overflowedTxMeter = metrics.NewRegisteredMeter("txpool/overflowed", nil)
|
||||
|
||||
// throttleTxMeter counts how many transactions are rejected due to too-many-changes between
|
||||
// txpool reorgs.
|
||||
throttleTxMeter = metrics.NewRegisteredMeter("txpool/throttle", nil)
|
||||
// reorgDurationTimer measures how long time a txpool reorg takes.
|
||||
reorgDurationTimer = metrics.NewRegisteredTimer("txpool/reorgtime", nil)
|
||||
// dropBetweenReorgHistogram counts how many drops we experience between two reorg runs. It is expected
|
||||
// that this number is pretty low, since txpool reorgs happen very frequently.
|
||||
dropBetweenReorgHistogram = metrics.NewRegisteredHistogram("txpool/dropbetweenreorg", nil, metrics.NewExpDecaySample(1028, 0.015))
|
||||
|
||||
pendingGauge = metrics.NewRegisteredGauge("txpool/pending", nil)
|
||||
queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil)
|
||||
localGauge = metrics.NewRegisteredGauge("txpool/local", nil)
|
||||
slotsGauge = metrics.NewRegisteredGauge("txpool/slots", nil)
|
||||
|
||||
reheapTimer = metrics.NewRegisteredTimer("txpool/reheap", nil)
|
||||
)
|
||||
|
||||
// TxStatus is the current status of a transaction as seen by the pool.
|
||||
|
|
@ -156,7 +157,7 @@ type blockChain interface {
|
|||
CurrentBlock() *types.Block
|
||||
GetBlock(hash common.Hash, number uint64) *types.Block
|
||||
StateAt(root common.Hash) (*state.StateDB, error)
|
||||
SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
|
||||
// Engine retrieves the chain's consensus engine.
|
||||
Engine() consensus.Engine
|
||||
|
|
@ -171,8 +172,8 @@ type blockChain interface {
|
|||
Config() *params.ChainConfig
|
||||
}
|
||||
|
||||
// TxPoolConfig are the configuration parameters of the transaction pool.
|
||||
type TxPoolConfig struct {
|
||||
// Config are the configuration parameters of the transaction pool.
|
||||
type Config struct {
|
||||
Locals []common.Address // Addresses that should be treated by default as local
|
||||
NoLocals bool // Whether local transaction handling should be disabled
|
||||
Journal string // Journal of local transactions to survive node restarts
|
||||
|
|
@ -189,9 +190,9 @@ type TxPoolConfig struct {
|
|||
Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
|
||||
}
|
||||
|
||||
// DefaultTxPoolConfig contains the default configurations for the transaction
|
||||
// DefaultConfig contains the default configurations for the transaction
|
||||
// pool.
|
||||
var DefaultTxPoolConfig = TxPoolConfig{
|
||||
var DefaultConfig = Config{
|
||||
Journal: "transactions.rlp",
|
||||
Rejournal: time.Hour,
|
||||
|
||||
|
|
@ -199,7 +200,7 @@ var DefaultTxPoolConfig = TxPoolConfig{
|
|||
PriceBump: 10,
|
||||
|
||||
AccountSlots: 16,
|
||||
GlobalSlots: 4096,
|
||||
GlobalSlots: 4096 + 1024, // urgent + floating queue capacity with 4:1 ratio
|
||||
AccountQueue: 64,
|
||||
GlobalQueue: 1024,
|
||||
|
||||
|
|
@ -208,39 +209,39 @@ var DefaultTxPoolConfig = TxPoolConfig{
|
|||
|
||||
// sanitize checks the provided user configurations and changes anything that's
|
||||
// unreasonable or unworkable.
|
||||
func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
||||
func (config *Config) sanitize() Config {
|
||||
conf := *config
|
||||
if conf.Rejournal < time.Second {
|
||||
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
|
||||
conf.Rejournal = time.Second
|
||||
}
|
||||
if conf.PriceLimit < 1 {
|
||||
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
|
||||
conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
|
||||
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
|
||||
conf.PriceLimit = DefaultConfig.PriceLimit
|
||||
}
|
||||
if conf.PriceBump < 1 {
|
||||
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump)
|
||||
conf.PriceBump = DefaultTxPoolConfig.PriceBump
|
||||
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultConfig.PriceBump)
|
||||
conf.PriceBump = DefaultConfig.PriceBump
|
||||
}
|
||||
if conf.AccountSlots < 1 {
|
||||
log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultTxPoolConfig.AccountSlots)
|
||||
conf.AccountSlots = DefaultTxPoolConfig.AccountSlots
|
||||
log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultConfig.AccountSlots)
|
||||
conf.AccountSlots = DefaultConfig.AccountSlots
|
||||
}
|
||||
if conf.GlobalSlots < 1 {
|
||||
log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultTxPoolConfig.GlobalSlots)
|
||||
conf.GlobalSlots = DefaultTxPoolConfig.GlobalSlots
|
||||
log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultConfig.GlobalSlots)
|
||||
conf.GlobalSlots = DefaultConfig.GlobalSlots
|
||||
}
|
||||
if conf.AccountQueue < 1 {
|
||||
log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultTxPoolConfig.AccountQueue)
|
||||
conf.AccountQueue = DefaultTxPoolConfig.AccountQueue
|
||||
log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultConfig.AccountQueue)
|
||||
conf.AccountQueue = DefaultConfig.AccountQueue
|
||||
}
|
||||
if conf.GlobalQueue < 1 {
|
||||
log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue)
|
||||
conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue
|
||||
log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultConfig.GlobalQueue)
|
||||
conf.GlobalQueue = DefaultConfig.GlobalQueue
|
||||
}
|
||||
if conf.Lifetime < 1 {
|
||||
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime)
|
||||
conf.Lifetime = DefaultTxPoolConfig.Lifetime
|
||||
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultConfig.Lifetime)
|
||||
conf.Lifetime = DefaultConfig.Lifetime
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
|
@ -253,7 +254,7 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
|||
// current state) and future transactions. Transactions move between those
|
||||
// two states over time as they are received and processed.
|
||||
type TxPool struct {
|
||||
config TxPoolConfig
|
||||
config Config
|
||||
chainconfig *params.ChainConfig
|
||||
chain blockChain
|
||||
gasPrice *big.Int
|
||||
|
|
@ -262,20 +263,23 @@ type TxPool struct {
|
|||
signer types.Signer
|
||||
mu sync.RWMutex
|
||||
|
||||
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
|
||||
eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions.
|
||||
|
||||
currentState *state.StateDB // Current state in the blockchain head
|
||||
pendingNonces *txNoncer // Pending state tracking virtual nonces
|
||||
pendingNonces *noncer // Pending state tracking virtual nonces
|
||||
currentMaxGas uint64 // Current gas limit for transaction caps
|
||||
|
||||
locals *accountSet // Set of local transaction to exempt from eviction rules
|
||||
journal *txJournal // Journal of local transaction to back up to disk
|
||||
journal *journal // Journal of local transaction to back up to disk
|
||||
|
||||
pending map[common.Address]*txList // All currently processable transactions
|
||||
queue map[common.Address]*txList // Queued but non-processable transactions
|
||||
pending map[common.Address]*list // All currently processable transactions
|
||||
queue map[common.Address]*list // Queued but non-processable transactions
|
||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||
all *txLookup // All transactions to allow lookups
|
||||
priced *txPricedList // All transactions sorted by price
|
||||
all *lookup // All transactions to allow lookups
|
||||
priced *pricedList // All transactions sorted by price
|
||||
|
||||
chainHeadCh chan ChainHeadEvent
|
||||
chainHeadCh chan core.ChainHeadEvent
|
||||
chainHeadSub event.Subscription
|
||||
reqResetCh chan *txpoolResetRequest
|
||||
reqPromoteCh chan *accountSet
|
||||
|
|
@ -285,7 +289,8 @@ type TxPool struct {
|
|||
wg sync.WaitGroup // tracks loop, scheduleReorgLoop
|
||||
initDoneCh chan struct{} // is closed once the pool is initialized (for tests)
|
||||
|
||||
eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions.
|
||||
changesSinceReorg int // A counter for how many drops we've performed in-between reorg.
|
||||
|
||||
IsSigner func(address common.Address) bool
|
||||
trc21FeeCapacity map[common.Address]*big.Int
|
||||
}
|
||||
|
|
@ -296,7 +301,7 @@ type txpoolResetRequest struct {
|
|||
|
||||
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
|
||||
// transactions from the network.
|
||||
func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
|
||||
func NewTxPool(config Config, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
|
||||
// Sanitize the input to ensure no vulnerable gas prices are set
|
||||
config = (&config).sanitize()
|
||||
|
||||
|
|
@ -306,11 +311,11 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
|||
chainconfig: chainconfig,
|
||||
chain: chain,
|
||||
signer: types.LatestSigner(chainconfig),
|
||||
pending: make(map[common.Address]*txList),
|
||||
queue: make(map[common.Address]*txList),
|
||||
pending: make(map[common.Address]*list),
|
||||
queue: make(map[common.Address]*list),
|
||||
beats: make(map[common.Address]time.Time),
|
||||
all: newTxLookup(),
|
||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
||||
all: newLookup(),
|
||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||
reqResetCh: make(chan *txpoolResetRequest),
|
||||
reqPromoteCh: make(chan *accountSet),
|
||||
queueTxEventCh: make(chan *types.Transaction),
|
||||
|
|
@ -325,7 +330,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
|||
log.Info("Setting new local account", "address", addr)
|
||||
pool.locals.add(addr)
|
||||
}
|
||||
pool.priced = newTxPricedList(pool.all)
|
||||
pool.priced = newPricedList(pool.all)
|
||||
pool.reset(nil, chain.CurrentBlock().Header())
|
||||
|
||||
// Start the reorg loop early so it can handle requests generated during journal loading.
|
||||
|
|
@ -448,7 +453,7 @@ func (pool *TxPool) Stop() {
|
|||
|
||||
// SubscribeNewTxsEvent registers a subscription of NewTxsEvent and
|
||||
// starts sending event to the given channel.
|
||||
func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- NewTxsEvent) event.Subscription {
|
||||
func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
|
|
@ -466,10 +471,18 @@ func (pool *TxPool) SetGasPrice(price *big.Int) {
|
|||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
old := pool.gasPrice
|
||||
pool.gasPrice = price
|
||||
for _, tx := range pool.priced.Cap(price) {
|
||||
pool.removeTx(tx.Hash(), false)
|
||||
// if the min miner fee increased, remove transactions below the new threshold
|
||||
if price.Cmp(old) > 0 {
|
||||
// pool.priced is sorted by GasFeeCap, so we have to iterate through pool.all instead
|
||||
drop := pool.all.RemotesBelowTip(price)
|
||||
for _, tx := range drop {
|
||||
pool.removeTx(tx.Hash(), false)
|
||||
}
|
||||
pool.priced.Removed(len(drop))
|
||||
}
|
||||
|
||||
log.Info("Transaction pool price threshold updated", "price", price)
|
||||
}
|
||||
|
||||
|
|
@ -511,29 +524,63 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
|
|||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
pending := make(map[common.Address]types.Transactions)
|
||||
pending := make(map[common.Address]types.Transactions, len(pool.pending))
|
||||
for addr, list := range pool.pending {
|
||||
pending[addr] = list.Flatten()
|
||||
}
|
||||
queued := make(map[common.Address]types.Transactions)
|
||||
queued := make(map[common.Address]types.Transactions, len(pool.queue))
|
||||
for addr, list := range pool.queue {
|
||||
queued[addr] = list.Flatten()
|
||||
}
|
||||
return pending, queued
|
||||
}
|
||||
|
||||
// ContentFrom retrieves the data content of the transaction pool, returning the
|
||||
// pending as well as queued transactions of this address, grouped by nonce.
|
||||
func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
|
||||
pool.mu.RLock()
|
||||
defer pool.mu.RUnlock()
|
||||
|
||||
var pending types.Transactions
|
||||
if list, ok := pool.pending[addr]; ok {
|
||||
pending = list.Flatten()
|
||||
}
|
||||
var queued types.Transactions
|
||||
if list, ok := pool.queue[addr]; ok {
|
||||
queued = list.Flatten()
|
||||
}
|
||||
return pending, queued
|
||||
}
|
||||
|
||||
// Pending retrieves all currently processable transactions, grouped by origin
|
||||
// account and sorted by nonce. The returned transaction set is a copy and can be
|
||||
// freely modified by calling code.
|
||||
func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
|
||||
//
|
||||
// The enforceTips parameter can be used to do an extra filtering on the pending
|
||||
// transactions and only return those whose **effective** tip is large enough in
|
||||
// the next pending execution environment.
|
||||
func (pool *TxPool) Pending(enforceTips bool) map[common.Address]types.Transactions {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
pending := make(map[common.Address]types.Transactions)
|
||||
for addr, list := range pool.pending {
|
||||
pending[addr] = list.Flatten()
|
||||
txs := list.Flatten()
|
||||
|
||||
// If the miner requests tip enforcement, cap the lists now
|
||||
if enforceTips && !pool.locals.contains(addr) {
|
||||
for i, tx := range txs {
|
||||
if tx.EffectiveGasTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 {
|
||||
txs = txs[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(txs) > 0 {
|
||||
pending[addr] = txs
|
||||
}
|
||||
}
|
||||
return pending, nil
|
||||
return pending
|
||||
}
|
||||
|
||||
// Locals retrieves the accounts currently considered local by the pool.
|
||||
|
|
@ -573,7 +620,11 @@ func (pool *TxPool) GetSender(tx *types.Transaction) (common.Address, error) {
|
|||
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
// Accept only legacy transactions until EIP-2718/2930 activates.
|
||||
if !pool.eip2718 && tx.Type() != types.LegacyTxType {
|
||||
return ErrTxTypeNotSupported
|
||||
return core.ErrTxTypeNotSupported
|
||||
}
|
||||
// Reject dynamic fee transactions until EIP-1559 activates.
|
||||
if !pool.eip1559 && tx.Type() == types.DynamicFeeTxType {
|
||||
return core.ErrTxTypeNotSupported
|
||||
}
|
||||
// Reject transactions over defined size to prevent DOS attacks
|
||||
if uint64(tx.Size()) > txMaxSize {
|
||||
|
|
@ -596,23 +647,34 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
if pool.currentMaxGas < tx.Gas() {
|
||||
return ErrGasLimit
|
||||
}
|
||||
// Sanity check for extremely large numbers
|
||||
if tx.GasFeeCap().BitLen() > 256 {
|
||||
return core.ErrFeeCapVeryHigh
|
||||
}
|
||||
if tx.GasTipCap().BitLen() > 256 {
|
||||
return core.ErrTipVeryHigh
|
||||
}
|
||||
// Ensure gasFeeCap is greater than or equal to gasTipCap.
|
||||
if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 {
|
||||
return core.ErrTipAboveFeeCap
|
||||
}
|
||||
// Make sure the transaction is signed properly.
|
||||
from, err := types.Sender(pool.signer, tx)
|
||||
if err != nil {
|
||||
return ErrInvalidSender
|
||||
}
|
||||
// Drop non-local transactions under our own minimal accepted gas price
|
||||
if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 {
|
||||
// Drop non-local transactions under our own minimal accepted gas price or tip
|
||||
if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 {
|
||||
if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) {
|
||||
return ErrUnderpriced
|
||||
}
|
||||
}
|
||||
// Ensure the transaction adheres to nonce ordering
|
||||
if pool.currentState.GetNonce(from) > tx.Nonce() {
|
||||
return ErrNonceTooLow
|
||||
return core.ErrNonceTooLow
|
||||
}
|
||||
if pool.pendingNonces.get(from)+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
||||
return ErrNonceTooHigh
|
||||
return core.ErrNonceTooHigh
|
||||
}
|
||||
// Transactor should have enough funds to cover the costs
|
||||
// cost == V + GP * GL
|
||||
|
|
@ -629,24 +691,24 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
if value, ok := pool.trc21FeeCapacity[*tx.To()]; ok {
|
||||
feeCapacity = value
|
||||
if !state.ValidateTRC21Tx(pool.currentState, from, *tx.To(), tx.Data()) {
|
||||
return ErrInsufficientFunds
|
||||
return core.ErrInsufficientFunds
|
||||
}
|
||||
cost = tx.TxCost(number)
|
||||
}
|
||||
}
|
||||
if new(big.Int).Add(balance, feeCapacity).Cmp(cost) < 0 {
|
||||
return ErrInsufficientFunds
|
||||
return core.ErrInsufficientFunds
|
||||
}
|
||||
|
||||
if tx.To() == nil || (tx.To() != nil && !tx.IsSpecialTransaction()) {
|
||||
// Ensure the transaction has more gas than the basic tx fee.
|
||||
intrGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true)
|
||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.eip1559)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Exclude check smart contract sign address.
|
||||
if tx.Gas() < intrGas {
|
||||
return ErrIntrinsicGas
|
||||
return core.ErrIntrinsicGas
|
||||
}
|
||||
|
||||
// Check zero gas price.
|
||||
|
|
@ -670,13 +732,13 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
// validate minFee slot for XDCZ
|
||||
if tx.IsXDCZApplyTransaction() {
|
||||
copyState := pool.currentState.Copy()
|
||||
return ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
|
||||
return core.ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
|
||||
}
|
||||
|
||||
// validate balance slot, token decimal for XDCX
|
||||
if tx.IsXDCXApplyTransaction() {
|
||||
copyState := pool.currentState.Copy()
|
||||
return ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
|
||||
return core.ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -686,8 +748,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
// pending or queued one, it overwrites the previous transaction if its price is higher.
|
||||
//
|
||||
// If a newly added transaction is marked as local, its sending account will be
|
||||
// whitelisted, preventing any associated transaction from being dropped out of the pool
|
||||
// due to pricing constraints.
|
||||
// be added to the allowlist, preventing any associated transaction from being dropped
|
||||
// out of the pool due to pricing constraints.
|
||||
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
|
||||
// If the transaction is already known, discard it
|
||||
hash := tx.Hash()
|
||||
|
|
@ -711,14 +773,22 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
|
|||
return pool.promoteSpecialTx(from, tx, isLocal)
|
||||
}
|
||||
// If the transaction pool is full, discard underpriced transactions
|
||||
if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
|
||||
log.Debug("Add transaction to pool full", "hash", hash, "nonce", tx.Nonce())
|
||||
if uint64(pool.all.Slots()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue {
|
||||
// If the new transaction is underpriced, don't accept it
|
||||
if !isLocal && pool.priced.Underpriced(tx) {
|
||||
log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
|
||||
log.Trace("Discarding underpriced transaction", "hash", hash, "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap())
|
||||
underpricedTxMeter.Mark(1)
|
||||
return false, ErrUnderpriced
|
||||
}
|
||||
// We're about to replace a transaction. The reorg does a more thorough
|
||||
// analysis of what to remove and how, but it runs async. We don't want to
|
||||
// do too many replacements between reorg-runs, so we cap the number of
|
||||
// replacements to 25% of the slots
|
||||
if pool.changesSinceReorg > int(pool.config.GlobalSlots/4) {
|
||||
throttleTxMeter.Mark(1)
|
||||
return false, ErrTxPoolOverflow
|
||||
}
|
||||
|
||||
// New transaction is better than our worse ones, make room for it.
|
||||
// If it's a local transaction, forcibly discard all available transactions.
|
||||
// Otherwise if we can't make enough room for new one, abort the operation.
|
||||
|
|
@ -730,9 +800,11 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
|
|||
overflowedTxMeter.Mark(1)
|
||||
return false, ErrTxPoolOverflow
|
||||
}
|
||||
// Bump the counter of rejections-since-reorg
|
||||
pool.changesSinceReorg += len(drop)
|
||||
// Kick out the underpriced remote transactions.
|
||||
for _, tx := range drop {
|
||||
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
|
||||
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap())
|
||||
underpricedTxMeter.Mark(1)
|
||||
pool.removeTx(tx.Hash(), false)
|
||||
}
|
||||
|
|
@ -788,7 +860,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction, local boo
|
|||
// Try to insert the transaction into the future queue
|
||||
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||
if pool.queue[from] == nil {
|
||||
pool.queue[from] = newTxList(false)
|
||||
pool.queue[from] = newList(false)
|
||||
}
|
||||
inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
|
||||
if !inserted {
|
||||
|
|
@ -840,7 +912,7 @@ func (pool *TxPool) journalTx(from common.Address, tx *types.Transaction) {
|
|||
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
|
||||
// Try to insert the transaction into the pending queue
|
||||
if pool.pending[addr] == nil {
|
||||
pool.pending[addr] = newTxList(true)
|
||||
pool.pending[addr] = newList(true)
|
||||
}
|
||||
list := pool.pending[addr]
|
||||
|
||||
|
|
@ -873,7 +945,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
|
|||
func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction, isLocal bool) (bool, error) {
|
||||
// Try to insert the transaction into the pending queue
|
||||
if pool.pending[addr] == nil {
|
||||
pool.pending[addr] = newTxList(true)
|
||||
pool.pending[addr] = newList(true)
|
||||
}
|
||||
list := pool.pending[addr]
|
||||
|
||||
|
|
@ -904,7 +976,7 @@ func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction,
|
|||
// Set the potentially new pending nonce and notify any subsystems of the new tx
|
||||
pool.beats[addr] = time.Now()
|
||||
pool.pendingNonces.set(addr, tx.Nonce()+1)
|
||||
go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}})
|
||||
go pool.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
|
@ -1146,7 +1218,7 @@ func (pool *TxPool) scheduleReorgLoop() {
|
|||
launchNextRun bool
|
||||
reset *txpoolResetRequest
|
||||
dirtyAccounts *accountSet
|
||||
queuedEvents = make(map[common.Address]*txSortedMap)
|
||||
queuedEvents = make(map[common.Address]*sortedMap)
|
||||
)
|
||||
for {
|
||||
// Launch next background reorg if needed
|
||||
|
|
@ -1159,7 +1231,7 @@ func (pool *TxPool) scheduleReorgLoop() {
|
|||
launchNextRun = false
|
||||
|
||||
reset, dirtyAccounts = nil, nil
|
||||
queuedEvents = make(map[common.Address]*txSortedMap)
|
||||
queuedEvents = make(map[common.Address]*sortedMap)
|
||||
}
|
||||
|
||||
select {
|
||||
|
|
@ -1188,7 +1260,7 @@ func (pool *TxPool) scheduleReorgLoop() {
|
|||
// request one later if they want the events sent.
|
||||
addr, _ := types.Sender(pool.signer, tx)
|
||||
if _, ok := queuedEvents[addr]; !ok {
|
||||
queuedEvents[addr] = newTxSortedMap()
|
||||
queuedEvents[addr] = newSortedMap()
|
||||
}
|
||||
queuedEvents[addr].Put(tx)
|
||||
|
||||
|
|
@ -1207,7 +1279,10 @@ func (pool *TxPool) scheduleReorgLoop() {
|
|||
}
|
||||
|
||||
// runReorg runs reset and promoteExecutables on behalf of scheduleReorgLoop.
|
||||
func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*txSortedMap) {
|
||||
func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events map[common.Address]*sortedMap) {
|
||||
defer func(t0 time.Time) {
|
||||
reorgDurationTimer.Update(time.Since(t0))
|
||||
}(time.Now())
|
||||
defer close(done)
|
||||
|
||||
var promoteAddrs []common.Address
|
||||
|
|
@ -1243,23 +1318,31 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
|
|||
// because of another transaction (e.g. higher gas price).
|
||||
if reset != nil {
|
||||
pool.demoteUnexecutables()
|
||||
if reset.newHead != nil && pool.chainconfig.IsEIP1559(new(big.Int).Add(reset.newHead.Number, big.NewInt(1))) {
|
||||
pendingBaseFee := eip1559.CalcBaseFee(pool.chainconfig, reset.newHead)
|
||||
pool.priced.SetBaseFee(pendingBaseFee)
|
||||
}
|
||||
// Update all accounts to the latest known pending nonce
|
||||
nonces := make(map[common.Address]uint64, len(pool.pending))
|
||||
for addr, list := range pool.pending {
|
||||
highestPending := list.LastElement()
|
||||
nonces[addr] = highestPending.Nonce() + 1
|
||||
}
|
||||
pool.pendingNonces.setAll(nonces)
|
||||
}
|
||||
// Ensure pool.queue and pool.pending sizes stay within the configured limits.
|
||||
pool.truncatePending()
|
||||
pool.truncateQueue()
|
||||
|
||||
// Update all accounts to the latest known pending nonce
|
||||
for addr, list := range pool.pending {
|
||||
highestPending := list.LastElement()
|
||||
pool.pendingNonces.set(addr, highestPending.Nonce()+1)
|
||||
}
|
||||
dropBetweenReorgHistogram.Update(int64(pool.changesSinceReorg))
|
||||
pool.changesSinceReorg = 0 // Reset change counter
|
||||
pool.mu.Unlock()
|
||||
|
||||
// Notify subsystems for newly added transactions
|
||||
for _, tx := range promoted {
|
||||
addr, _ := types.Sender(pool.signer, tx)
|
||||
if _, ok := events[addr]; !ok {
|
||||
events[addr] = newTxSortedMap()
|
||||
events[addr] = newSortedMap()
|
||||
}
|
||||
events[addr].Put(tx)
|
||||
}
|
||||
|
|
@ -1268,7 +1351,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
|
|||
for _, set := range events {
|
||||
txs = append(txs, set.Flatten()...)
|
||||
}
|
||||
pool.txFeed.Send(NewTxsEvent{txs})
|
||||
pool.txFeed.Send(core.NewTxsEvent{Txs: txs})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1295,7 +1378,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
|||
if rem == nil {
|
||||
// This can happen if a setHead is performed, where we simply discard the old
|
||||
// head from the chain.
|
||||
// If that is the case, we don't have the lost transactions any more, and
|
||||
// If that is the case, we don't have the lost transactions anymore, and
|
||||
// there's nothing to add
|
||||
if newNum >= oldNum {
|
||||
// If we reorged to a same or higher number, then it's not a case of setHead
|
||||
|
|
@ -1349,17 +1432,18 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
|||
}
|
||||
pool.currentState = statedb
|
||||
pool.trc21FeeCapacity = state.GetTRC21FeeCapacityFromStateWithCache(newHead.Root, statedb)
|
||||
pool.pendingNonces = newTxNoncer(statedb)
|
||||
pool.pendingNonces = newNoncer(statedb)
|
||||
pool.currentMaxGas = newHead.GasLimit
|
||||
|
||||
// Inject any transactions discarded due to reorgs
|
||||
log.Debug("Reinjecting stale transactions", "count", len(reinject))
|
||||
senderCacher.recover(pool.signer, reinject)
|
||||
core.SenderCacher.Recover(pool.signer, reinject)
|
||||
pool.addTxsLocked(reinject, false)
|
||||
|
||||
// Update all fork indicator by next pending block number.
|
||||
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
|
||||
pool.eip2718 = pool.chainconfig.IsEIP1559(next)
|
||||
pool.eip1559 = pool.chainconfig.IsEIP1559(next)
|
||||
}
|
||||
|
||||
// promoteExecutables moves transactions that have become processable from the
|
||||
|
|
@ -1524,7 +1608,7 @@ func (pool *TxPool) truncatePending() {
|
|||
pendingRateLimitMeter.Mark(int64(pendingBeforeCap - pending))
|
||||
}
|
||||
|
||||
// truncateQueue drops the oldes transactions in the queue if the pool is above the global queue limit.
|
||||
// truncateQueue drops the oldest transactions in the queue if the pool is above the global queue limit.
|
||||
func (pool *TxPool) truncateQueue() {
|
||||
queued := uint64(0)
|
||||
for _, list := range pool.queue {
|
||||
|
|
@ -1541,7 +1625,7 @@ func (pool *TxPool) truncateQueue() {
|
|||
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
|
||||
}
|
||||
}
|
||||
sort.Sort(addresses)
|
||||
sort.Sort(sort.Reverse(addresses))
|
||||
|
||||
// Drop transactions until the total is below the limit or only locals remain
|
||||
for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; {
|
||||
|
|
@ -1572,6 +1656,10 @@ func (pool *TxPool) truncateQueue() {
|
|||
// demoteUnexecutables removes invalid and processed transactions from the pools
|
||||
// executable/pending queue and any subsequent transactions that become unexecutable
|
||||
// are moved back into the future queue.
|
||||
//
|
||||
// Note: transactions are not marked as removed in the priced list because re-heaping
|
||||
// is always explicitly triggered by SetBaseFee and it would be unnecessary and wasteful
|
||||
// to trigger a re-heap is this function
|
||||
func (pool *TxPool) demoteUnexecutables() {
|
||||
// Iterate over all accounts and demote any non-executable transactions
|
||||
for addr, list := range pool.pending {
|
||||
|
|
@ -1620,8 +1708,6 @@ func (pool *TxPool) demoteUnexecutables() {
|
|||
pool.enqueueTx(hash, tx, false, false)
|
||||
}
|
||||
pendingGauge.Dec(int64(len(gapped)))
|
||||
// This might happen in a reorg, so log it to the metering
|
||||
blockReorgInvalidatedTx.Mark(int64(len(gapped)))
|
||||
}
|
||||
// Delete the entire pending entry if it became empty.
|
||||
if list.Empty() {
|
||||
|
|
@ -1654,7 +1740,7 @@ type accountSet struct {
|
|||
// derivations.
|
||||
func newAccountSet(signer types.Signer, addrs ...common.Address) *accountSet {
|
||||
as := &accountSet{
|
||||
accounts: make(map[common.Address]struct{}),
|
||||
accounts: make(map[common.Address]struct{}, len(addrs)),
|
||||
signer: signer,
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
|
|
@ -1716,7 +1802,7 @@ func (as *accountSet) merge(other *accountSet) {
|
|||
as.cache = nil
|
||||
}
|
||||
|
||||
// txLookup is used internally by TxPool to track transactions while allowing
|
||||
// lookup is used internally by TxPool to track transactions while allowing
|
||||
// lookup without mutex contention.
|
||||
//
|
||||
// Note, although this type is properly protected against concurrent access, it
|
||||
|
|
@ -1728,16 +1814,16 @@ func (as *accountSet) merge(other *accountSet) {
|
|||
//
|
||||
// This lookup set combines the notion of "local transactions", which is useful
|
||||
// to build upper-level structure.
|
||||
type txLookup struct {
|
||||
type lookup struct {
|
||||
slots int
|
||||
lock sync.RWMutex
|
||||
locals map[common.Hash]*types.Transaction
|
||||
remotes map[common.Hash]*types.Transaction
|
||||
}
|
||||
|
||||
// newTxLookup returns a new txLookup structure.
|
||||
func newTxLookup() *txLookup {
|
||||
return &txLookup{
|
||||
// newLookup returns a new lookup structure.
|
||||
func newLookup() *lookup {
|
||||
return &lookup{
|
||||
locals: make(map[common.Hash]*types.Transaction),
|
||||
remotes: make(map[common.Hash]*types.Transaction),
|
||||
}
|
||||
|
|
@ -1746,7 +1832,7 @@ func newTxLookup() *txLookup {
|
|||
// Range calls f on each key and value present in the map. The callback passed
|
||||
// should return the indicator whether the iteration needs to be continued.
|
||||
// Callers need to specify which set (or both) to be iterated.
|
||||
func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) {
|
||||
func (t *lookup) Range(f func(hash common.Hash, tx *types.Transaction, local bool) bool, local bool, remote bool) {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
|
|
@ -1767,7 +1853,7 @@ func (t *txLookup) Range(f func(hash common.Hash, tx *types.Transaction, local b
|
|||
}
|
||||
|
||||
// Get returns a transaction if it exists in the lookup, or nil if not found.
|
||||
func (t *txLookup) Get(hash common.Hash) *types.Transaction {
|
||||
func (t *lookup) Get(hash common.Hash) *types.Transaction {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
|
|
@ -1778,7 +1864,7 @@ func (t *txLookup) Get(hash common.Hash) *types.Transaction {
|
|||
}
|
||||
|
||||
// GetLocal returns a transaction if it exists in the lookup, or nil if not found.
|
||||
func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction {
|
||||
func (t *lookup) GetLocal(hash common.Hash) *types.Transaction {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
|
|
@ -1786,7 +1872,7 @@ func (t *txLookup) GetLocal(hash common.Hash) *types.Transaction {
|
|||
}
|
||||
|
||||
// GetRemote returns a transaction if it exists in the lookup, or nil if not found.
|
||||
func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction {
|
||||
func (t *lookup) GetRemote(hash common.Hash) *types.Transaction {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
|
|
@ -1794,7 +1880,7 @@ func (t *txLookup) GetRemote(hash common.Hash) *types.Transaction {
|
|||
}
|
||||
|
||||
// Count returns the current number of transactions in the lookup.
|
||||
func (t *txLookup) Count() int {
|
||||
func (t *lookup) Count() int {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
|
|
@ -1802,7 +1888,7 @@ func (t *txLookup) Count() int {
|
|||
}
|
||||
|
||||
// LocalCount returns the current number of local transactions in the lookup.
|
||||
func (t *txLookup) LocalCount() int {
|
||||
func (t *lookup) LocalCount() int {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
|
|
@ -1810,7 +1896,7 @@ func (t *txLookup) LocalCount() int {
|
|||
}
|
||||
|
||||
// RemoteCount returns the current number of remote transactions in the lookup.
|
||||
func (t *txLookup) RemoteCount() int {
|
||||
func (t *lookup) RemoteCount() int {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
|
|
@ -1818,7 +1904,7 @@ func (t *txLookup) RemoteCount() int {
|
|||
}
|
||||
|
||||
// Slots returns the current number of slots used in the lookup.
|
||||
func (t *txLookup) Slots() int {
|
||||
func (t *lookup) Slots() int {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
|
|
@ -1826,7 +1912,7 @@ func (t *txLookup) Slots() int {
|
|||
}
|
||||
|
||||
// Add adds a transaction to the lookup.
|
||||
func (t *txLookup) Add(tx *types.Transaction, local bool) {
|
||||
func (t *lookup) Add(tx *types.Transaction, local bool) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
|
|
@ -1841,7 +1927,7 @@ func (t *txLookup) Add(tx *types.Transaction, local bool) {
|
|||
}
|
||||
|
||||
// Remove removes a transaction from the lookup.
|
||||
func (t *txLookup) Remove(hash common.Hash) {
|
||||
func (t *lookup) Remove(hash common.Hash) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
|
|
@ -1862,7 +1948,7 @@ func (t *txLookup) Remove(hash common.Hash) {
|
|||
|
||||
// RemoteToLocals migrates the transactions belongs to the given locals to locals
|
||||
// set. The assumption is held the locals set is thread-safe to be used.
|
||||
func (t *txLookup) RemoteToLocals(locals *accountSet) int {
|
||||
func (t *lookup) RemoteToLocals(locals *accountSet) int {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
|
|
@ -1877,6 +1963,18 @@ func (t *txLookup) RemoteToLocals(locals *accountSet) int {
|
|||
return migrated
|
||||
}
|
||||
|
||||
// RemotesBelowTip finds all remote transactions below the given tip threshold.
|
||||
func (t *lookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
|
||||
found := make(types.Transactions, 0, 128)
|
||||
t.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool {
|
||||
if tx.GasTipCapIntCmp(threshold) < 0 {
|
||||
found = append(found, tx)
|
||||
}
|
||||
return true
|
||||
}, false, true) // Only iterate remotes
|
||||
return found
|
||||
}
|
||||
|
||||
// numSlots calculates the number of slots needed for a single transaction.
|
||||
func numSlots(tx *types.Transaction) int {
|
||||
return int((tx.Size() + txSlotSize - 1) / txSlotSize)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -81,6 +81,9 @@ type Header struct {
|
|||
Validators []byte `json:"validators" gencodec:"required"`
|
||||
Validator []byte `json:"validator" gencodec:"required"`
|
||||
Penalties []byte `json:"penalties" gencodec:"required"`
|
||||
|
||||
// BaseFee was added by EIP-1559 and is ignored in legacy headers.
|
||||
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
|
||||
}
|
||||
|
||||
// field type overrides for gencodec
|
||||
|
|
@ -91,6 +94,7 @@ type headerMarshaling struct {
|
|||
GasUsed hexutil.Uint64
|
||||
Time *hexutil.Big
|
||||
Extra hexutil.Bytes
|
||||
BaseFee *hexutil.Big
|
||||
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
|
||||
}
|
||||
|
||||
|
|
@ -264,6 +268,9 @@ func CopyHeader(h *Header) *Header {
|
|||
if cpy.Number = new(big.Int); h.Number != nil {
|
||||
cpy.Number.Set(h.Number)
|
||||
}
|
||||
if h.BaseFee != nil {
|
||||
cpy.BaseFee = new(big.Int).Set(h.BaseFee)
|
||||
}
|
||||
if len(h.Extra) > 0 {
|
||||
cpy.Extra = make([]byte, len(h.Extra))
|
||||
copy(cpy.Extra, h.Extra)
|
||||
|
|
@ -340,6 +347,13 @@ func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Ext
|
|||
func (b *Block) Penalties() []byte { return common.CopyBytes(b.header.Penalties) }
|
||||
func (b *Block) Validator() []byte { return common.CopyBytes(b.header.Validator) }
|
||||
|
||||
func (b *Block) BaseFee() *big.Int {
|
||||
if b.header.BaseFee == nil {
|
||||
return nil
|
||||
}
|
||||
return new(big.Int).Set(b.header.BaseFee)
|
||||
}
|
||||
|
||||
func (b *Block) Header() *Header { return CopyHeader(b.header) }
|
||||
|
||||
// Body returns the non-header content of the block.
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
var _ = (*headerMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (h Header) MarshalJSON() ([]byte, error) {
|
||||
type Header struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
|
|
@ -30,6 +31,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
|
||||
Nonce BlockNonce `json:"nonce" gencodec:"required"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
var enc Header
|
||||
|
|
@ -48,10 +50,12 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||
enc.Extra = h.Extra
|
||||
enc.MixDigest = h.MixDigest
|
||||
enc.Nonce = h.Nonce
|
||||
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
|
||||
enc.Hash = h.Hash()
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
type Header struct {
|
||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||
|
|
@ -69,6 +73,7 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
|||
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest *common.Hash `json:"mixHash" gencodec:"required"`
|
||||
Nonce *BlockNonce `json:"nonce" gencodec:"required"`
|
||||
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||
}
|
||||
var dec Header
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -134,5 +139,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
|||
return errors.New("missing required field 'nonce' for Header")
|
||||
}
|
||||
h.Nonce = *dec.Nonce
|
||||
if dec.BaseFee != nil {
|
||||
h.BaseFee = (*big.Int)(dec.BaseFee)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
|||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress common.Address `json:"contractAddress"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
|
||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
||||
|
|
@ -39,6 +40,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
|||
enc.TxHash = r.TxHash
|
||||
enc.ContractAddress = r.ContractAddress
|
||||
enc.GasUsed = hexutil.Uint64(r.GasUsed)
|
||||
enc.EffectiveGasPrice = (*hexutil.Big)(r.EffectiveGasPrice)
|
||||
enc.BlockHash = r.BlockHash
|
||||
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
||||
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
||||
|
|
@ -57,6 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
|||
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress *common.Address `json:"contractAddress"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
|
||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
|
||||
|
|
@ -97,6 +100,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
|||
return errors.New("missing required field 'gasUsed' for Receipt")
|
||||
}
|
||||
r.GasUsed = uint64(*dec.GasUsed)
|
||||
if dec.EffectiveGasPrice != nil {
|
||||
r.EffectiveGasPrice = (*big.Int)(dec.EffectiveGasPrice)
|
||||
}
|
||||
if dec.BlockHash != nil {
|
||||
r.BlockHash = *dec.BlockHash
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ var (
|
|||
receiptStatusSuccessfulRLP = []byte{0x01}
|
||||
)
|
||||
|
||||
// This error is returned when a typed receipt is decoded, but the string is empty.
|
||||
var errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
|
||||
var errShortTypedReceipt = errors.New("typed receipt too short")
|
||||
|
||||
const (
|
||||
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
||||
|
|
@ -60,10 +59,10 @@ type Receipt struct {
|
|||
Logs []*Log `json:"logs" gencodec:"required"`
|
||||
|
||||
// Implementation fields: These fields are added by geth when processing a transaction.
|
||||
// They are stored in the chain database.
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress common.Address `json:"contractAddress"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress common.Address `json:"contractAddress"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
EffectiveGasPrice *big.Int `json:"effectiveGasPrice"` // required, but tag omitted for backwards compatibility
|
||||
|
||||
// Inclusion information: These fields provide information about the inclusion of the
|
||||
// transaction corresponding to this receipt.
|
||||
|
|
@ -78,6 +77,7 @@ type receiptMarshaling struct {
|
|||
Status hexutil.Uint64
|
||||
CumulativeGasUsed hexutil.Uint64
|
||||
GasUsed hexutil.Uint64
|
||||
EffectiveGasPrice *hexutil.Big
|
||||
BlockNumber *hexutil.Big
|
||||
TransactionIndex hexutil.Uint
|
||||
}
|
||||
|
|
@ -90,18 +90,8 @@ type receiptRLP struct {
|
|||
Logs []*Log
|
||||
}
|
||||
|
||||
// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
|
||||
type v4StoredReceiptRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
TxHash common.Hash
|
||||
ContractAddress common.Address
|
||||
Logs []*LogForStorage
|
||||
GasUsed uint64
|
||||
}
|
||||
|
||||
// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields.
|
||||
type v3StoredReceiptRLP struct {
|
||||
// receiptStorageRLP is the original storage encoding of a receipt including some unnecessary fields.
|
||||
type receiptStorageRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
Bloom Bloom
|
||||
|
|
@ -134,20 +124,33 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
|
|||
if r.Type == LegacyTxType {
|
||||
return rlp.Encode(w, data)
|
||||
}
|
||||
// It's an EIP-2718 typed TX receipt.
|
||||
if r.Type != AccessListTxType {
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||
defer encodeBufferPool.Put(buf)
|
||||
buf.Reset()
|
||||
buf.WriteByte(r.Type)
|
||||
if err := rlp.Encode(buf, data); err != nil {
|
||||
if err := r.encodeTyped(data, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return rlp.Encode(w, buf.Bytes())
|
||||
}
|
||||
|
||||
// encodeTyped writes the canonical encoding of a typed receipt to w.
|
||||
func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
|
||||
w.WriteByte(r.Type)
|
||||
return rlp.Encode(w, data)
|
||||
}
|
||||
|
||||
// MarshalBinary returns the consensus encoding of the receipt.
|
||||
func (r *Receipt) MarshalBinary() ([]byte, error) {
|
||||
if r.Type == LegacyTxType {
|
||||
return rlp.EncodeToBytes(r)
|
||||
}
|
||||
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
|
||||
var buf bytes.Buffer
|
||||
err := r.encodeTyped(data, &buf)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
||||
// from an RLP stream.
|
||||
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||
|
|
@ -163,26 +166,49 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
|||
}
|
||||
r.Type = LegacyTxType
|
||||
return r.setFromRLP(dec)
|
||||
case kind == rlp.String:
|
||||
default:
|
||||
// It's an EIP-2718 typed tx receipt.
|
||||
b, err := s.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(b) == 0 {
|
||||
return errEmptyTypedReceipt
|
||||
return r.decodeTyped(b)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the consensus encoding of receipts.
|
||||
// It supports legacy RLP receipts and EIP-2718 typed receipts.
|
||||
func (r *Receipt) UnmarshalBinary(b []byte) error {
|
||||
if len(b) > 0 && b[0] > 0x7f {
|
||||
// It's a legacy receipt decode the RLP
|
||||
var data receiptRLP
|
||||
err := rlp.DecodeBytes(b, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Type = LegacyTxType
|
||||
return r.setFromRLP(data)
|
||||
}
|
||||
// It's an EIP2718 typed transaction envelope.
|
||||
return r.decodeTyped(b)
|
||||
}
|
||||
|
||||
// decodeTyped decodes a typed receipt from the canonical format.
|
||||
func (r *Receipt) decodeTyped(b []byte) error {
|
||||
if len(b) <= 1 {
|
||||
return errShortTypedReceipt
|
||||
}
|
||||
switch b[0] {
|
||||
case DynamicFeeTxType, AccessListTxType:
|
||||
var data receiptRLP
|
||||
err := rlp.DecodeBytes(b[1:], &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Type = b[0]
|
||||
if r.Type == AccessListTxType {
|
||||
var dec receiptRLP
|
||||
if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.setFromRLP(dec)
|
||||
}
|
||||
return ErrTxTypeNotSupported
|
||||
return r.setFromRLP(data)
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -241,7 +267,7 @@ type ReceiptForStorage Receipt
|
|||
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||
// into an RLP stream.
|
||||
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
||||
enc := &v3StoredReceiptRLP{
|
||||
enc := &receiptStorageRLP{
|
||||
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
|
||||
CumulativeGasUsed: r.CumulativeGasUsed,
|
||||
Bloom: r.Bloom,
|
||||
|
|
@ -264,17 +290,11 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Try decoding from the newest format for future proofness, then the older one
|
||||
// for old nodes that just upgraded. V4 was an intermediate unreleased format so
|
||||
// we do need to decode it, but it's not common (try last).
|
||||
if err := decodeV3StoredReceiptRLP(r, blob); err == nil {
|
||||
return nil
|
||||
}
|
||||
return decodeV4StoredReceiptRLP(r, blob)
|
||||
return decodeStoredReceiptRLP(r, blob)
|
||||
}
|
||||
|
||||
func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
var stored v3StoredReceiptRLP
|
||||
func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
var stored receiptStorageRLP
|
||||
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -295,36 +315,15 @@ func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
var stored v4StoredReceiptRLP
|
||||
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||
r.TxHash = stored.TxHash
|
||||
r.ContractAddress = stored.ContractAddress
|
||||
r.GasUsed = stored.GasUsed
|
||||
r.Logs = make([]*Log, len(stored.Logs))
|
||||
for i, log := range stored.Logs {
|
||||
r.Logs[i] = (*Log)(log)
|
||||
}
|
||||
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Receipts implements DerivableList for receipts.
|
||||
type Receipts []*Receipt
|
||||
|
||||
// Len returns the number of receipts in this list.
|
||||
func (r Receipts) Len() int { return len(r) }
|
||||
func (rs Receipts) Len() int { return len(rs) }
|
||||
|
||||
// GetRlp returns the RLP encoding of one receipt from the list.
|
||||
func (r Receipts) GetRlp(i int) []byte {
|
||||
bytes, err := rlp.EncodeToBytes(r[i])
|
||||
func (rs Receipts) GetRlp(i int) []byte {
|
||||
bytes, err := rlp.EncodeToBytes(rs[i])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -333,42 +332,48 @@ func (r Receipts) GetRlp(i int) []byte {
|
|||
|
||||
// DeriveFields fills the receipts with their computed fields based on consensus
|
||||
// data and contextual infos like containing block and transactions.
|
||||
func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
|
||||
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, baseFee *big.Int, txs []*Transaction) error {
|
||||
signer := MakeSigner(config, new(big.Int).SetUint64(number))
|
||||
|
||||
logIndex := uint(0)
|
||||
if len(txs) != len(r) {
|
||||
if len(txs) != len(rs) {
|
||||
return errors.New("transaction and receipt count mismatch")
|
||||
}
|
||||
for i := 0; i < len(r); i++ {
|
||||
for i := 0; i < len(rs); i++ {
|
||||
// The transaction type and hash can be retrieved from the transaction itself
|
||||
r[i].Type = txs[i].Type()
|
||||
r[i].TxHash = txs[i].Hash()
|
||||
rs[i].Type = txs[i].Type()
|
||||
rs[i].TxHash = txs[i].Hash()
|
||||
|
||||
rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee)
|
||||
|
||||
// block location fields
|
||||
r[i].BlockHash = hash
|
||||
r[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||
r[i].TransactionIndex = uint(i)
|
||||
rs[i].BlockHash = hash
|
||||
rs[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||
rs[i].TransactionIndex = uint(i)
|
||||
|
||||
// The contract address can be derived from the transaction itself
|
||||
if txs[i].To() == nil {
|
||||
// Deriving the signer is expensive, only do if it's actually needed
|
||||
from, _ := Sender(signer, txs[i])
|
||||
r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
|
||||
rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
|
||||
} else {
|
||||
rs[i].ContractAddress = common.Address{}
|
||||
}
|
||||
|
||||
// The used gas can be calculated based on previous r
|
||||
if i == 0 {
|
||||
r[i].GasUsed = r[i].CumulativeGasUsed
|
||||
rs[i].GasUsed = rs[i].CumulativeGasUsed
|
||||
} else {
|
||||
r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed
|
||||
rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed
|
||||
}
|
||||
|
||||
// The derived log fields can simply be set from the block and transaction
|
||||
for j := 0; j < len(r[i].Logs); j++ {
|
||||
r[i].Logs[j].BlockNumber = number
|
||||
r[i].Logs[j].BlockHash = hash
|
||||
r[i].Logs[j].TxHash = r[i].TxHash
|
||||
r[i].Logs[j].TxIndex = uint(i)
|
||||
r[i].Logs[j].Index = logIndex
|
||||
for j := 0; j < len(rs[i].Logs); j++ {
|
||||
rs[i].Logs[j].BlockNumber = number
|
||||
rs[i].Logs[j].BlockHash = hash
|
||||
rs[i].Logs[j].TxHash = rs[i].TxHash
|
||||
rs[i].Logs[j].TxIndex = uint(i)
|
||||
rs[i].Logs[j].Index = logIndex
|
||||
logIndex++
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,35 +18,278 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"github.com/kylelemons/godebug/diff"
|
||||
)
|
||||
|
||||
var (
|
||||
legacyReceipt = &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
}
|
||||
accessListReceipt = &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
Type: AccessListTxType,
|
||||
}
|
||||
eip1559Receipt = &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
Type: DynamicFeeTxType,
|
||||
}
|
||||
|
||||
// Create a few transactions to have receipts for
|
||||
to2 = common.HexToAddress("0x2")
|
||||
to3 = common.HexToAddress("0x3")
|
||||
to4 = common.HexToAddress("0x4")
|
||||
to5 = common.HexToAddress("0x5")
|
||||
to6 = common.HexToAddress("0x6")
|
||||
to7 = common.HexToAddress("0x7")
|
||||
txs = Transactions{
|
||||
NewTx(&LegacyTx{
|
||||
Nonce: 1,
|
||||
Value: big.NewInt(1),
|
||||
Gas: 1,
|
||||
GasPrice: big.NewInt(11),
|
||||
}),
|
||||
NewTx(&LegacyTx{
|
||||
To: &to2,
|
||||
Nonce: 2,
|
||||
Value: big.NewInt(2),
|
||||
Gas: 2,
|
||||
GasPrice: big.NewInt(22),
|
||||
}),
|
||||
NewTx(&AccessListTx{
|
||||
To: &to3,
|
||||
Nonce: 3,
|
||||
Value: big.NewInt(3),
|
||||
Gas: 3,
|
||||
GasPrice: big.NewInt(33),
|
||||
}),
|
||||
// EIP-1559 transactions.
|
||||
NewTx(&DynamicFeeTx{
|
||||
To: &to4,
|
||||
Nonce: 4,
|
||||
Value: big.NewInt(4),
|
||||
Gas: 4,
|
||||
GasTipCap: big.NewInt(44),
|
||||
GasFeeCap: big.NewInt(1044),
|
||||
}),
|
||||
NewTx(&DynamicFeeTx{
|
||||
To: &to5,
|
||||
Nonce: 5,
|
||||
Value: big.NewInt(5),
|
||||
Gas: 5,
|
||||
GasTipCap: big.NewInt(55),
|
||||
GasFeeCap: big.NewInt(1055),
|
||||
}),
|
||||
}
|
||||
|
||||
blockNumber = big.NewInt(1)
|
||||
blockTime = uint64(2)
|
||||
blockHash = common.BytesToHash([]byte{0x03, 0x14})
|
||||
|
||||
// Create the corresponding receipts
|
||||
receipts = Receipts{
|
||||
&Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[0].Hash(),
|
||||
TxIndex: 0,
|
||||
BlockHash: blockHash,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[0].Hash(),
|
||||
TxIndex: 0,
|
||||
BlockHash: blockHash,
|
||||
Index: 1,
|
||||
},
|
||||
},
|
||||
// derived fields:
|
||||
TxHash: txs[0].Hash(),
|
||||
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
||||
GasUsed: 1,
|
||||
EffectiveGasPrice: big.NewInt(11),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 0,
|
||||
},
|
||||
&Receipt{
|
||||
PostState: common.Hash{2}.Bytes(),
|
||||
CumulativeGasUsed: 3,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x22}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[1].Hash(),
|
||||
TxIndex: 1,
|
||||
BlockHash: blockHash,
|
||||
Index: 2,
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x02, 0x22}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[1].Hash(),
|
||||
TxIndex: 1,
|
||||
BlockHash: blockHash,
|
||||
Index: 3,
|
||||
},
|
||||
},
|
||||
// derived fields:
|
||||
TxHash: txs[1].Hash(),
|
||||
GasUsed: 2,
|
||||
EffectiveGasPrice: big.NewInt(22),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 1,
|
||||
},
|
||||
&Receipt{
|
||||
Type: AccessListTxType,
|
||||
PostState: common.Hash{3}.Bytes(),
|
||||
CumulativeGasUsed: 6,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[2].Hash(),
|
||||
GasUsed: 3,
|
||||
EffectiveGasPrice: big.NewInt(33),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 2,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DynamicFeeTxType,
|
||||
PostState: common.Hash{4}.Bytes(),
|
||||
CumulativeGasUsed: 10,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[3].Hash(),
|
||||
GasUsed: 4,
|
||||
EffectiveGasPrice: big.NewInt(1044),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 3,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DynamicFeeTxType,
|
||||
PostState: common.Hash{5}.Bytes(),
|
||||
CumulativeGasUsed: 15,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[4].Hash(),
|
||||
GasUsed: 5,
|
||||
EffectiveGasPrice: big.NewInt(1055),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 4,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||
input := []byte{0x80}
|
||||
var r Receipt
|
||||
err := rlp.DecodeBytes(input, &r)
|
||||
if err != errEmptyTypedReceipt {
|
||||
if err != errShortTypedReceipt {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we can marshal/unmarshal receipts to/from json without errors.
|
||||
// This also confirms that our test receipts contain all the required fields.
|
||||
func TestReceiptJSON(t *testing.T) {
|
||||
for i := range receipts {
|
||||
b, err := receipts[i].MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal("error marshaling receipt to json:", err)
|
||||
}
|
||||
r := Receipt{}
|
||||
err = r.UnmarshalJSON(b)
|
||||
if err != nil {
|
||||
t.Fatal("error unmarshaling receipt from json:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test we can still parse receipt without EffectiveGasPrice for backwards compatibility, even
|
||||
// though it is required per the spec.
|
||||
func TestEffectiveGasPriceNotRequired(t *testing.T) {
|
||||
r := *receipts[0]
|
||||
r.EffectiveGasPrice = nil
|
||||
b, err := r.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal("error marshaling receipt to json:", err)
|
||||
}
|
||||
r2 := Receipt{}
|
||||
err = r2.UnmarshalJSON(b)
|
||||
if err != nil {
|
||||
t.Fatal("error unmarshaling receipt from json:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyReceiptDecoding(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
encode func(*Receipt) ([]byte, error)
|
||||
}{
|
||||
{
|
||||
"V4StoredReceiptRLP",
|
||||
encodeAsV4StoredReceiptRLP,
|
||||
},
|
||||
{
|
||||
"V3StoredReceiptRLP",
|
||||
encodeAsV3StoredReceiptRLP,
|
||||
|
|
@ -113,23 +356,8 @@ func TestLegacyReceiptDecoding(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
stored := &v4StoredReceiptRLP{
|
||||
PostStateOrStatus: want.statusEncoding(),
|
||||
CumulativeGasUsed: want.CumulativeGasUsed,
|
||||
TxHash: want.TxHash,
|
||||
ContractAddress: want.ContractAddress,
|
||||
Logs: make([]*LogForStorage, len(want.Logs)),
|
||||
GasUsed: want.GasUsed,
|
||||
}
|
||||
for i, log := range want.Logs {
|
||||
stored.Logs[i] = (*LogForStorage)(log)
|
||||
}
|
||||
return rlp.EncodeToBytes(stored)
|
||||
}
|
||||
|
||||
func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
stored := &v3StoredReceiptRLP{
|
||||
stored := &receiptStorageRLP{
|
||||
PostStateOrStatus: want.statusEncoding(),
|
||||
CumulativeGasUsed: want.CumulativeGasUsed,
|
||||
Bloom: want.Bloom,
|
||||
|
|
@ -149,162 +377,212 @@ func TestDeriveFields(t *testing.T) {
|
|||
// Create a few transactions to have receipts for
|
||||
to2 := common.HexToAddress("0x2")
|
||||
to3 := common.HexToAddress("0x3")
|
||||
to4 := common.HexToAddress("0x4")
|
||||
to5 := common.HexToAddress("0x5")
|
||||
txs := Transactions{
|
||||
NewTx(&LegacyTx{
|
||||
Nonce: 1,
|
||||
Value: big.NewInt(1),
|
||||
Gas: 1,
|
||||
GasPrice: big.NewInt(1),
|
||||
GasPrice: big.NewInt(11),
|
||||
}),
|
||||
NewTx(&LegacyTx{
|
||||
To: &to2,
|
||||
Nonce: 2,
|
||||
Value: big.NewInt(2),
|
||||
Gas: 2,
|
||||
GasPrice: big.NewInt(2),
|
||||
GasPrice: big.NewInt(22),
|
||||
}),
|
||||
NewTx(&AccessListTx{
|
||||
To: &to3,
|
||||
Nonce: 3,
|
||||
Value: big.NewInt(3),
|
||||
Gas: 3,
|
||||
GasPrice: big.NewInt(3),
|
||||
GasPrice: big.NewInt(33),
|
||||
}),
|
||||
// EIP-1559 transactions.
|
||||
NewTx(&DynamicFeeTx{
|
||||
To: &to4,
|
||||
Nonce: 4,
|
||||
Value: big.NewInt(4),
|
||||
Gas: 4,
|
||||
GasTipCap: big.NewInt(44),
|
||||
GasFeeCap: big.NewInt(1045),
|
||||
}),
|
||||
NewTx(&DynamicFeeTx{
|
||||
To: &to5,
|
||||
Nonce: 5,
|
||||
Value: big.NewInt(5),
|
||||
Gas: 5,
|
||||
GasTipCap: big.NewInt(56),
|
||||
GasFeeCap: big.NewInt(1055),
|
||||
}),
|
||||
}
|
||||
|
||||
blockNumber := big.NewInt(1)
|
||||
blockHash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
|
||||
// Create the corresponding receipts
|
||||
receipts := Receipts{
|
||||
&Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{Address: common.BytesToAddress([]byte{0x11})},
|
||||
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[0].Hash(),
|
||||
TxIndex: 0,
|
||||
BlockHash: blockHash,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[0].Hash(),
|
||||
TxIndex: 0,
|
||||
BlockHash: blockHash,
|
||||
Index: 1,
|
||||
},
|
||||
},
|
||||
TxHash: txs[0].Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||
GasUsed: 1,
|
||||
// derived fields:
|
||||
TxHash: txs[0].Hash(),
|
||||
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
||||
GasUsed: 1,
|
||||
EffectiveGasPrice: big.NewInt(11),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 0,
|
||||
},
|
||||
&Receipt{
|
||||
PostState: common.Hash{2}.Bytes(),
|
||||
CumulativeGasUsed: 3,
|
||||
Logs: []*Log{
|
||||
{Address: common.BytesToAddress([]byte{0x22})},
|
||||
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x22}),
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[1].Hash(),
|
||||
TxIndex: 1,
|
||||
BlockHash: blockHash,
|
||||
Index: 2,
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x02, 0x22}),
|
||||
// derived fields:
|
||||
BlockNumber: blockNumber.Uint64(),
|
||||
TxHash: txs[1].Hash(),
|
||||
TxIndex: 1,
|
||||
BlockHash: blockHash,
|
||||
Index: 3,
|
||||
},
|
||||
},
|
||||
TxHash: txs[1].Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
||||
GasUsed: 2,
|
||||
// derived fields:
|
||||
TxHash: txs[1].Hash(),
|
||||
GasUsed: 2,
|
||||
EffectiveGasPrice: big.NewInt(22),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 1,
|
||||
},
|
||||
&Receipt{
|
||||
Type: AccessListTxType,
|
||||
PostState: common.Hash{3}.Bytes(),
|
||||
CumulativeGasUsed: 6,
|
||||
Logs: []*Log{
|
||||
{Address: common.BytesToAddress([]byte{0x33})},
|
||||
{Address: common.BytesToAddress([]byte{0x03, 0x33})},
|
||||
},
|
||||
TxHash: txs[2].Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
|
||||
GasUsed: 3,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[2].Hash(),
|
||||
GasUsed: 3,
|
||||
EffectiveGasPrice: big.NewInt(33),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 2,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DynamicFeeTxType,
|
||||
PostState: common.Hash{4}.Bytes(),
|
||||
CumulativeGasUsed: 10,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[3].Hash(),
|
||||
GasUsed: 4,
|
||||
EffectiveGasPrice: big.NewInt(1044),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 3,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DynamicFeeTxType,
|
||||
PostState: common.Hash{5}.Bytes(),
|
||||
CumulativeGasUsed: 15,
|
||||
Logs: []*Log{},
|
||||
// derived fields:
|
||||
TxHash: txs[4].Hash(),
|
||||
GasUsed: 5,
|
||||
EffectiveGasPrice: big.NewInt(1055),
|
||||
BlockHash: blockHash,
|
||||
BlockNumber: blockNumber,
|
||||
TransactionIndex: 4,
|
||||
},
|
||||
}
|
||||
// Clear all the computed fields and re-derive them
|
||||
number := big.NewInt(1)
|
||||
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
|
||||
clearComputedFieldsOnReceipts(t, receipts)
|
||||
if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil {
|
||||
// Re-derive receipts.
|
||||
basefee := big.NewInt(1000)
|
||||
derivedReceipts := clearComputedFieldsOnReceipts(receipts)
|
||||
err := Receipts(derivedReceipts).DeriveFields(params.TestChainConfig, blockHash, blockNumber.Uint64(), basefee, txs)
|
||||
if err != nil {
|
||||
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
|
||||
}
|
||||
// Iterate over all the computed fields and check that they're correct
|
||||
signer := MakeSigner(params.TestChainConfig, number)
|
||||
|
||||
logIndex := uint(0)
|
||||
for i := range receipts {
|
||||
if receipts[i].Type != txs[i].Type() {
|
||||
t.Errorf("receipts[%d].Type = %d, want %d", i, receipts[i].Type, txs[i].Type())
|
||||
}
|
||||
if receipts[i].TxHash != txs[i].Hash() {
|
||||
t.Errorf("receipts[%d].TxHash = %s, want %s", i, receipts[i].TxHash.String(), txs[i].Hash().String())
|
||||
}
|
||||
if receipts[i].BlockHash != hash {
|
||||
t.Errorf("receipts[%d].BlockHash = %s, want %s", i, receipts[i].BlockHash.String(), hash.String())
|
||||
}
|
||||
if receipts[i].BlockNumber.Cmp(number) != 0 {
|
||||
t.Errorf("receipts[%c].BlockNumber = %s, want %s", i, receipts[i].BlockNumber.String(), number.String())
|
||||
}
|
||||
if receipts[i].TransactionIndex != uint(i) {
|
||||
t.Errorf("receipts[%d].TransactionIndex = %d, want %d", i, receipts[i].TransactionIndex, i)
|
||||
}
|
||||
if receipts[i].GasUsed != txs[i].Gas() {
|
||||
t.Errorf("receipts[%d].GasUsed = %d, want %d", i, receipts[i].GasUsed, txs[i].Gas())
|
||||
}
|
||||
if txs[i].To() != nil && receipts[i].ContractAddress != (common.Address{}) {
|
||||
t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), (common.Address{}).String())
|
||||
}
|
||||
from, _ := Sender(signer, txs[i])
|
||||
contractAddress := crypto.CreateAddress(from, txs[i].Nonce())
|
||||
if txs[i].To() == nil && receipts[i].ContractAddress != contractAddress {
|
||||
t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), contractAddress.String())
|
||||
}
|
||||
for j := range receipts[i].Logs {
|
||||
if receipts[i].Logs[j].BlockNumber != number.Uint64() {
|
||||
t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, number.Uint64())
|
||||
}
|
||||
if receipts[i].Logs[j].BlockHash != hash {
|
||||
t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
|
||||
}
|
||||
if receipts[i].Logs[j].TxHash != txs[i].Hash() {
|
||||
t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
|
||||
}
|
||||
if receipts[i].Logs[j].TxIndex != uint(i) {
|
||||
t.Errorf("receipts[%d].Logs[%d].TransactionIndex = %d, want %d", i, j, receipts[i].Logs[j].TxIndex, i)
|
||||
}
|
||||
if receipts[i].Logs[j].Index != logIndex {
|
||||
t.Errorf("receipts[%d].Logs[%d].Index = %d, want %d", i, j, receipts[i].Logs[j].Index, logIndex)
|
||||
}
|
||||
logIndex++
|
||||
}
|
||||
// Check diff of receipts against derivedReceipts.
|
||||
r1, err := json.MarshalIndent(receipts, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal("error marshaling input receipts:", err)
|
||||
}
|
||||
r2, err := json.MarshalIndent(derivedReceipts, "", " ")
|
||||
if err != nil {
|
||||
t.Fatal("error marshaling derived receipts:", err)
|
||||
}
|
||||
d := diff.Diff(string(r1), string(r2))
|
||||
if d != "" {
|
||||
t.Fatal("receipts differ:", d)
|
||||
}
|
||||
}
|
||||
|
||||
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
|
||||
t.Helper()
|
||||
|
||||
for _, receipt := range receipts {
|
||||
clearComputedFieldsOnReceipt(t, receipt)
|
||||
func clearComputedFieldsOnReceipts(receipts []*Receipt) []*Receipt {
|
||||
r := make([]*Receipt, len(receipts))
|
||||
for i, receipt := range receipts {
|
||||
r[i] = clearComputedFieldsOnReceipt(receipt)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func clearComputedFieldsOnReceipt(t *testing.T, receipt *Receipt) {
|
||||
t.Helper()
|
||||
|
||||
receipt.TxHash = common.Hash{}
|
||||
receipt.BlockHash = common.Hash{}
|
||||
receipt.BlockNumber = big.NewInt(math.MaxUint32)
|
||||
receipt.TransactionIndex = math.MaxUint32
|
||||
receipt.ContractAddress = common.Address{}
|
||||
receipt.GasUsed = 0
|
||||
|
||||
clearComputedFieldsOnLogs(t, receipt.Logs)
|
||||
func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt {
|
||||
cpy := *receipt
|
||||
cpy.TxHash = common.Hash{0xff, 0xff, 0x11}
|
||||
cpy.BlockHash = common.Hash{0xff, 0xff, 0x22}
|
||||
cpy.BlockNumber = big.NewInt(math.MaxUint32)
|
||||
cpy.TransactionIndex = math.MaxUint32
|
||||
cpy.ContractAddress = common.Address{0xff, 0xff, 0x33}
|
||||
cpy.GasUsed = 0xffffffff
|
||||
cpy.Logs = clearComputedFieldsOnLogs(receipt.Logs)
|
||||
return &cpy
|
||||
}
|
||||
|
||||
func clearComputedFieldsOnLogs(t *testing.T, logs []*Log) {
|
||||
t.Helper()
|
||||
|
||||
for _, log := range logs {
|
||||
clearComputedFieldsOnLog(t, log)
|
||||
func clearComputedFieldsOnLogs(logs []*Log) []*Log {
|
||||
l := make([]*Log, len(logs))
|
||||
for i, log := range logs {
|
||||
cpy := *log
|
||||
cpy.BlockNumber = math.MaxUint32
|
||||
cpy.BlockHash = common.Hash{}
|
||||
cpy.TxHash = common.Hash{}
|
||||
cpy.TxIndex = math.MaxUint32
|
||||
cpy.Index = math.MaxUint32
|
||||
l[i] = &cpy
|
||||
}
|
||||
}
|
||||
|
||||
func clearComputedFieldsOnLog(t *testing.T, log *Log) {
|
||||
t.Helper()
|
||||
|
||||
log.BlockNumber = math.MaxUint32
|
||||
log.BlockHash = common.Hash{}
|
||||
log.TxHash = common.Hash{}
|
||||
log.TxIndex = math.MaxUint32
|
||||
log.Index = math.MaxUint32
|
||||
return l
|
||||
}
|
||||
|
||||
// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
|
||||
|
|
@ -334,3 +612,38 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
|
|||
check(bundle)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReceiptUnmarshalBinary(t *testing.T) {
|
||||
// Legacy Receipt
|
||||
legacyBinary := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
|
||||
gotLegacyReceipt := new(Receipt)
|
||||
if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
|
||||
t.Fatalf("unmarshal binary error: %v", err)
|
||||
}
|
||||
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
|
||||
if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
|
||||
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt)
|
||||
}
|
||||
|
||||
// 2930 Receipt
|
||||
accessListBinary := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
|
||||
gotAccessListReceipt := new(Receipt)
|
||||
if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil {
|
||||
t.Fatalf("unmarshal binary error: %v", err)
|
||||
}
|
||||
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
|
||||
if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
|
||||
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt)
|
||||
}
|
||||
|
||||
// 1559 Receipt
|
||||
eip1559RctBinary := common.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
|
||||
got1559Receipt := new(Receipt)
|
||||
if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil {
|
||||
t.Fatalf("unmarshal binary error: %v", err)
|
||||
}
|
||||
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
|
||||
if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) {
|
||||
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,23 +27,25 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
)
|
||||
|
||||
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
|
||||
var (
|
||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||
ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
|
||||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||
errShortTypedTx = errors.New("typed transaction too short")
|
||||
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
|
||||
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
|
||||
errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
|
||||
errEmptyTypedTx = errors.New("empty typed transaction bytes")
|
||||
errNoSigner = errors.New("missing signing methods")
|
||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||
ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
|
||||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||
errShortTypedTx = errors.New("typed transaction too short")
|
||||
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
|
||||
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
|
||||
errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
|
||||
errNoSigner = errors.New("missing signing methods")
|
||||
ErrFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||
|
||||
skipNonceDestinationAddress = map[common.Address]bool{
|
||||
common.XDCXAddrBinary: true,
|
||||
common.TradingStateAddrBinary: true,
|
||||
|
|
@ -56,6 +58,7 @@ var (
|
|||
const (
|
||||
LegacyTxType = iota
|
||||
AccessListTxType
|
||||
DynamicFeeTxType
|
||||
)
|
||||
|
||||
// Transaction is an Ethereum transaction.
|
||||
|
|
@ -88,12 +91,22 @@ type TxData interface {
|
|||
data() []byte
|
||||
gas() uint64
|
||||
gasPrice() *big.Int
|
||||
gasTipCap() *big.Int
|
||||
gasFeeCap() *big.Int
|
||||
value() *big.Int
|
||||
nonce() uint64
|
||||
to() *common.Address
|
||||
|
||||
rawSignatureValues() (v, r, s *big.Int)
|
||||
setSignatureValues(chainID, v, r, s *big.Int)
|
||||
|
||||
// effectiveGasPrice computes the gas price paid by the transaction, given
|
||||
// the inclusion block baseFee.
|
||||
//
|
||||
// Unlike other TxData methods, the returned *big.Int should be an independent
|
||||
// copy of the computed value, i.e. callers are allowed to mutate the result.
|
||||
// Method implementations can use 'dst' to store the result.
|
||||
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
|
|
@ -143,7 +156,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|||
tx.setDecoded(&inner, int(rlp.ListSize(size)))
|
||||
}
|
||||
return err
|
||||
case kind == rlp.String:
|
||||
default:
|
||||
// It's an EIP-2718 typed TX envelope.
|
||||
var b []byte
|
||||
if b, err = s.Bytes(); err != nil {
|
||||
|
|
@ -154,8 +167,6 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|||
tx.setDecoded(inner, len(b))
|
||||
}
|
||||
return err
|
||||
default:
|
||||
return rlp.ErrExpectedList
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,14 +194,18 @@ func (tx *Transaction) UnmarshalBinary(b []byte) error {
|
|||
|
||||
// decodeTyped decodes a typed transaction from the canonical format.
|
||||
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||
if len(b) == 0 {
|
||||
return nil, errEmptyTypedTx
|
||||
if len(b) <= 1 {
|
||||
return nil, errShortTypedTx
|
||||
}
|
||||
switch b[0] {
|
||||
case AccessListTxType:
|
||||
var inner AccessListTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
case DynamicFeeTxType:
|
||||
var inner DynamicFeeTx
|
||||
err := rlp.DecodeBytes(b[1:], &inner)
|
||||
return &inner, err
|
||||
default:
|
||||
return nil, ErrTxTypeNotSupported
|
||||
}
|
||||
|
|
@ -274,6 +289,12 @@ func (tx *Transaction) Gas() uint64 { return tx.inner.gas() }
|
|||
// GasPrice returns the gas price of the transaction.
|
||||
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
|
||||
|
||||
// GasTipCap returns the gasTipCap per gas of the transaction.
|
||||
func (tx *Transaction) GasTipCap() *big.Int { return new(big.Int).Set(tx.inner.gasTipCap()) }
|
||||
|
||||
// GasFeeCap returns the fee cap per gas of the transaction.
|
||||
func (tx *Transaction) GasFeeCap() *big.Int { return new(big.Int).Set(tx.inner.gasFeeCap()) }
|
||||
|
||||
// Value returns the ether amount of the transaction.
|
||||
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
|
||||
|
||||
|
|
@ -283,13 +304,7 @@ func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
|
|||
// To returns the recipient address of the transaction.
|
||||
// For contract-creation transactions, To returns nil.
|
||||
func (tx *Transaction) To() *common.Address {
|
||||
// Copy the pointed-to address.
|
||||
ito := tx.inner.to()
|
||||
if ito == nil {
|
||||
return nil
|
||||
}
|
||||
cpy := *ito
|
||||
return &cpy
|
||||
return copyAddressPtr(tx.inner.to())
|
||||
}
|
||||
|
||||
func (tx *Transaction) From() *common.Address {
|
||||
|
|
@ -306,20 +321,75 @@ func (tx *Transaction) From() *common.Address {
|
|||
return &from
|
||||
}
|
||||
|
||||
// Cost returns gas * gasPrice + value.
|
||||
func (tx *Transaction) Cost() *big.Int {
|
||||
total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
|
||||
total.Add(total, tx.Value())
|
||||
return total
|
||||
}
|
||||
|
||||
// RawSignatureValues returns the V, R, S signature values of the transaction.
|
||||
// The return values should not be modified by the caller.
|
||||
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.inner.rawSignatureValues()
|
||||
}
|
||||
|
||||
// GasPriceCmp compares the gas prices of two transactions.
|
||||
func (tx *Transaction) GasPriceCmp(other *Transaction) int {
|
||||
return tx.inner.gasPrice().Cmp(other.inner.gasPrice())
|
||||
// GasFeeCapCmp compares the fee cap of two transactions.
|
||||
func (tx *Transaction) GasFeeCapCmp(other *Transaction) int {
|
||||
return tx.inner.gasFeeCap().Cmp(other.inner.gasFeeCap())
|
||||
}
|
||||
|
||||
// GasPriceIntCmp compares the gas price of the transaction against the given price.
|
||||
func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
|
||||
return tx.inner.gasPrice().Cmp(other)
|
||||
// GasFeeCapIntCmp compares the fee cap of the transaction against the given fee cap.
|
||||
func (tx *Transaction) GasFeeCapIntCmp(other *big.Int) int {
|
||||
return tx.inner.gasFeeCap().Cmp(other)
|
||||
}
|
||||
|
||||
// GasTipCapCmp compares the gasTipCap of two transactions.
|
||||
func (tx *Transaction) GasTipCapCmp(other *Transaction) int {
|
||||
return tx.inner.gasTipCap().Cmp(other.inner.gasTipCap())
|
||||
}
|
||||
|
||||
// GasTipCapIntCmp compares the gasTipCap of the transaction against the given gasTipCap.
|
||||
func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int {
|
||||
return tx.inner.gasTipCap().Cmp(other)
|
||||
}
|
||||
|
||||
// EffectiveGasTip returns the effective miner gasTipCap for the given base fee.
|
||||
// Note: if the effective gasTipCap is negative, this method returns both error
|
||||
// the actual negative value, _and_ ErrGasFeeCapTooLow
|
||||
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
||||
if baseFee == nil {
|
||||
return tx.GasTipCap(), nil
|
||||
}
|
||||
var err error
|
||||
gasFeeCap := tx.GasFeeCap()
|
||||
if gasFeeCap.Cmp(baseFee) == -1 {
|
||||
err = ErrGasFeeCapTooLow
|
||||
}
|
||||
return math.BigMin(tx.GasTipCap(), gasFeeCap.Sub(gasFeeCap, baseFee)), err
|
||||
}
|
||||
|
||||
// EffectiveGasTipValue is identical to EffectiveGasTip, but does not return an
|
||||
// error in case the effective gasTipCap is negative
|
||||
func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int {
|
||||
effectiveTip, _ := tx.EffectiveGasTip(baseFee)
|
||||
return effectiveTip
|
||||
}
|
||||
|
||||
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
|
||||
func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) int {
|
||||
if baseFee == nil {
|
||||
return tx.GasTipCapCmp(other)
|
||||
}
|
||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other.EffectiveGasTipValue(baseFee))
|
||||
}
|
||||
|
||||
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
|
||||
func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) int {
|
||||
if baseFee == nil {
|
||||
return tx.GasTipCapIntCmp(other)
|
||||
}
|
||||
return tx.EffectiveGasTipValue(baseFee).Cmp(other)
|
||||
}
|
||||
|
||||
// Hash returns the transaction hash.
|
||||
|
|
@ -350,31 +420,43 @@ func (tx *Transaction) Size() common.StorageSize {
|
|||
return common.StorageSize(c)
|
||||
}
|
||||
|
||||
func (tx *Transaction) EffectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
return tx.inner.effectiveGasPrice(dst, baseFee)
|
||||
}
|
||||
|
||||
// AsMessage returns the transaction as a core.Message.
|
||||
func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int) (Message, error) {
|
||||
func (tx *Transaction) AsMessage(s Signer, balanceFee, blockNumber, baseFee *big.Int) (Message, error) {
|
||||
msg := Message{
|
||||
nonce: tx.Nonce(),
|
||||
gasLimit: tx.Gas(),
|
||||
gasPrice: new(big.Int).Set(tx.GasPrice()),
|
||||
gasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
|
||||
gasTipCap: new(big.Int).Set(tx.GasTipCap()),
|
||||
to: tx.To(),
|
||||
amount: tx.Value(),
|
||||
data: tx.Data(),
|
||||
accessList: tx.AccessList(),
|
||||
checkNonce: true,
|
||||
isFake: false,
|
||||
balanceTokenFee: balanceFee,
|
||||
}
|
||||
|
||||
if balanceFee != nil {
|
||||
if blockNumber != nil {
|
||||
if blockNumber.Cmp(common.BlockNumberGas50x) >= 0 {
|
||||
msg.gasPrice = common.GasPrice50x
|
||||
} else if blockNumber.Cmp(common.TIPTRC21Fee) > 0 {
|
||||
msg.gasPrice = common.TRC21GasPrice
|
||||
} else {
|
||||
msg.gasPrice = common.TRC21GasPriceBefore
|
||||
}
|
||||
}
|
||||
} else if baseFee != nil {
|
||||
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
||||
msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap)
|
||||
}
|
||||
|
||||
var err error
|
||||
msg.from, err = Sender(s, tx)
|
||||
if balanceFee != nil {
|
||||
if number.Cmp(common.BlockNumberGas50x) >= 0 {
|
||||
msg.gasPrice = common.GasPrice50x
|
||||
} else if number.Cmp(common.TIPTRC21Fee) > 0 {
|
||||
msg.gasPrice = common.TRC21GasPrice
|
||||
} else {
|
||||
msg.gasPrice = common.TRC21GasPriceBefore
|
||||
}
|
||||
}
|
||||
return msg, err
|
||||
}
|
||||
|
||||
|
|
@ -390,13 +472,6 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
|
|||
return &Transaction{inner: cpy, time: tx.time}, nil
|
||||
}
|
||||
|
||||
// Cost returns gas * gasPrice + value.
|
||||
func (tx *Transaction) Cost() *big.Int {
|
||||
total := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas()))
|
||||
total.Add(total, tx.Value())
|
||||
return total
|
||||
}
|
||||
|
||||
// TxCost returns gas * gasPrice + value.
|
||||
func (tx *Transaction) TxCost(number *big.Int) *big.Int {
|
||||
total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.Gas()))
|
||||
|
|
@ -742,13 +817,15 @@ type Message struct {
|
|||
amount *big.Int
|
||||
gasLimit uint64
|
||||
gasPrice *big.Int
|
||||
gasFeeCap *big.Int
|
||||
gasTipCap *big.Int
|
||||
data []byte
|
||||
accessList AccessList
|
||||
checkNonce bool
|
||||
isFake bool
|
||||
balanceTokenFee *big.Int
|
||||
}
|
||||
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, accessList AccessList, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message {
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool, balanceTokenFee *big.Int, number *big.Int) Message {
|
||||
if balanceTokenFee != nil {
|
||||
gasPrice = common.GetGasPrice(number)
|
||||
}
|
||||
|
|
@ -759,9 +836,11 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
|||
amount: amount,
|
||||
gasLimit: gasLimit,
|
||||
gasPrice: gasPrice,
|
||||
gasFeeCap: gasFeeCap,
|
||||
gasTipCap: gasTipCap,
|
||||
data: data,
|
||||
accessList: accessList,
|
||||
checkNonce: checkNonce,
|
||||
isFake: isFake,
|
||||
balanceTokenFee: balanceTokenFee,
|
||||
}
|
||||
}
|
||||
|
|
@ -770,11 +849,13 @@ func (m Message) From() common.Address { return m.from }
|
|||
func (m Message) BalanceTokenFee() *big.Int { return m.balanceTokenFee }
|
||||
func (m Message) To() *common.Address { return m.to }
|
||||
func (m Message) GasPrice() *big.Int { return m.gasPrice }
|
||||
func (m Message) GasFeeCap() *big.Int { return m.gasFeeCap }
|
||||
func (m Message) GasTipCap() *big.Int { return m.gasTipCap }
|
||||
func (m Message) Value() *big.Int { return m.amount }
|
||||
func (m Message) Gas() uint64 { return m.gasLimit }
|
||||
func (m Message) Nonce() uint64 { return m.nonce }
|
||||
func (m Message) Data() []byte { return m.data }
|
||||
func (m Message) CheckNonce() bool { return m.checkNonce }
|
||||
func (m Message) IsFake() bool { return m.isFake }
|
||||
func (m Message) AccessList() AccessList { return m.accessList }
|
||||
|
||||
func (m *Message) SetNonce(nonce uint64) { m.nonce = nonce }
|
||||
|
|
@ -783,3 +864,16 @@ func (m *Message) SetBalanceTokenFeeForCall() {
|
|||
m.balanceTokenFee = new(big.Int).SetUint64(m.gasLimit)
|
||||
m.balanceTokenFee.Mul(m.balanceTokenFee, m.gasPrice)
|
||||
}
|
||||
|
||||
func (m *Message) SetBalanceTokenFee(balanceTokenFee *big.Int) {
|
||||
m.balanceTokenFee = balanceTokenFee
|
||||
}
|
||||
|
||||
// copyAddressPtr copies an address.
|
||||
func copyAddressPtr(a *common.Address) *common.Address {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
cpy := *a
|
||||
return &cpy
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
|
|
@ -14,15 +30,17 @@ type txJSON struct {
|
|||
Type hexutil.Uint64 `json:"type"`
|
||||
|
||||
// Common transaction fields:
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
To *common.Address `json:"to"`
|
||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
|
||||
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
|
||||
Gas *hexutil.Uint64 `json:"gas"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Data *hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
To *common.Address `json:"to"`
|
||||
|
||||
// Access list transaction fields:
|
||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||
|
|
@ -63,6 +81,19 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
|
|||
enc.V = (*hexutil.Big)(tx.V)
|
||||
enc.R = (*hexutil.Big)(tx.R)
|
||||
enc.S = (*hexutil.Big)(tx.S)
|
||||
case *DynamicFeeTx:
|
||||
enc.ChainID = (*hexutil.Big)(tx.ChainID)
|
||||
enc.AccessList = &tx.AccessList
|
||||
enc.Nonce = (*hexutil.Uint64)(&tx.Nonce)
|
||||
enc.Gas = (*hexutil.Uint64)(&tx.Gas)
|
||||
enc.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap)
|
||||
enc.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap)
|
||||
enc.Value = (*hexutil.Big)(tx.Value)
|
||||
enc.Data = (*hexutil.Bytes)(&tx.Data)
|
||||
enc.To = t.To()
|
||||
enc.V = (*hexutil.Big)(tx.V)
|
||||
enc.R = (*hexutil.Big)(tx.R)
|
||||
enc.S = (*hexutil.Big)(tx.S)
|
||||
}
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
|
@ -175,6 +206,63 @@ func (t *Transaction) UnmarshalJSON(input []byte) error {
|
|||
}
|
||||
}
|
||||
|
||||
case DynamicFeeTxType:
|
||||
var itx DynamicFeeTx
|
||||
inner = &itx
|
||||
// Access list is optional for now.
|
||||
if dec.AccessList != nil {
|
||||
itx.AccessList = *dec.AccessList
|
||||
}
|
||||
if dec.ChainID == nil {
|
||||
return errors.New("missing required field 'chainId' in transaction")
|
||||
}
|
||||
itx.ChainID = (*big.Int)(dec.ChainID)
|
||||
if dec.To != nil {
|
||||
itx.To = dec.To
|
||||
}
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' in transaction")
|
||||
}
|
||||
itx.Nonce = uint64(*dec.Nonce)
|
||||
if dec.MaxPriorityFeePerGas == nil {
|
||||
return errors.New("missing required field 'maxPriorityFeePerGas' for txdata")
|
||||
}
|
||||
itx.GasTipCap = (*big.Int)(dec.MaxPriorityFeePerGas)
|
||||
if dec.MaxFeePerGas == nil {
|
||||
return errors.New("missing required field 'maxFeePerGas' for txdata")
|
||||
}
|
||||
itx.GasFeeCap = (*big.Int)(dec.MaxFeePerGas)
|
||||
if dec.Gas == nil {
|
||||
return errors.New("missing required field 'gas' for txdata")
|
||||
}
|
||||
itx.Gas = uint64(*dec.Gas)
|
||||
if dec.Value == nil {
|
||||
return errors.New("missing required field 'value' in transaction")
|
||||
}
|
||||
itx.Value = (*big.Int)(dec.Value)
|
||||
if dec.Data == nil {
|
||||
return errors.New("missing required field 'input' in transaction")
|
||||
}
|
||||
itx.Data = *dec.Data
|
||||
if dec.V == nil {
|
||||
return errors.New("missing required field 'v' in transaction")
|
||||
}
|
||||
itx.V = (*big.Int)(dec.V)
|
||||
if dec.R == nil {
|
||||
return errors.New("missing required field 'r' in transaction")
|
||||
}
|
||||
itx.R = (*big.Int)(dec.R)
|
||||
if dec.S == nil {
|
||||
return errors.New("missing required field 's' in transaction")
|
||||
}
|
||||
itx.S = (*big.Int)(dec.S)
|
||||
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
|
||||
if withSignature {
|
||||
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
|||
var signer Signer
|
||||
switch {
|
||||
case config.IsEIP1559(blockNumber):
|
||||
signer = NewEIP2930Signer(config.ChainId)
|
||||
signer = NewLondonSigner(config.ChainId)
|
||||
case config.IsEIP155(blockNumber):
|
||||
signer = NewEIP155Signer(config.ChainId)
|
||||
case config.IsHomestead(blockNumber):
|
||||
|
|
@ -63,7 +63,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
|||
func LatestSigner(config *params.ChainConfig) Signer {
|
||||
if config.ChainId != nil {
|
||||
if common.Eip1559Block.Uint64() != 9999999999 || config.Eip1559Block != nil {
|
||||
return NewEIP2930Signer(config.ChainId)
|
||||
return NewLondonSigner(config.ChainId)
|
||||
}
|
||||
if config.EIP155Block != nil {
|
||||
return NewEIP155Signer(config.ChainId)
|
||||
|
|
@ -83,7 +83,7 @@ func LatestSignerForChainID(chainID *big.Int) Signer {
|
|||
if chainID == nil {
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
return NewEIP2930Signer(chainID)
|
||||
return NewLondonSigner(chainID)
|
||||
}
|
||||
|
||||
// SignTx signs the transaction using the given signer and private key.
|
||||
|
|
@ -170,6 +170,72 @@ type Signer interface {
|
|||
Equal(Signer) bool
|
||||
}
|
||||
|
||||
type londonSigner struct{ eip2930Signer }
|
||||
|
||||
// NewLondonSigner returns a signer that accepts
|
||||
// - EIP-1559 dynamic fee transactions
|
||||
// - EIP-2930 access list transactions,
|
||||
// - EIP-155 replay protected transactions, and
|
||||
// - legacy Homestead transactions.
|
||||
func NewLondonSigner(chainId *big.Int) Signer {
|
||||
return londonSigner{eip2930Signer{NewEIP155Signer(chainId)}}
|
||||
}
|
||||
|
||||
func (s londonSigner) Sender(tx *Transaction) (common.Address, error) {
|
||||
if tx.Type() != DynamicFeeTxType {
|
||||
return s.eip2930Signer.Sender(tx)
|
||||
}
|
||||
V, R, S := tx.RawSignatureValues()
|
||||
// DynamicFee txs are defined to use 0 and 1 as their recovery
|
||||
// id, add 27 to become equivalent to unprotected Homestead signatures.
|
||||
V = new(big.Int).Add(V, big.NewInt(27))
|
||||
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||
return common.Address{}, ErrInvalidChainId
|
||||
}
|
||||
return recoverPlain(s.Hash(tx), R, S, V, true)
|
||||
}
|
||||
|
||||
func (s londonSigner) Equal(s2 Signer) bool {
|
||||
x, ok := s2.(londonSigner)
|
||||
return ok && x.chainId.Cmp(s.chainId) == 0
|
||||
}
|
||||
|
||||
func (s londonSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
|
||||
txdata, ok := tx.inner.(*DynamicFeeTx)
|
||||
if !ok {
|
||||
return s.eip2930Signer.SignatureValues(tx, sig)
|
||||
}
|
||||
// Check that chain ID of tx matches the signer. We also accept ID zero here,
|
||||
// because it indicates that the chain ID was not specified in the tx.
|
||||
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
|
||||
return nil, nil, nil, ErrInvalidChainId
|
||||
}
|
||||
R, S, _ = decodeSignature(sig)
|
||||
V = big.NewInt(int64(sig[64]))
|
||||
return R, S, V, nil
|
||||
}
|
||||
|
||||
// Hash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s londonSigner) Hash(tx *Transaction) common.Hash {
|
||||
if tx.Type() != DynamicFeeTxType {
|
||||
return s.eip2930Signer.Hash(tx)
|
||||
}
|
||||
return prefixedRlpHash(
|
||||
tx.Type(),
|
||||
[]interface{}{
|
||||
s.chainId,
|
||||
tx.Nonce(),
|
||||
tx.GasTipCap(),
|
||||
tx.GasFeeCap(),
|
||||
tx.Gas(),
|
||||
tx.To(),
|
||||
tx.Value(),
|
||||
tx.Data(),
|
||||
tx.AccessList(),
|
||||
})
|
||||
}
|
||||
|
||||
type eip2930Signer struct{ EIP155Signer }
|
||||
|
||||
// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
|
||||
|
|
@ -193,8 +259,8 @@ func (s eip2930Signer) Sender(tx *Transaction) (common.Address, error) {
|
|||
case LegacyTxType:
|
||||
return s.EIP155Signer.Sender(tx)
|
||||
case AccessListTxType:
|
||||
// ACL txs are defined to use 0 and 1 as their recovery id, add
|
||||
// 27 to become equivalent to unprotected Homestead signatures.
|
||||
// AL txs are defined to use 0 and 1 as their recovery
|
||||
// id, add 27 to become equivalent to unprotected Homestead signatures.
|
||||
V = new(big.Int).Add(V, big.NewInt(27))
|
||||
default:
|
||||
return common.Address{}, ErrTxTypeNotSupported
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ func TestDecodeEmptyTypedTx(t *testing.T) {
|
|||
input := []byte{0x80}
|
||||
var tx Transaction
|
||||
err := rlp.DecodeBytes(input, &tx)
|
||||
if err != errEmptyTypedTx {
|
||||
if err != errShortTypedTx {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ type AccessListTx struct {
|
|||
func (tx *AccessListTx) copy() TxData {
|
||||
cpy := &AccessListTx{
|
||||
Nonce: tx.Nonce,
|
||||
To: tx.To, // TODO: copy pointed-to address
|
||||
To: copyAddressPtr(tx.To),
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
Gas: tx.Gas,
|
||||
// These are copied below.
|
||||
|
|
@ -94,18 +94,22 @@ func (tx *AccessListTx) copy() TxData {
|
|||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
|
||||
func (tx *AccessListTx) txType() byte { return AccessListTxType }
|
||||
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
|
||||
func (tx *AccessListTx) protected() bool { return true }
|
||||
func (tx *AccessListTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *AccessListTx) data() []byte { return tx.Data }
|
||||
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) gasTipCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *AccessListTx) value() *big.Int { return tx.Value }
|
||||
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *AccessListTx) to() *common.Address { return tx.To }
|
||||
|
||||
func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
return dst.Set(tx.GasPrice)
|
||||
}
|
||||
|
||||
func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.V, tx.R, tx.S
|
||||
}
|
||||
114
core/types/tx_dynamic_fee.go
Normal file
114
core/types/tx_dynamic_fee.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
)
|
||||
|
||||
type DynamicFeeTx struct {
|
||||
ChainID *big.Int
|
||||
Nonce uint64
|
||||
GasTipCap *big.Int // a.k.a. maxPriorityFeePerGas
|
||||
GasFeeCap *big.Int // a.k.a. maxFeePerGas
|
||||
Gas uint64
|
||||
To *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Value *big.Int
|
||||
Data []byte
|
||||
AccessList AccessList
|
||||
|
||||
// Signature values
|
||||
V *big.Int `json:"v" gencodec:"required"`
|
||||
R *big.Int `json:"r" gencodec:"required"`
|
||||
S *big.Int `json:"s" gencodec:"required"`
|
||||
}
|
||||
|
||||
// copy creates a deep copy of the transaction data and initializes all fields.
|
||||
func (tx *DynamicFeeTx) copy() TxData {
|
||||
cpy := &DynamicFeeTx{
|
||||
Nonce: tx.Nonce,
|
||||
To: copyAddressPtr(tx.To),
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
Gas: tx.Gas,
|
||||
// These are copied below.
|
||||
AccessList: make(AccessList, len(tx.AccessList)),
|
||||
Value: new(big.Int),
|
||||
ChainID: new(big.Int),
|
||||
GasTipCap: new(big.Int),
|
||||
GasFeeCap: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
}
|
||||
copy(cpy.AccessList, tx.AccessList)
|
||||
if tx.Value != nil {
|
||||
cpy.Value.Set(tx.Value)
|
||||
}
|
||||
if tx.ChainID != nil {
|
||||
cpy.ChainID.Set(tx.ChainID)
|
||||
}
|
||||
if tx.GasTipCap != nil {
|
||||
cpy.GasTipCap.Set(tx.GasTipCap)
|
||||
}
|
||||
if tx.GasFeeCap != nil {
|
||||
cpy.GasFeeCap.Set(tx.GasFeeCap)
|
||||
}
|
||||
if tx.V != nil {
|
||||
cpy.V.Set(tx.V)
|
||||
}
|
||||
if tx.R != nil {
|
||||
cpy.R.Set(tx.R)
|
||||
}
|
||||
if tx.S != nil {
|
||||
cpy.S.Set(tx.S)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// accessors for innerTx.
|
||||
func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
|
||||
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
|
||||
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
|
||||
func (tx *DynamicFeeTx) data() []byte { return tx.Data }
|
||||
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *DynamicFeeTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *DynamicFeeTx) gasTipCap() *big.Int { return tx.GasTipCap }
|
||||
func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap }
|
||||
func (tx *DynamicFeeTx) value() *big.Int { return tx.Value }
|
||||
func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *DynamicFeeTx) to() *common.Address { return tx.To }
|
||||
|
||||
func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
if baseFee == nil {
|
||||
return dst.Set(tx.GasFeeCap)
|
||||
}
|
||||
tip := dst.Sub(tx.GasFeeCap, baseFee)
|
||||
if tip.Cmp(tx.GasTipCap) > 0 {
|
||||
tip.Set(tx.GasTipCap)
|
||||
}
|
||||
return tip.Add(tip, baseFee)
|
||||
}
|
||||
|
||||
func (tx *DynamicFeeTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.V, tx.R, tx.S
|
||||
}
|
||||
|
||||
func (tx *DynamicFeeTx) setSignatureValues(chainID, v, r, s *big.Int) {
|
||||
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPric
|
|||
func (tx *LegacyTx) copy() TxData {
|
||||
cpy := &LegacyTx{
|
||||
Nonce: tx.Nonce,
|
||||
To: tx.To, // TODO: copy pointed-to address
|
||||
To: copyAddressPtr(tx.To),
|
||||
Data: common.CopyBytes(tx.Data),
|
||||
Gas: tx.Gas,
|
||||
// These are initialized below.
|
||||
|
|
@ -98,10 +98,16 @@ func (tx *LegacyTx) accessList() AccessList { return nil }
|
|||
func (tx *LegacyTx) data() []byte { return tx.Data }
|
||||
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
|
||||
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) gasTipCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice }
|
||||
func (tx *LegacyTx) value() *big.Int { return tx.Value }
|
||||
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
|
||||
func (tx *LegacyTx) to() *common.Address { return tx.To }
|
||||
|
||||
func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
|
||||
return dst.Set(tx.GasPrice)
|
||||
}
|
||||
|
||||
func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||
return tx.V, tx.R, tx.S
|
||||
}
|
||||
|
|
@ -61,16 +61,14 @@ func (al accessList) equal(other accessList) bool {
|
|||
if len(al) != len(other) {
|
||||
return false
|
||||
}
|
||||
// Given that len(al) == len(other), we only need to check that
|
||||
// all the items from al are in other.
|
||||
for addr := range al {
|
||||
if _, ok := other[addr]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for addr := range other {
|
||||
if _, ok := al[addr]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Accounts match, cross reference the storage slots too
|
||||
for addr, slots := range al {
|
||||
otherslots := other[addr]
|
||||
|
|
@ -78,16 +76,13 @@ func (al accessList) equal(other accessList) bool {
|
|||
if len(slots) != len(otherslots) {
|
||||
return false
|
||||
}
|
||||
// Given that len(slots) == len(otherslots), we only need to check that
|
||||
// all the items from slots are in otherslots.
|
||||
for hash := range slots {
|
||||
if _, ok := otherslots[hash]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for hash := range otherslots {
|
||||
if _, ok := slots[hash]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import (
|
|||
|
||||
var activators = map[int]func(*JumpTable){
|
||||
3855: enable3855,
|
||||
3860: enable3860,
|
||||
3529: enable3529,
|
||||
3198: enable3198,
|
||||
2929: enable2929,
|
||||
2200: enable2200,
|
||||
|
|
@ -104,28 +106,28 @@ func enable2929(jt *JumpTable) {
|
|||
jt[SLOAD].constantGas = 0
|
||||
jt[SLOAD].dynamicGas = gasSLoadEIP2929
|
||||
|
||||
jt[EXTCODECOPY].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
|
||||
|
||||
jt[EXTCODESIZE].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[EXTCODEHASH].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[BALANCE].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
||||
|
||||
jt[CALL].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[CALL].dynamicGas = gasCallEIP2929
|
||||
|
||||
jt[CALLCODE].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
||||
|
||||
jt[STATICCALL].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
||||
|
||||
jt[DELEGATECALL].constantGas = WarmStorageReadCostEIP2929
|
||||
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
||||
|
||||
// This was previously part of the dynamic cost, but we're using it as a constantGas
|
||||
|
|
@ -134,6 +136,15 @@ func enable2929(jt *JumpTable) {
|
|||
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
|
||||
}
|
||||
|
||||
// enable3529 enabled "EIP-3529: Reduction in refunds":
|
||||
// - Removes refunds for selfdestructs
|
||||
// - Reduces refunds for SSTORE
|
||||
// - Reduces max refunds to 20% gas
|
||||
func enable3529(jt *JumpTable) {
|
||||
jt[SSTORE].dynamicGas = gasSStoreEIP3529
|
||||
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
|
||||
}
|
||||
|
||||
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
||||
// - Adds an opcode that returns the current block's base fee.
|
||||
func enable3198(jt *JumpTable) {
|
||||
|
|
@ -148,7 +159,7 @@ func enable3198(jt *JumpTable) {
|
|||
|
||||
// opBaseFee implements BASEFEE opcode
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||
baseFee, _ := uint256.FromBig(common.MinGasPrice50x)
|
||||
baseFee, _ := uint256.FromBig(common.BaseFee)
|
||||
callContext.Stack.push(baseFee)
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -169,3 +180,10 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext)
|
|||
callContext.Stack.push(new(uint256.Int))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ebnable3860 enables "EIP-3860: Limit and meter initcode"
|
||||
// https://eips.ethereum.org/EIPS/eip-3860
|
||||
func enable3860(jt *JumpTable) {
|
||||
jt[CREATE].dynamicGas = gasCreateEip3860
|
||||
jt[CREATE2].dynamicGas = gasCreate2Eip3860
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ var (
|
|||
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
||||
ErrContractAddressCollision = errors.New("contract address collision")
|
||||
ErrExecutionReverted = errors.New("execution reverted")
|
||||
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
||||
ErrInvalidJump = errors.New("invalid jump destination")
|
||||
ErrWriteProtection = errors.New("write protection")
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ type BlockContext struct {
|
|||
BlockNumber *big.Int // Provides information for NUMBER
|
||||
Time *big.Int // Provides information for TIME
|
||||
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||
BaseFee *big.Int // Provides information for BASEFEE
|
||||
Random *common.Hash // Provides information for PREVRANDAO
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +140,7 @@ type EVM struct {
|
|||
chainRules params.Rules
|
||||
// virtual machine configuration options used to initialise the
|
||||
// evm.
|
||||
vmConfig Config
|
||||
Config Config
|
||||
// global (to this context) ethereum virtual machine
|
||||
// used throughout the execution of the tx.
|
||||
interpreters []Interpreter
|
||||
|
|
@ -155,13 +156,13 @@ type EVM struct {
|
|||
|
||||
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
|
||||
// only ever be used *once*.
|
||||
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
|
||||
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStateDB *tradingstate.TradingStateDB, chainConfig *params.ChainConfig, config Config) *EVM {
|
||||
evm := &EVM{
|
||||
Context: blockCtx,
|
||||
TxContext: txCtx,
|
||||
StateDB: statedb,
|
||||
tradingStateDB: tradingStateDB,
|
||||
vmConfig: vmConfig,
|
||||
Config: config,
|
||||
chainConfig: chainConfig,
|
||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber),
|
||||
interpreters: make([]Interpreter, 0, 1),
|
||||
|
|
@ -169,7 +170,7 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, tradingStat
|
|||
|
||||
// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
|
||||
// as we always want to have the built-in EVM as the failover option.
|
||||
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
|
||||
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, config))
|
||||
evm.interpreter = evm.interpreters[0]
|
||||
|
||||
return evm
|
||||
|
|
@ -217,13 +218,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
if !evm.StateDB.Exist(addr) {
|
||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
||||
// Calling a non existing account, don't do anything, but ping the tracer
|
||||
if evm.vmConfig.Debug {
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
|
||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
||||
evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
|
||||
} else {
|
||||
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
||||
evm.vmConfig.Tracer.CaptureExit(ret, 0, nil)
|
||||
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
||||
evm.Config.Tracer.CaptureExit(ret, 0, nil)
|
||||
}
|
||||
}
|
||||
return nil, gas, nil
|
||||
|
|
@ -233,18 +234,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
|||
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
|
||||
|
||||
// Capture the tracer start/end events in debug mode
|
||||
if evm.vmConfig.Debug {
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
||||
|
||||
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
|
||||
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
|
||||
}(gas, time.Now())
|
||||
} else {
|
||||
// Handle tracer events for entering and exiting a call frame
|
||||
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
||||
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
||||
defer func(startGas uint64) {
|
||||
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
}(gas)
|
||||
}
|
||||
}
|
||||
|
|
@ -304,10 +305,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
|||
var snapshot = evm.StateDB.Snapshot()
|
||||
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.vmConfig.Debug {
|
||||
evm.vmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
|
||||
if evm.Config.Debug {
|
||||
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
|
||||
defer func(startGas uint64) {
|
||||
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
}(gas)
|
||||
}
|
||||
|
||||
|
|
@ -345,10 +346,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
|||
var snapshot = evm.StateDB.Snapshot()
|
||||
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.vmConfig.Debug {
|
||||
evm.vmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
|
||||
if evm.Config.Debug {
|
||||
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
|
||||
defer func(startGas uint64) {
|
||||
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
}(gas)
|
||||
}
|
||||
|
||||
|
|
@ -395,10 +396,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
evm.StateDB.AddBalance(addr, big0)
|
||||
|
||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||
if evm.vmConfig.Debug {
|
||||
evm.vmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
|
||||
if evm.Config.Debug {
|
||||
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
|
||||
defer func(startGas uint64) {
|
||||
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||
}(gas)
|
||||
}
|
||||
|
||||
|
|
@ -479,11 +480,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
contract := NewContract(caller, AccountRef(address), value, gas)
|
||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||
|
||||
if evm.vmConfig.Debug {
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
|
||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
|
||||
} else {
|
||||
evm.vmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
|
||||
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
|
||||
}
|
||||
}
|
||||
start := time.Now()
|
||||
|
|
@ -523,11 +524,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
|||
}
|
||||
}
|
||||
|
||||
if evm.vmConfig.Debug {
|
||||
if evm.Config.Debug {
|
||||
if evm.depth == 0 {
|
||||
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
|
||||
} else {
|
||||
evm.vmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err)
|
||||
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
|
||||
}
|
||||
}
|
||||
return ret, address, contract.Gas, err
|
||||
|
|
|
|||
|
|
@ -300,6 +300,40 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
|||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow || size > params.MaxInitCodeSize {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
||||
moreGas := params.InitCodeWordGas * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size, overflow := stack.Back(2).Uint64WithOverflow()
|
||||
if overflow || size > params.MaxInitCodeSize {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
|
||||
moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
|
||||
if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
|
|
@ -106,3 +108,72 @@ func TestEIP2200(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var createGasTests = []struct {
|
||||
code string
|
||||
eip3860 bool
|
||||
gasUsed uint64
|
||||
minimumGas uint64
|
||||
}{
|
||||
// legacy create(0, 0, 0xc000) without 3860 used
|
||||
{"0x61C00060006000f0" + "600052" + "60206000F3", false, 41237, 41237},
|
||||
// legacy create(0, 0, 0xc000) _with_ 3860
|
||||
{"0x61C00060006000f0" + "600052" + "60206000F3", true, 44309, 44309},
|
||||
// create2(0, 0, 0xc001, 0) without 3860
|
||||
{"0x600061C00160006000f5" + "600052" + "60206000F3", false, 50471, 100_000},
|
||||
// create2(0, 0, 0xc001, 0) (too large), with 3860
|
||||
{"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32012, 100_000},
|
||||
// create2(0, 0, 0xc000, 0)
|
||||
// This case is trying to deploy code at (within) the limit
|
||||
{"0x600061C00060006000f5" + "600052" + "60206000F3", true, 53528, 100_000},
|
||||
// create2(0, 0, 0xc001, 0)
|
||||
// This case is trying to deploy code exceeding the limit
|
||||
{"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32024, 100_000}}
|
||||
|
||||
func TestCreateGas(t *testing.T) {
|
||||
for i, tt := range createGasTests {
|
||||
var gasUsed = uint64(0)
|
||||
doCheck := func(testGas int) bool {
|
||||
address := common.BytesToAddress([]byte("contract"))
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
statedb.CreateAccount(address)
|
||||
statedb.SetCode(address, hexutil.MustDecode(tt.code))
|
||||
statedb.Finalise(true)
|
||||
vmctx := BlockContext{
|
||||
CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
|
||||
Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
|
||||
BlockNumber: big.NewInt(0),
|
||||
}
|
||||
config := Config{}
|
||||
if tt.eip3860 {
|
||||
config.ExtraEips = []int{3860}
|
||||
}
|
||||
|
||||
vmenv := NewEVM(vmctx, TxContext{}, statedb, nil, params.AllEthashProtocolChanges, config)
|
||||
var startGas = uint64(testGas)
|
||||
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
gasUsed = startGas - gas
|
||||
if len(ret) != 32 {
|
||||
t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret))
|
||||
}
|
||||
if bytes.Equal(ret, make([]byte, 32)) {
|
||||
// Failure
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
minGas := sort.Search(100_000, doCheck)
|
||||
if uint64(minGas) != tt.minimumGas {
|
||||
t.Fatalf("test %d: min gas error, want %d, have %d", i, tt.minimumGas, minGas)
|
||||
}
|
||||
// If the deployment succeeded, we also check the gas used
|
||||
if minGas < 100_000 {
|
||||
if gasUsed != tt.gasUsed {
|
||||
t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
|||
interpreter.hasher.Read(interpreter.hasherBuf[:])
|
||||
|
||||
evm := interpreter.evm
|
||||
if evm.vmConfig.EnablePreimageRecording {
|
||||
if evm.Config.EnablePreimageRecording {
|
||||
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
|
||||
}
|
||||
|
||||
|
|
@ -646,7 +646,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
|||
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||
gas = scope.Contract.Gas
|
||||
)
|
||||
|
||||
// Apply EIP150
|
||||
gas -= gas / 64
|
||||
scope.Contract.UseGas(gas)
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ func TestAddMod(t *testing.T) {
|
|||
var (
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
pc = uint64(0)
|
||||
)
|
||||
tests := []struct {
|
||||
|
|
@ -290,7 +290,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
|||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
scope = &ScopeContext{nil, stack, nil}
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
|
|
@ -531,7 +531,7 @@ func TestOpMstore(t *testing.T) {
|
|||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
mem = NewMemory()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
|
|
@ -557,7 +557,7 @@ func BenchmarkOpMstore(bench *testing.B) {
|
|||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
mem = NewMemory()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
|
|
@ -579,7 +579,7 @@ func BenchmarkOpKeccak256(bench *testing.B) {
|
|||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
mem = NewMemory()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
)
|
||||
env.interpreter = evmInterpreter
|
||||
mem.Resize(32)
|
||||
|
|
@ -683,7 +683,7 @@ func TestRandom(t *testing.T) {
|
|||
env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||
)
|
||||
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||
if len(stack.data) != 1 {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
type Config struct {
|
||||
Debug bool // Enables debugging
|
||||
Tracer EVMLogger // Opcode logger
|
||||
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
|
||||
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
||||
|
||||
JumpTable *JumpTable // EVM instruction table, automatically populated if unset
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ func validate(jt JumpTable) JumpTable {
|
|||
func newEip1559InstructionSet() JumpTable {
|
||||
instructionSet := newShanghaiInstructionSet()
|
||||
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
|
||||
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
|
||||
enable3860(&instructionSet) // Limit and meter initcode
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +109,6 @@ func newMergeInstructionSet() JumpTable {
|
|||
// constantinople, istanbul, petersburg, berlin and london instructions.
|
||||
func newLondonInstructionSet() JumpTable {
|
||||
instructionSet := newBerlinInstructionSet()
|
||||
// enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
|
||||
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
|
||||
return validate(instructionSet)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,91 +24,75 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
)
|
||||
|
||||
const (
|
||||
ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST
|
||||
ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST
|
||||
WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST
|
||||
)
|
||||
|
||||
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929"
|
||||
//
|
||||
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
|
||||
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
|
||||
// Additionally, modify the parameters defined in EIP 2200 as follows:
|
||||
//
|
||||
// Parameter Old value New value
|
||||
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
|
||||
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
|
||||
//
|
||||
// The other parameters defined in EIP 2200 are unchanged.
|
||||
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
|
||||
func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
y, x = stack.Back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
current = evm.StateDB.GetState(contract.Address(), slot)
|
||||
cost = uint64(0)
|
||||
)
|
||||
// Check slot presence in the access list
|
||||
if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
cost = ColdSloadCostEIP2929
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
if !addrPresent {
|
||||
// Once we're done with YOLOv2 and schedule this for mainnet, might
|
||||
// be good to remove this panic here, which is just really a
|
||||
// canary to have during testing
|
||||
panic("impossible case: address was not present in access list during sstore op")
|
||||
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// If we fail the minimum gas availability invariant, fail (0)
|
||||
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||
}
|
||||
}
|
||||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||
}
|
||||
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return cost + params.SstoreSetGasEIP2200, nil
|
||||
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||
var (
|
||||
y, x = stack.Back(1), stack.peek()
|
||||
slot = common.Hash(x.Bytes32())
|
||||
current = evm.StateDB.GetState(contract.Address(), slot)
|
||||
cost = uint64(0)
|
||||
)
|
||||
// Check slot presence in the access list
|
||||
if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||
cost = params.ColdSloadCostEIP2929
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
if !addrPresent {
|
||||
// Once we're done with YOLOv2 and schedule this for mainnet, might
|
||||
// be good to remove this panic here, which is just really a
|
||||
// canary to have during testing
|
||||
panic("impossible case: address was not present in access list during sstore op")
|
||||
}
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
value := common.Hash(y.Bytes32())
|
||||
|
||||
if current == value { // noop (1)
|
||||
// EIP 2200 original clause:
|
||||
// return params.SloadGasEIP2200, nil
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
||||
}
|
||||
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
return cost + params.SstoreSetGasEIP2200, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
evm.StateDB.AddRefund(clearingRefund)
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
evm.StateDB.SubRefund(clearingRefund)
|
||||
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
|
||||
evm.StateDB.AddRefund(clearingRefund)
|
||||
}
|
||||
}
|
||||
if original == value {
|
||||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||
// EIP 2200 Original clause:
|
||||
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||||
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
|
||||
} else { // reset to original existing slot (2.2.2.2)
|
||||
// EIP 2200 Original clause:
|
||||
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
|
||||
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
|
||||
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
|
||||
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
|
||||
}
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
||||
return cost + (params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
|
||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
||||
}
|
||||
if original != (common.Hash{}) {
|
||||
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
|
||||
evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
|
||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
||||
}
|
||||
}
|
||||
if original == value {
|
||||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||
// EIP 2200 Original clause:
|
||||
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||||
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - WarmStorageReadCostEIP2929)
|
||||
} else { // reset to original existing slot (2.2.2.2)
|
||||
// EIP 2200 Original clause:
|
||||
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
|
||||
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
|
||||
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
|
||||
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929) - WarmStorageReadCostEIP2929)
|
||||
}
|
||||
}
|
||||
// EIP-2200 original clause:
|
||||
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||
return cost + WarmStorageReadCostEIP2929, nil // dirty update (2.2)
|
||||
}
|
||||
|
||||
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
|
||||
|
|
@ -124,9 +108,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
|
|||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
||||
return ColdSloadCostEIP2929, nil
|
||||
return params.ColdSloadCostEIP2929, nil
|
||||
}
|
||||
return WarmStorageReadCostEIP2929, nil
|
||||
return params.WarmStorageReadCostEIP2929, nil
|
||||
}
|
||||
|
||||
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
|
||||
|
|
@ -146,7 +130,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
|
|||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
var overflow bool
|
||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
||||
if gas, overflow = math.SafeAdd(gas, ColdAccountAccessCostEIP2929-WarmStorageReadCostEIP2929); overflow {
|
||||
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
|
|
@ -168,7 +152,7 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
|
|||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The warm storage read cost is already charged as constantGas
|
||||
return ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929, nil
|
||||
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
|
@ -177,10 +161,15 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
|||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.Back(1).Bytes20())
|
||||
// Check slot presence in the access list
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||||
// the cost to charge for cold access, if any, is Cold - Warm
|
||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
if !warmAccess {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost
|
||||
if !contract.UseGas(ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929) {
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(coldCost) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
|
@ -189,7 +178,16 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
|||
// - transfer value
|
||||
// - memory expansion
|
||||
// - 63/64ths rule
|
||||
return oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
if warmAccess || err != nil {
|
||||
return gas, err
|
||||
}
|
||||
// In case of a cold access, we temporarily add the cold charge back, and also
|
||||
// add it to the returned gas. By adding it to the return, it will be charged
|
||||
// outside of this function, as part of the dynamic gas, and that will make it
|
||||
// also become correctly reported to tracers.
|
||||
contract.Gas += coldCost
|
||||
return gas + coldCost, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -198,24 +196,49 @@ var (
|
|||
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
|
||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
|
||||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
||||
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
|
||||
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
|
||||
|
||||
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
|
||||
//
|
||||
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
|
||||
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
|
||||
// Additionally, modify the parameters defined in EIP 2200 as follows:
|
||||
//
|
||||
// Parameter Old value New value
|
||||
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
|
||||
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
|
||||
//
|
||||
//The other parameters defined in EIP 2200 are unchanged.
|
||||
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
|
||||
gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)
|
||||
|
||||
// gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
|
||||
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
|
||||
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
|
||||
)
|
||||
|
||||
func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
gas uint64
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(address)
|
||||
gas = ColdAccountAccessCostEIP2929
|
||||
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539
|
||||
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
gas uint64
|
||||
address = common.Address(stack.peek().Bytes20())
|
||||
)
|
||||
if !evm.StateDB.AddressInAccessList(address) {
|
||||
// If the caller cannot afford the cost, this change will be rolled back
|
||||
evm.StateDB.AddAddressToAccessList(address)
|
||||
gas = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
gas += params.CreateBySelfdestructGas
|
||||
}
|
||||
if refundsEnabled && !evm.StateDB.HasSuicided(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
// if empty and transfers value
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
|
||||
gas += params.CreateBySelfdestructGas
|
||||
}
|
||||
if !evm.StateDB.HasSuicided(contract.Address()) {
|
||||
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
|
||||
}
|
||||
return gas, nil
|
||||
return gasFunc
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ func NewEnv(cfg *Config) *vm.EVM {
|
|||
Time: cfg.Time,
|
||||
Difficulty: cfg.Difficulty,
|
||||
GasLimit: cfg.GasLimit,
|
||||
BaseFee: cfg.BaseFee,
|
||||
}
|
||||
|
||||
return vm.NewEVM(blockContext, txContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ type Config struct {
|
|||
Value *big.Int
|
||||
Debug bool
|
||||
EVMConfig vm.Config
|
||||
BaseFee *big.Int
|
||||
|
||||
State *state.StateDB
|
||||
GetHashFn func(n uint64) common.Hash
|
||||
|
|
@ -68,6 +69,7 @@ func setDefaults(cfg *Config) {
|
|||
LondonBlock: new(big.Int),
|
||||
MergeBlock: new(big.Int),
|
||||
ShanghaiBlock: new(big.Int),
|
||||
Eip1559Block: new(big.Int),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +96,9 @@ func setDefaults(cfg *Config) {
|
|||
return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String())))
|
||||
}
|
||||
}
|
||||
if cfg.BaseFee == nil {
|
||||
cfg.BaseFee = big.NewInt(params.InitialBaseFee)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the code using the input as call data during the execution.
|
||||
|
|
|
|||
|
|
@ -597,3 +597,83 @@ func TestEip2929Cases(t *testing.T) {
|
|||
"account (cheap)", code)
|
||||
}
|
||||
}
|
||||
|
||||
// TestColdAccountAccessCost test that the cold account access cost is reported
|
||||
// correctly
|
||||
// see: https://github.com/ethereum/go-ethereum/issues/22649
|
||||
func TestColdAccountAccessCost(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
code []byte
|
||||
step int
|
||||
want uint64
|
||||
}{
|
||||
{ // EXTCODEHASH(0xff)
|
||||
code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.EXTCODEHASH), byte(vm.POP)},
|
||||
step: 1,
|
||||
want: 2600,
|
||||
},
|
||||
{ // BALANCE(0xff)
|
||||
code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.BALANCE), byte(vm.POP)},
|
||||
step: 1,
|
||||
want: 2600,
|
||||
},
|
||||
{ // CALL(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0x0,
|
||||
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP),
|
||||
},
|
||||
step: 7,
|
||||
want: 2855,
|
||||
},
|
||||
{ // CALLCODE(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0x0,
|
||||
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP),
|
||||
},
|
||||
step: 7,
|
||||
want: 2855,
|
||||
},
|
||||
{ // DELEGATECALL(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0x0,
|
||||
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP),
|
||||
},
|
||||
step: 6,
|
||||
want: 2855,
|
||||
},
|
||||
{ // STATICCALL(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0x0,
|
||||
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP),
|
||||
},
|
||||
step: 6,
|
||||
want: 2855,
|
||||
},
|
||||
{ // SELFDESTRUCT(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT),
|
||||
},
|
||||
step: 1,
|
||||
want: 7600,
|
||||
},
|
||||
} {
|
||||
tracer := vm.NewStructLogger(nil)
|
||||
Execute(tc.code, nil, &Config{
|
||||
EVMConfig: vm.Config{
|
||||
Debug: true,
|
||||
Tracer: tracer,
|
||||
},
|
||||
})
|
||||
have := tracer.StructLogs()[tc.step].GasCost
|
||||
if want := tc.want; have != want {
|
||||
for ii, op := range tracer.StructLogs() {
|
||||
t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost)
|
||||
}
|
||||
t.Fatalf("tescase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*t
|
|||
}
|
||||
|
||||
func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
|
||||
return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil
|
||||
return b.eth.blockchain.GetReceiptsByHash(blockHash), nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||
|
|
@ -298,10 +298,7 @@ func (b *EthApiBackend) SendLendingTx(ctx context.Context, signedTx *types.Lendi
|
|||
}
|
||||
|
||||
func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) {
|
||||
pending, err := b.eth.txPool.Pending()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pending := b.eth.txPool.Pending(false)
|
||||
var txs types.Transactions
|
||||
for _, batch := range pending {
|
||||
txs = append(txs, batch...)
|
||||
|
|
@ -325,6 +322,10 @@ func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions,
|
|||
return b.eth.TxPool().Content()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
|
||||
return b.eth.TxPool().ContentFrom(addr)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) OrderTxPoolContent() (map[common.Address]types.OrderTransactions, map[common.Address]types.OrderTransactions) {
|
||||
return b.eth.OrderPool().Content()
|
||||
}
|
||||
|
|
@ -344,8 +345,12 @@ func (b *EthApiBackend) ProtocolVersion() int {
|
|||
return b.eth.EthVersion()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
|
||||
return b.gpo.SuggestPrice(ctx)
|
||||
func (b *EthApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||
return b.gpo.SuggestTipCap(ctx)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (firstBlock *big.Int, reward [][]*big.Int, baseFee []*big.Int, gasUsedRatio []float64, err error) {
|
||||
return b.gpo.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) ChainDb() ethdb.Database {
|
||||
|
|
@ -393,6 +398,10 @@ func (b *EthApiBackend) GetEngine() consensus.Engine {
|
|||
return b.eth.engine
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) CurrentHeader() *types.Header {
|
||||
return b.eth.blockchain.CurrentHeader()
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) {
|
||||
return b.eth.stateAtBlock(block, reexec, base, checkLive)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
|
|
@ -236,13 +235,14 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
|||
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
|
||||
// Trace all the transactions contained within
|
||||
for i, tx := range task.block.Transactions() {
|
||||
var balacne *big.Int
|
||||
var balance *big.Int
|
||||
if tx.To() != nil {
|
||||
if value, ok := feeCapacity[*tx.To()]; ok {
|
||||
balacne = value
|
||||
balance = value
|
||||
}
|
||||
}
|
||||
msg, _ := tx.AsMessage(signer, balacne, task.block.Number())
|
||||
header := task.block.Header()
|
||||
msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee)
|
||||
txctx := &tracers.Context{
|
||||
BlockHash: task.block.Hash(),
|
||||
TxIndex: i,
|
||||
|
|
@ -480,13 +480,14 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
|||
// Fetch and execute the next transaction trace tasks
|
||||
for task := range jobs {
|
||||
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
|
||||
var balacne *big.Int
|
||||
var balance *big.Int
|
||||
if txs[task.index].To() != nil {
|
||||
if value, ok := feeCapacity[*txs[task.index].To()]; ok {
|
||||
balacne = value
|
||||
balance = value
|
||||
}
|
||||
}
|
||||
msg, _ := txs[task.index].AsMessage(signer, balacne, block.Number())
|
||||
header := block.Header()
|
||||
msg, _ := txs[task.index].AsMessage(signer, balance, header.Number, header.BaseFee)
|
||||
txctx := &tracers.Context{
|
||||
BlockHash: blockHash,
|
||||
TxIndex: task.index,
|
||||
|
|
@ -507,18 +508,19 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
|||
for i, tx := range txs {
|
||||
// Send the trace task over for execution
|
||||
jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
|
||||
var balacne *big.Int
|
||||
var balance *big.Int
|
||||
if tx.To() != nil {
|
||||
// Bypass the validation for trading and lending transactions as their nonce are not incremented
|
||||
if tx.IsSkipNonceTransaction() {
|
||||
continue
|
||||
}
|
||||
if value, ok := feeCapacity[*tx.To()]; ok {
|
||||
balacne = value
|
||||
balance = value
|
||||
}
|
||||
}
|
||||
// Generate the next state snapshot fast without tracing
|
||||
msg, _ := tx.AsMessage(signer, balacne, block.Number())
|
||||
header := block.Header()
|
||||
msg, _ := tx.AsMessage(signer, balance, header.Number, header.BaseFee)
|
||||
txContext := core.NewEVMTxContext(msg)
|
||||
statedb.Prepare(tx.Hash(), i)
|
||||
|
||||
|
|
@ -689,7 +691,12 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti
|
|||
}
|
||||
}
|
||||
// Execute the trace
|
||||
msg := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap())
|
||||
// TODO: replace block.BaseFee() with vmctx.BaseFee
|
||||
// reference: https://github.com/ethereum/go-ethereum/pull/29051
|
||||
msg, err := args.ToMessage(api.eth.ApiBackend, block.Number(), api.eth.ApiBackend.RPCGasCap(), block.BaseFee())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vmctx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil)
|
||||
var traceConfig *TraceConfig
|
||||
if config != nil {
|
||||
|
|
@ -737,7 +744,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t
|
|||
tracer = vm.NewStructLogger(config.LogConfig)
|
||||
}
|
||||
// Run the transaction with tracing enabled.
|
||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer})
|
||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer, NoBaseFee: true})
|
||||
|
||||
// Call Prepare to clear out the statedb access list
|
||||
statedb.Prepare(txctx.TxHash, txctx.TxIndex)
|
||||
|
|
@ -800,7 +807,8 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
|
|||
balanceFee = value
|
||||
}
|
||||
}
|
||||
msg, err := tx.AsMessage(types.MakeSigner(api.config, block.Header().Number), balanceFee, block.Number())
|
||||
header := block.Header()
|
||||
msg, err := tx.AsMessage(types.MakeSigner(api.config, header.Number), balanceFee, header.Number, header.BaseFee)
|
||||
if err != nil {
|
||||
return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/contracts"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||
|
|
@ -72,9 +73,9 @@ type Ethereum struct {
|
|||
shutdownChan chan bool // Channel for shutting down the ethereum
|
||||
|
||||
// Handlers
|
||||
txPool *core.TxPool
|
||||
orderPool *core.OrderPool
|
||||
lendingPool *core.LendingPool
|
||||
txPool *txpool.TxPool
|
||||
orderPool *txpool.OrderPool
|
||||
lendingPool *txpool.LendingPool
|
||||
blockchain *core.BlockChain
|
||||
protocolManager *ProtocolManager
|
||||
lesServer LesServer
|
||||
|
|
@ -186,9 +187,9 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
|
|||
if config.TxPool.Journal != "" {
|
||||
config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
|
||||
}
|
||||
eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
|
||||
eth.orderPool = core.NewOrderPool(eth.chainConfig, eth.blockchain)
|
||||
eth.lendingPool = core.NewLendingPool(eth.chainConfig, eth.blockchain)
|
||||
eth.txPool = txpool.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
|
||||
eth.orderPool = txpool.NewOrderPool(eth.chainConfig, eth.blockchain)
|
||||
eth.lendingPool = txpool.NewLendingPool(eth.chainConfig, eth.blockchain)
|
||||
if common.RollbackHash != (common.Hash{}) {
|
||||
curBlock := eth.blockchain.CurrentBlock()
|
||||
if curBlock == nil {
|
||||
|
|
@ -517,7 +518,7 @@ func (e *Ethereum) Miner() *miner.Miner { return e.miner }
|
|||
|
||||
func (e *Ethereum) AccountManager() *accounts.Manager { return e.accountManager }
|
||||
func (e *Ethereum) BlockChain() *core.BlockChain { return e.blockchain }
|
||||
func (e *Ethereum) TxPool() *core.TxPool { return e.txPool }
|
||||
func (e *Ethereum) TxPool() *txpool.TxPool { return e.txPool }
|
||||
func (e *Ethereum) EventMux() *event.TypeMux { return e.eventMux }
|
||||
func (e *Ethereum) Engine() consensus.Engine { return e.engine }
|
||||
func (e *Ethereum) ChainDb() ethdb.Database { return e.chainDb }
|
||||
|
|
@ -591,7 +592,7 @@ func (e *Ethereum) GetXDCX() *XDCx.XDCX {
|
|||
return e.XDCX
|
||||
}
|
||||
|
||||
func (e *Ethereum) OrderPool() *core.OrderPool {
|
||||
func (e *Ethereum) OrderPool() *txpool.OrderPool {
|
||||
return e.orderPool
|
||||
}
|
||||
|
||||
|
|
@ -600,6 +601,6 @@ func (e *Ethereum) GetXDCXLending() *XDCxlending.Lending {
|
|||
}
|
||||
|
||||
// LendingPool geth eth lending pool
|
||||
func (e *Ethereum) LendingPool() *core.LendingPool {
|
||||
func (e *Ethereum) LendingPool() *txpool.LendingPool {
|
||||
return e.lendingPool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package downloader
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
|
@ -78,7 +79,7 @@ type downloadTester struct {
|
|||
// newTester creates a new downloader test mocker.
|
||||
func newTester() *downloadTester {
|
||||
testdb := rawdb.NewMemoryDatabase()
|
||||
genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
|
||||
genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(math.MaxInt64))
|
||||
|
||||
tester := &downloadTester{
|
||||
genesis: genesis,
|
||||
|
|
@ -109,7 +110,8 @@ func newTester() *downloadTester {
|
|||
// reassembly.
|
||||
func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) {
|
||||
// Generate the block chain
|
||||
blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), dl.peerDb, n, func(i int, block *core.BlockGen) {
|
||||
config := dl.Config()
|
||||
blocks, receipts := core.GenerateChain(config, parent, ethash.NewFaker(), dl.peerDb, n, func(i int, block *core.BlockGen) {
|
||||
block.SetCoinbase(common.Address{seed})
|
||||
|
||||
// If a heavy chain is requested, delay blocks to raise difficulty
|
||||
|
|
@ -118,8 +120,8 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren
|
|||
}
|
||||
// If the block number is multiple of 3, send a bonus transaction to the miner
|
||||
if parent == dl.genesis && i%3 == 0 {
|
||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
|
||||
signer := types.MakeSigner(config, block.Number())
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -464,7 +466,11 @@ func (dl *downloadTester) handleProposedBlock(header *types.Header) error {
|
|||
}
|
||||
|
||||
// Config retrieves the blockchain's chain configuration.
|
||||
func (dl *downloadTester) Config() *params.ChainConfig { return params.TestChainConfig }
|
||||
func (dl *downloadTester) Config() *params.ChainConfig {
|
||||
config := *params.TestChainConfig
|
||||
config.Eip1559Block = big.NewInt((0))
|
||||
return &config
|
||||
}
|
||||
|
||||
type downloadTesterPeer struct {
|
||||
dl *downloadTester
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
|
|
@ -36,18 +37,22 @@ import (
|
|||
|
||||
// FullNodeGPO contains default gasprice oracle settings for full node.
|
||||
var FullNodeGPO = gasprice.Config{
|
||||
Blocks: 20,
|
||||
Percentile: 60,
|
||||
MaxPrice: gasprice.DefaultMaxPrice,
|
||||
IgnorePrice: gasprice.DefaultIgnorePrice,
|
||||
Blocks: 20,
|
||||
Percentile: 60,
|
||||
MaxHeaderHistory: 1024,
|
||||
MaxBlockHistory: 1024,
|
||||
MaxPrice: gasprice.DefaultMaxPrice,
|
||||
IgnorePrice: gasprice.DefaultIgnorePrice,
|
||||
}
|
||||
|
||||
// LightClientGPO contains default gasprice oracle settings for light client.
|
||||
var LightClientGPO = gasprice.Config{
|
||||
Blocks: 2,
|
||||
Percentile: 60,
|
||||
MaxPrice: gasprice.DefaultMaxPrice,
|
||||
IgnorePrice: gasprice.DefaultIgnorePrice,
|
||||
Blocks: 2,
|
||||
Percentile: 60,
|
||||
MaxHeaderHistory: 300,
|
||||
MaxBlockHistory: 5,
|
||||
MaxPrice: gasprice.DefaultMaxPrice,
|
||||
IgnorePrice: gasprice.DefaultIgnorePrice,
|
||||
}
|
||||
|
||||
// Defaults contains default settings for use on the Ethereum main net.
|
||||
|
|
@ -68,7 +73,7 @@ var Defaults = Config{
|
|||
FilterLogCacheSize: 32,
|
||||
GasPrice: big.NewInt(0.25 * params.Shannon),
|
||||
|
||||
TxPool: core.DefaultTxPoolConfig,
|
||||
TxPool: txpool.DefaultConfig,
|
||||
RPCGasCap: 50000000,
|
||||
GPO: FullNodeGPO,
|
||||
RPCTxFeeCap: 1, // 1 ether
|
||||
|
|
@ -125,7 +130,7 @@ type Config struct {
|
|||
Ethash ethash.Config
|
||||
|
||||
// Transaction pool options
|
||||
TxPool core.TxPoolConfig
|
||||
TxPool txpool.Config
|
||||
|
||||
// Gas Price Oracle options
|
||||
GPO gasprice.Config
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
|
||||
)
|
||||
|
|
@ -33,7 +34,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||
GasPrice *big.Int
|
||||
FilterLogCacheSize int
|
||||
Ethash ethash.Config
|
||||
TxPool core.TxPoolConfig
|
||||
TxPool txpool.Config
|
||||
GPO gasprice.Config
|
||||
EnablePreimageRecording bool
|
||||
DocRoot string `toml:"-"`
|
||||
|
|
@ -87,7 +88,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||
GasPrice *big.Int
|
||||
FilterLogCacheSize *int
|
||||
Ethash *ethash.Config
|
||||
TxPool *core.TxPoolConfig
|
||||
TxPool *txpool.Config
|
||||
GPO *gasprice.Config
|
||||
EnablePreimageRecording *bool
|
||||
DocRoot *string `toml:"-"`
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ func TestBlockSubscription(t *testing.T) {
|
|||
db = rawdb.NewMemoryDatabase()
|
||||
backend, sys = newTestFilterSystem(t, db, Config{})
|
||||
api = NewFilterAPI(sys, false)
|
||||
genesis = new(core.Genesis).MustCommit(db)
|
||||
genesis = (&core.Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
|
||||
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {})
|
||||
chainEvents = []core.ChainEvent{}
|
||||
)
|
||||
|
|
|
|||
333
eth/gasprice/feehistory.go
Normal file
333
eth/gasprice/feehistory.go
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package gasprice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidPercentile = errors.New("invalid reward percentile")
|
||||
errRequestBeyondHead = errors.New("request beyond head block")
|
||||
)
|
||||
|
||||
const (
|
||||
// maxBlockFetchers is the max number of goroutines to spin up to pull blocks
|
||||
// for the fee history calculation (mostly relevant for LES).
|
||||
maxBlockFetchers = 4
|
||||
)
|
||||
|
||||
// blockFees represents a single block for processing
|
||||
type blockFees struct {
|
||||
// set by the caller
|
||||
blockNumber uint64
|
||||
header *types.Header
|
||||
block *types.Block // only set if reward percentiles are requested
|
||||
receipts types.Receipts
|
||||
// filled by processBlock
|
||||
results processedFees
|
||||
err error
|
||||
}
|
||||
|
||||
// processedFees contains the results of a processed block and is also used for caching
|
||||
type processedFees struct {
|
||||
reward []*big.Int
|
||||
baseFee, nextBaseFee *big.Int
|
||||
gasUsedRatio float64
|
||||
}
|
||||
|
||||
// txGasAndReward is sorted in ascending order based on reward
|
||||
type (
|
||||
txGasAndReward struct {
|
||||
gasUsed uint64
|
||||
reward *big.Int
|
||||
}
|
||||
sortGasAndReward []txGasAndReward
|
||||
)
|
||||
|
||||
func (s sortGasAndReward) Len() int { return len(s) }
|
||||
func (s sortGasAndReward) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s sortGasAndReward) Less(i, j int) bool {
|
||||
return s[i].reward.Cmp(s[j].reward) < 0
|
||||
}
|
||||
|
||||
// processBlock takes a blockFees structure with the blockNumber, the header and optionally
|
||||
// the block field filled in, retrieves the block from the backend if not present yet and
|
||||
// fills in the rest of the fields.
|
||||
func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
|
||||
chainconfig := oracle.backend.ChainConfig()
|
||||
if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil {
|
||||
bf.results.baseFee = new(big.Int)
|
||||
}
|
||||
if chainconfig.IsEIP1559(big.NewInt(int64(bf.blockNumber + 1))) {
|
||||
bf.results.nextBaseFee = eip1559.CalcBaseFee(chainconfig, bf.header)
|
||||
} else {
|
||||
bf.results.nextBaseFee = new(big.Int)
|
||||
}
|
||||
bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
|
||||
if len(percentiles) == 0 {
|
||||
// rewards were not requested, return null
|
||||
return
|
||||
}
|
||||
if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) {
|
||||
log.Error("Block or receipts are missing while reward percentiles are requested")
|
||||
return
|
||||
}
|
||||
|
||||
bf.results.reward = make([]*big.Int, len(percentiles))
|
||||
if len(bf.block.Transactions()) == 0 {
|
||||
// return an all zero row if there are no transactions to gather data from
|
||||
for i := range bf.results.reward {
|
||||
bf.results.reward[i] = new(big.Int)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
sorter := make(sortGasAndReward, len(bf.block.Transactions()))
|
||||
for i, tx := range bf.block.Transactions() {
|
||||
reward, _ := tx.EffectiveGasTip(bf.block.BaseFee())
|
||||
sorter[i] = txGasAndReward{gasUsed: bf.receipts[i].GasUsed, reward: reward}
|
||||
}
|
||||
sort.Stable(sorter)
|
||||
|
||||
var txIndex int
|
||||
sumGasUsed := sorter[0].gasUsed
|
||||
|
||||
for i, p := range percentiles {
|
||||
thresholdGasUsed := uint64(float64(bf.block.GasUsed()) * p / 100)
|
||||
for sumGasUsed < thresholdGasUsed && txIndex < len(bf.block.Transactions())-1 {
|
||||
txIndex++
|
||||
sumGasUsed += sorter[txIndex].gasUsed
|
||||
}
|
||||
bf.results.reward[i] = sorter[txIndex].reward
|
||||
}
|
||||
}
|
||||
|
||||
// resolveBlockRange resolves the specified block range to absolute block numbers while also
|
||||
// enforcing backend specific limitations. The pending block and corresponding receipts are
|
||||
// also returned if requested and available.
|
||||
// Note: an error is only returned if retrieving the head header has failed. If there are no
|
||||
// retrievable blocks in the specified range then zero block count is returned with no error.
|
||||
func (oracle *Oracle) resolveBlockRange(ctx context.Context, reqEnd rpc.BlockNumber, blocks uint64) (*types.Block, []*types.Receipt, uint64, uint64, error) {
|
||||
var (
|
||||
headBlock *types.Header
|
||||
pendingBlock *types.Block
|
||||
pendingReceipts types.Receipts
|
||||
err error
|
||||
)
|
||||
|
||||
// Get the chain's current head.
|
||||
if headBlock, err = oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber); err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
head := rpc.BlockNumber(headBlock.Number.Uint64())
|
||||
|
||||
// Fail if request block is beyond the chain's current head.
|
||||
if head < reqEnd {
|
||||
return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, reqEnd, head)
|
||||
}
|
||||
|
||||
// Resolve block tag.
|
||||
if reqEnd < 0 {
|
||||
var (
|
||||
resolved *types.Header
|
||||
err error
|
||||
)
|
||||
switch reqEnd {
|
||||
case rpc.PendingBlockNumber:
|
||||
if pendingBlock, pendingReceipts = oracle.backend.PendingBlockAndReceipts(); pendingBlock != nil {
|
||||
resolved = pendingBlock.Header()
|
||||
} else {
|
||||
// Pending block not supported by backend, process only until latest block.
|
||||
resolved = headBlock
|
||||
|
||||
// Update total blocks to return to account for this.
|
||||
blocks--
|
||||
}
|
||||
case rpc.LatestBlockNumber:
|
||||
// Retrieved above.
|
||||
resolved = headBlock
|
||||
case rpc.CommittedBlockNumber:
|
||||
resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.CommittedBlockNumber)
|
||||
case rpc.EarliestBlockNumber:
|
||||
resolved, err = oracle.backend.HeaderByNumber(ctx, rpc.EarliestBlockNumber)
|
||||
}
|
||||
if resolved == nil || err != nil {
|
||||
return nil, nil, 0, 0, err
|
||||
}
|
||||
// Absolute number resolved.
|
||||
reqEnd = rpc.BlockNumber(resolved.Number.Uint64())
|
||||
}
|
||||
|
||||
// If there are no blocks to return, short circuit.
|
||||
if blocks == 0 {
|
||||
return nil, nil, 0, 0, nil
|
||||
}
|
||||
// Ensure not trying to retrieve before genesis.
|
||||
if uint64(reqEnd+1) < blocks {
|
||||
blocks = uint64(reqEnd + 1)
|
||||
}
|
||||
return pendingBlock, pendingReceipts, uint64(reqEnd), blocks, nil
|
||||
}
|
||||
|
||||
// FeeHistory returns data relevant for fee estimation based on the specified range of blocks.
|
||||
// The range can be specified either with absolute block numbers or ending with the latest
|
||||
// or pending block. Backends may or may not support gathering data from the pending block
|
||||
// or blocks older than a certain age (specified in maxHistory). The first block of the
|
||||
// actually processed range is returned to avoid ambiguity when parts of the requested range
|
||||
// are not available or when the head has changed during processing this request.
|
||||
// Three arrays are returned based on the processed blocks:
|
||||
// - reward: the requested percentiles of effective priority fees per gas of transactions in each
|
||||
// block, sorted in ascending order and weighted by gas used.
|
||||
// - baseFee: base fee per gas in the given block
|
||||
// - gasUsedRatio: gasUsed/gasLimit in the given block
|
||||
//
|
||||
// Note: baseFee includes the next block after the newest of the returned range, because this
|
||||
// value can be derived from the newest block.
|
||||
func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
|
||||
if blocks < 1 {
|
||||
return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
|
||||
}
|
||||
maxFeeHistory := oracle.maxHeaderHistory
|
||||
if len(rewardPercentiles) != 0 {
|
||||
maxFeeHistory = oracle.maxBlockHistory
|
||||
}
|
||||
if blocks > maxFeeHistory {
|
||||
log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
|
||||
blocks = maxFeeHistory
|
||||
}
|
||||
for i, p := range rewardPercentiles {
|
||||
if p < 0 || p > 100 {
|
||||
return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
|
||||
}
|
||||
if i > 0 && p <= rewardPercentiles[i-1] {
|
||||
return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f >= #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
|
||||
}
|
||||
}
|
||||
var (
|
||||
pendingBlock *types.Block
|
||||
pendingReceipts []*types.Receipt
|
||||
err error
|
||||
)
|
||||
pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks)
|
||||
if err != nil || blocks == 0 {
|
||||
return common.Big0, nil, nil, nil, err
|
||||
}
|
||||
oldestBlock := lastBlock + 1 - blocks
|
||||
|
||||
var (
|
||||
next = oldestBlock
|
||||
results = make(chan *blockFees, blocks)
|
||||
)
|
||||
percentileKey := make([]byte, 8*len(rewardPercentiles))
|
||||
for i, p := range rewardPercentiles {
|
||||
binary.LittleEndian.PutUint64(percentileKey[i*8:(i+1)*8], math.Float64bits(p))
|
||||
}
|
||||
for i := 0; i < maxBlockFetchers && i < int(blocks); i++ {
|
||||
go func() {
|
||||
for {
|
||||
// Retrieve the next block number to fetch with this goroutine
|
||||
blockNumber := atomic.AddUint64(&next, 1) - 1
|
||||
if blockNumber > lastBlock {
|
||||
return
|
||||
}
|
||||
|
||||
fees := &blockFees{blockNumber: blockNumber}
|
||||
if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() {
|
||||
fees.block, fees.receipts = pendingBlock, pendingReceipts
|
||||
fees.header = fees.block.Header()
|
||||
oracle.processBlock(fees, rewardPercentiles)
|
||||
results <- fees
|
||||
} else {
|
||||
cacheKey := struct {
|
||||
number uint64
|
||||
percentiles string
|
||||
}{blockNumber, string(percentileKey)}
|
||||
|
||||
if p, ok := oracle.historyCache.Get(cacheKey); ok {
|
||||
fees.results = p.(processedFees)
|
||||
results <- fees
|
||||
} else {
|
||||
if len(rewardPercentiles) != 0 {
|
||||
fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber))
|
||||
if fees.block != nil && fees.err == nil {
|
||||
fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash())
|
||||
fees.header = fees.block.Header()
|
||||
}
|
||||
} else {
|
||||
fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber))
|
||||
}
|
||||
if fees.header != nil && fees.err == nil {
|
||||
oracle.processBlock(fees, rewardPercentiles)
|
||||
if fees.err == nil {
|
||||
oracle.historyCache.Add(cacheKey, fees.results)
|
||||
}
|
||||
}
|
||||
// send to results even if empty to guarantee that blocks items are sent in total
|
||||
results <- fees
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
var (
|
||||
reward = make([][]*big.Int, blocks)
|
||||
baseFee = make([]*big.Int, blocks+1)
|
||||
gasUsedRatio = make([]float64, blocks)
|
||||
firstMissing = blocks
|
||||
)
|
||||
for ; blocks > 0; blocks-- {
|
||||
fees := <-results
|
||||
if fees.err != nil {
|
||||
return common.Big0, nil, nil, nil, fees.err
|
||||
}
|
||||
i := fees.blockNumber - oldestBlock
|
||||
if fees.results.baseFee != nil {
|
||||
reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.results.reward, fees.results.baseFee, fees.results.nextBaseFee, fees.results.gasUsedRatio
|
||||
} else {
|
||||
// getting no block and no error means we are requesting into the future (might happen because of a reorg)
|
||||
if i < firstMissing {
|
||||
firstMissing = i
|
||||
}
|
||||
}
|
||||
}
|
||||
if firstMissing == 0 {
|
||||
return common.Big0, nil, nil, nil, nil
|
||||
}
|
||||
if len(rewardPercentiles) != 0 {
|
||||
reward = reward[:firstMissing]
|
||||
} else {
|
||||
reward = nil
|
||||
}
|
||||
baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing]
|
||||
return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, nil
|
||||
}
|
||||
90
eth/gasprice/feehistory_test.go
Normal file
90
eth/gasprice/feehistory_test.go
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package gasprice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
)
|
||||
|
||||
func TestFeeHistory(t *testing.T) {
|
||||
var cases = []struct {
|
||||
pending bool
|
||||
maxHeader, maxBlock uint64
|
||||
count uint64
|
||||
last rpc.BlockNumber
|
||||
percent []float64
|
||||
expFirst uint64
|
||||
expCount int
|
||||
expErr error
|
||||
}{
|
||||
{false, 1000, 1000, 10, 30, nil, 21, 10, nil},
|
||||
{false, 1000, 1000, 10, 30, []float64{0, 10}, 21, 10, nil},
|
||||
{false, 1000, 1000, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile},
|
||||
{false, 1000, 1000, 1000000000, 30, nil, 0, 31, nil},
|
||||
{false, 1000, 1000, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil},
|
||||
{false, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead},
|
||||
{true, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead},
|
||||
{false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil},
|
||||
{false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil},
|
||||
{false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil},
|
||||
{false, 1000, 1000, 1, rpc.PendingBlockNumber, nil, 0, 0, nil},
|
||||
{false, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 1, nil},
|
||||
{true, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 2, nil},
|
||||
{true, 1000, 1000, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil},
|
||||
{false, 1000, 1000, 2, rpc.CommittedBlockNumber, []float64{0, 10}, 32, 1, nil},
|
||||
}
|
||||
for i, c := range cases {
|
||||
config := Config{
|
||||
MaxHeaderHistory: c.maxHeader,
|
||||
MaxBlockHistory: c.maxBlock,
|
||||
}
|
||||
backend := newTestBackend(t, big.NewInt(16), c.pending)
|
||||
oracle := NewOracle(backend, config)
|
||||
|
||||
first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
|
||||
|
||||
expReward := c.expCount
|
||||
if len(c.percent) == 0 {
|
||||
expReward = 0
|
||||
}
|
||||
expBaseFee := c.expCount
|
||||
if expBaseFee != 0 {
|
||||
expBaseFee++
|
||||
}
|
||||
|
||||
if first.Uint64() != c.expFirst {
|
||||
t.Fatalf("Test case %d: first block mismatch, want %d, got %d", i, c.expFirst, first)
|
||||
}
|
||||
if len(reward) != expReward {
|
||||
t.Fatalf("Test case %d: reward array length mismatch, want %d, got %d", i, expReward, len(reward))
|
||||
}
|
||||
if len(baseFee) != expBaseFee {
|
||||
t.Fatalf("Test case %d: baseFee array length mismatch, want %d, got %d", i, expBaseFee, len(baseFee))
|
||||
}
|
||||
if len(ratio) != c.expCount {
|
||||
t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio))
|
||||
}
|
||||
if err != c.expErr && !errors.Is(err, c.expErr) {
|
||||
t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,30 +23,40 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
const sampleNumber = 3 // Number of transactions sampled in a block
|
||||
|
||||
var DefaultMaxPrice = big.NewInt(500 * params.GWei)
|
||||
var DefaultIgnorePrice = big.NewInt(2 * params.Wei)
|
||||
var (
|
||||
DefaultMaxPrice = big.NewInt(500 * params.GWei)
|
||||
DefaultIgnorePrice = big.NewInt(2 * params.Wei)
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Blocks int
|
||||
Percentile int
|
||||
Default *big.Int `toml:",omitempty"`
|
||||
MaxPrice *big.Int `toml:",omitempty"`
|
||||
IgnorePrice *big.Int `toml:",omitempty"`
|
||||
Blocks int
|
||||
Percentile int
|
||||
MaxHeaderHistory uint64
|
||||
MaxBlockHistory uint64
|
||||
Default *big.Int `toml:",omitempty"`
|
||||
MaxPrice *big.Int `toml:",omitempty"`
|
||||
IgnorePrice *big.Int `toml:",omitempty"`
|
||||
}
|
||||
|
||||
// OracleBackend includes all necessary background APIs for oracle.
|
||||
type OracleBackend interface {
|
||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
||||
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
||||
PendingBlockAndReceipts() (*types.Block, types.Receipts)
|
||||
ChainConfig() *params.ChainConfig
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
}
|
||||
|
||||
// Oracle recommends gas prices based on the content of recent
|
||||
|
|
@ -60,8 +70,9 @@ type Oracle struct {
|
|||
cacheLock sync.RWMutex
|
||||
fetchLock sync.Mutex
|
||||
|
||||
checkBlocks int
|
||||
percentile int
|
||||
checkBlocks, percentile int
|
||||
maxHeaderHistory, maxBlockHistory uint64
|
||||
historyCache *lru.Cache
|
||||
}
|
||||
|
||||
// NewOracle returns a new gasprice oracle which can recommend suitable
|
||||
|
|
@ -93,47 +104,69 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
|
|||
} else if ignorePrice.Int64() > 0 {
|
||||
log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
|
||||
}
|
||||
|
||||
cache, _ := lru.New(2048)
|
||||
headEvent := make(chan core.ChainHeadEvent, 1)
|
||||
backend.SubscribeChainHeadEvent(headEvent)
|
||||
go func() {
|
||||
var lastHead common.Hash
|
||||
for ev := range headEvent {
|
||||
if ev.Block.ParentHash() != lastHead {
|
||||
cache.Purge()
|
||||
}
|
||||
lastHead = ev.Block.Hash()
|
||||
}
|
||||
}()
|
||||
|
||||
return &Oracle{
|
||||
backend: backend,
|
||||
lastPrice: params.Default,
|
||||
maxPrice: maxPrice,
|
||||
ignorePrice: ignorePrice,
|
||||
checkBlocks: blocks,
|
||||
percentile: percent,
|
||||
backend: backend,
|
||||
lastPrice: params.Default,
|
||||
maxPrice: maxPrice,
|
||||
ignorePrice: ignorePrice,
|
||||
checkBlocks: blocks,
|
||||
percentile: percent,
|
||||
maxHeaderHistory: params.MaxHeaderHistory,
|
||||
maxBlockHistory: params.MaxBlockHistory,
|
||||
historyCache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// SuggestPrice returns the recommended gas price.
|
||||
func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
|
||||
head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
|
||||
// SuggestTipCap returns a tip cap so that newly created transaction can have a
|
||||
// very high chance to be included in the following blocks.
|
||||
//
|
||||
// Note, for legacy transactions and the legacy eth_gasPrice RPC call, it will be
|
||||
// necessary to add the basefee to the returned number to fall back to the legacy
|
||||
// behavior.
|
||||
func (oracle *Oracle) SuggestTipCap(ctx context.Context) (*big.Int, error) {
|
||||
head, _ := oracle.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
|
||||
headHash := head.Hash()
|
||||
|
||||
// If the latest gasprice is still available, return it.
|
||||
gpo.cacheLock.RLock()
|
||||
lastHead, lastPrice := gpo.lastHead, gpo.lastPrice
|
||||
gpo.cacheLock.RUnlock()
|
||||
oracle.cacheLock.RLock()
|
||||
lastHead, lastPrice := oracle.lastHead, oracle.lastPrice
|
||||
oracle.cacheLock.RUnlock()
|
||||
if headHash == lastHead {
|
||||
return lastPrice, nil
|
||||
return new(big.Int).Set(lastPrice), nil
|
||||
}
|
||||
gpo.fetchLock.Lock()
|
||||
defer gpo.fetchLock.Unlock()
|
||||
oracle.fetchLock.Lock()
|
||||
defer oracle.fetchLock.Unlock()
|
||||
|
||||
// Try checking the cache again, maybe the last fetch fetched what we need
|
||||
gpo.cacheLock.RLock()
|
||||
lastHead, lastPrice = gpo.lastHead, gpo.lastPrice
|
||||
gpo.cacheLock.RUnlock()
|
||||
oracle.cacheLock.RLock()
|
||||
lastHead, lastPrice = oracle.lastHead, oracle.lastPrice
|
||||
oracle.cacheLock.RUnlock()
|
||||
if headHash == lastHead {
|
||||
return lastPrice, nil
|
||||
return new(big.Int).Set(lastPrice), nil
|
||||
}
|
||||
var (
|
||||
sent, exp int
|
||||
number = head.Number.Uint64()
|
||||
result = make(chan getBlockPricesResult, gpo.checkBlocks)
|
||||
result = make(chan results, oracle.checkBlocks)
|
||||
quit = make(chan struct{})
|
||||
txPrices []*big.Int
|
||||
results []*big.Int
|
||||
)
|
||||
for sent < gpo.checkBlocks && number > 0 {
|
||||
go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit)
|
||||
for sent < oracle.checkBlocks && number > 0 {
|
||||
go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit)
|
||||
sent++
|
||||
exp++
|
||||
number--
|
||||
|
|
@ -142,93 +175,116 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
|
|||
res := <-result
|
||||
if res.err != nil {
|
||||
close(quit)
|
||||
return lastPrice, res.err
|
||||
return new(big.Int).Set(lastPrice), res.err
|
||||
}
|
||||
exp--
|
||||
// Nothing returned. There are two special cases here:
|
||||
// - The block is empty
|
||||
// - All the transactions included are sent by the miner itself.
|
||||
// In these cases, use the latest calculated price for samping.
|
||||
if len(res.prices) == 0 {
|
||||
res.prices = []*big.Int{lastPrice}
|
||||
if len(res.values) == 0 {
|
||||
res.values = []*big.Int{lastPrice}
|
||||
}
|
||||
// Besides, in order to collect enough data for sampling, if nothing
|
||||
// meaningful returned, try to query more blocks. But the maximum
|
||||
// is 2*checkBlocks.
|
||||
if len(res.prices) == 1 && len(txPrices)+1+exp < gpo.checkBlocks*2 && number > 0 {
|
||||
go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit)
|
||||
if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 {
|
||||
go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit)
|
||||
sent++
|
||||
exp++
|
||||
number--
|
||||
}
|
||||
txPrices = append(txPrices, res.prices...)
|
||||
results = append(results, res.values...)
|
||||
}
|
||||
price := lastPrice
|
||||
if len(txPrices) > 0 {
|
||||
sort.Sort(bigIntArray(txPrices))
|
||||
price = txPrices[(len(txPrices)-1)*gpo.percentile/100]
|
||||
if len(results) > 0 {
|
||||
sort.Sort(bigIntArray(results))
|
||||
price = results[(len(results)-1)*oracle.percentile/100]
|
||||
}
|
||||
if price.Cmp(gpo.maxPrice) > 0 {
|
||||
price = new(big.Int).Set(gpo.maxPrice)
|
||||
if price.Cmp(oracle.maxPrice) > 0 {
|
||||
price = new(big.Int).Set(oracle.maxPrice)
|
||||
}
|
||||
|
||||
// Check gas price min.
|
||||
minGasPrice := common.GetMinGasPrice(head.Number)
|
||||
if price.Cmp(minGasPrice) < 0 {
|
||||
price = new(big.Int).Set(minGasPrice)
|
||||
// Check min gas price for non-eip1559 block
|
||||
if head.BaseFee == nil {
|
||||
minGasPrice := common.GetMinGasPrice(head.Number)
|
||||
if price.Cmp(minGasPrice) < 0 {
|
||||
price = new(big.Int).Set(minGasPrice)
|
||||
}
|
||||
}
|
||||
|
||||
gpo.cacheLock.Lock()
|
||||
gpo.lastHead = headHash
|
||||
gpo.lastPrice = price
|
||||
gpo.cacheLock.Unlock()
|
||||
return price, nil
|
||||
oracle.cacheLock.Lock()
|
||||
oracle.lastHead = headHash
|
||||
oracle.lastPrice = price
|
||||
oracle.cacheLock.Unlock()
|
||||
|
||||
return new(big.Int).Set(price), nil
|
||||
}
|
||||
|
||||
type getBlockPricesResult struct {
|
||||
prices []*big.Int
|
||||
type results struct {
|
||||
values []*big.Int
|
||||
err error
|
||||
}
|
||||
|
||||
type transactionsByGasPrice []*types.Transaction
|
||||
type txSorter struct {
|
||||
txs []*types.Transaction
|
||||
baseFee *big.Int
|
||||
}
|
||||
|
||||
func (t transactionsByGasPrice) Len() int { return len(t) }
|
||||
func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
||||
func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPriceCmp(t[j]) < 0 }
|
||||
func newSorter(txs []*types.Transaction, baseFee *big.Int) *txSorter {
|
||||
return &txSorter{
|
||||
txs: txs,
|
||||
baseFee: baseFee,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *txSorter) Len() int { return len(s.txs) }
|
||||
func (s *txSorter) Swap(i, j int) {
|
||||
s.txs[i], s.txs[j] = s.txs[j], s.txs[i]
|
||||
}
|
||||
func (s *txSorter) Less(i, j int) bool {
|
||||
// It's okay to discard the error because a tx would never be
|
||||
// accepted into a block with an invalid effective tip.
|
||||
tip1, _ := s.txs[i].EffectiveGasTip(s.baseFee)
|
||||
tip2, _ := s.txs[j].EffectiveGasTip(s.baseFee)
|
||||
return tip1.Cmp(tip2) < 0
|
||||
}
|
||||
|
||||
// getBlockPrices calculates the lowest transaction gas price in a given block
|
||||
// and sends it to the result channel. If the block is empty or all transactions
|
||||
// are sent by the miner itself(it doesn't make any sense to include this kind of
|
||||
// transaction prices for sampling), nil gasprice is returned.
|
||||
func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan getBlockPricesResult, quit chan struct{}) {
|
||||
block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
|
||||
func (oracle *Oracle) getBlockValues(ctx context.Context, signer types.Signer, blockNum uint64, limit int, ignoreUnder *big.Int, result chan results, quit chan struct{}) {
|
||||
block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
|
||||
if block == nil {
|
||||
select {
|
||||
case result <- getBlockPricesResult{nil, err}:
|
||||
case result <- results{nil, err}:
|
||||
case <-quit:
|
||||
}
|
||||
return
|
||||
}
|
||||
blockTxs := block.Transactions()
|
||||
txs := make([]*types.Transaction, len(blockTxs))
|
||||
copy(txs, blockTxs)
|
||||
sort.Sort(transactionsByGasPrice(txs))
|
||||
// Sort the transaction by effective tip in ascending sort.
|
||||
txs := make([]*types.Transaction, len(block.Transactions()))
|
||||
copy(txs, block.Transactions())
|
||||
sorter := newSorter(txs, block.BaseFee())
|
||||
sort.Sort(sorter)
|
||||
|
||||
var prices []*big.Int
|
||||
for _, tx := range txs {
|
||||
if ignoreUnder != nil && tx.GasPrice().Cmp(ignoreUnder) == -1 {
|
||||
for _, tx := range sorter.txs {
|
||||
tip, _ := tx.EffectiveGasTip(block.BaseFee())
|
||||
if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 {
|
||||
continue
|
||||
}
|
||||
sender, err := types.Sender(signer, tx)
|
||||
if err == nil && sender != block.Coinbase() {
|
||||
prices = append(prices, tx.GasPrice())
|
||||
prices = append(prices, tip)
|
||||
if len(prices) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
select {
|
||||
case result <- getBlockPricesResult{prices, nil}:
|
||||
case result <- results{prices, nil}:
|
||||
case <-quit:
|
||||
}
|
||||
}
|
||||
|
|
|
|||
202
eth/gasprice/gasprice_test.go
Normal file
202
eth/gasprice/gasprice_test.go
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package gasprice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/event"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
)
|
||||
|
||||
const testHead = 32
|
||||
|
||||
type testBackend struct {
|
||||
chain *core.BlockChain
|
||||
pending bool // pending block available
|
||||
}
|
||||
|
||||
func (b *testBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
||||
if number > testHead {
|
||||
return nil, nil
|
||||
}
|
||||
if number == rpc.EarliestBlockNumber {
|
||||
number = 0
|
||||
}
|
||||
if number == rpc.CommittedBlockNumber {
|
||||
return b.chain.CurrentBlock().Header(), nil
|
||||
}
|
||||
if number == rpc.LatestBlockNumber {
|
||||
number = testHead
|
||||
}
|
||||
if number == rpc.PendingBlockNumber {
|
||||
if b.pending {
|
||||
number = testHead + 1
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return b.chain.GetHeaderByNumber(uint64(number)), nil
|
||||
}
|
||||
|
||||
func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
|
||||
if number > testHead {
|
||||
return nil, nil
|
||||
}
|
||||
if number == rpc.EarliestBlockNumber {
|
||||
number = 0
|
||||
}
|
||||
if number == rpc.CommittedBlockNumber {
|
||||
return b.chain.CurrentBlock(), nil
|
||||
}
|
||||
if number == rpc.LatestBlockNumber {
|
||||
number = testHead
|
||||
}
|
||||
if number == rpc.PendingBlockNumber {
|
||||
if b.pending {
|
||||
number = testHead + 1
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return b.chain.GetBlockByNumber(uint64(number)), nil
|
||||
}
|
||||
|
||||
func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
return b.chain.GetReceiptsByHash(hash), nil
|
||||
}
|
||||
|
||||
func (b *testBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
||||
if b.pending {
|
||||
block := b.chain.GetBlockByNumber(testHead + 1)
|
||||
return block, b.chain.GetReceiptsByHash(block.Hash())
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *testBackend) ChainConfig() *params.ChainConfig {
|
||||
return b.chain.Config()
|
||||
}
|
||||
|
||||
func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newTestBackend(t *testing.T, eip1559Block *big.Int, pending bool) *testBackend {
|
||||
config := *params.TestChainConfig // needs copy because it is modified below
|
||||
config.Eip1559Block = eip1559Block
|
||||
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
gspec = &core.Genesis{
|
||||
Config: &config,
|
||||
Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
|
||||
}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
engine := ethash.NewFaker()
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesis, _ := gspec.Commit(db)
|
||||
|
||||
// Generate testing blocks
|
||||
blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, testHead+1, func(i int, b *core.BlockGen) {
|
||||
b.SetCoinbase(common.Address{1})
|
||||
|
||||
var txdata types.TxData
|
||||
if eip1559Block != nil && b.Number().Cmp(eip1559Block) >= 0 {
|
||||
txdata = &types.DynamicFeeTx{
|
||||
ChainID: gspec.Config.ChainId,
|
||||
Nonce: b.TxNonce(addr),
|
||||
To: &common.Address{},
|
||||
Gas: 30000,
|
||||
GasFeeCap: big.NewInt(100 * params.GWei),
|
||||
GasTipCap: big.NewInt(int64(i+1) * params.GWei),
|
||||
Data: []byte{},
|
||||
}
|
||||
} else {
|
||||
txdata = &types.LegacyTx{
|
||||
Nonce: b.TxNonce(addr),
|
||||
To: &common.Address{},
|
||||
Gas: 21000,
|
||||
GasPrice: big.NewInt(int64(i+1) * params.GWei),
|
||||
Value: big.NewInt(100),
|
||||
Data: []byte{},
|
||||
}
|
||||
}
|
||||
b.AddTx(types.MustSignNewTx(key, signer, txdata))
|
||||
})
|
||||
// Construct testing chain
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.Commit(diskdb)
|
||||
chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create local chain, %v", err)
|
||||
}
|
||||
chain.InsertChain(blocks)
|
||||
return &testBackend{chain: chain, pending: pending}
|
||||
}
|
||||
|
||||
func (b *testBackend) CurrentHeader() *types.Header {
|
||||
return b.chain.CurrentHeader()
|
||||
}
|
||||
|
||||
func (b *testBackend) GetBlockByNumber(number uint64) *types.Block {
|
||||
return b.chain.GetBlockByNumber(number)
|
||||
}
|
||||
|
||||
func TestSuggestTipCap(t *testing.T) {
|
||||
config := Config{
|
||||
Blocks: 3,
|
||||
Percentile: 60,
|
||||
Default: big.NewInt(params.GWei),
|
||||
}
|
||||
var cases = []struct {
|
||||
fork *big.Int // Eip1559 fork number
|
||||
expect *big.Int // Expected gasprice suggestion
|
||||
}{
|
||||
{nil, big.NewInt(params.GWei * int64(30))},
|
||||
{big.NewInt(0), big.NewInt(params.GWei * int64(30))}, // Fork point in genesis
|
||||
{big.NewInt(1), big.NewInt(params.GWei * int64(30))}, // Fork point in first block
|
||||
{big.NewInt(32), big.NewInt(params.GWei * int64(30))}, // Fork point in last block
|
||||
{big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future
|
||||
}
|
||||
for _, c := range cases {
|
||||
backend := newTestBackend(t, c.fork, false)
|
||||
oracle := NewOracle(backend, config)
|
||||
|
||||
// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G
|
||||
got, err := oracle.SuggestTipCap(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve recommended gas price: %v", err)
|
||||
}
|
||||
if got.Cmp(c.expect) != 0 {
|
||||
t.Fatalf("Gas price mismatch, want %d, got %d", c.expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error {
|
|||
}
|
||||
|
||||
// Pending returns all the transactions known to the pool
|
||||
func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
|
||||
func (p *testTxPool) Pending(enforceTips bool) map[common.Address]types.Transactions {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
|
|
@ -123,7 +123,7 @@ func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
|
|||
for _, batch := range batches {
|
||||
sort.Sort(types.TxByNonce(batch))
|
||||
}
|
||||
return batches, nil
|
||||
return batches
|
||||
}
|
||||
|
||||
func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ type txPool interface {
|
|||
|
||||
// Pending should return pending transactions.
|
||||
// The slice should be modifiable by the caller.
|
||||
Pending() (map[common.Address]types.Transactions, error)
|
||||
Pending(enforceTips bool) map[common.Address]types.Transactions
|
||||
|
||||
// SubscribeNewTxsEvent should return an event subscription of
|
||||
// NewTxsEvent and send events to the given channel.
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ type txsync struct {
|
|||
// syncTransactions starts sending all currently pending transactions to the given peer.
|
||||
func (pm *ProtocolManager) syncTransactions(p *peer) {
|
||||
var txs types.Transactions
|
||||
pending, _ := pm.txpool.Pending()
|
||||
pending := pm.txpool.Pending(false)
|
||||
for _, batch := range pending {
|
||||
txs = append(txs, batch...)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
|||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
|
||||
msg, err := tx.AsMessage(signer, nil, nil)
|
||||
msg, err := tx.AsMessage(signer, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
|
|
@ -201,7 +201,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
|||
b.Fatalf("failed to parse testcase input: %v", err)
|
||||
}
|
||||
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
|
||||
msg, err := tx.AsMessage(signer, nil, nil)
|
||||
msg, err := tx.AsMessage(signer, nil, nil, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -682,9 +682,10 @@ func (jst *JsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
|
|||
|
||||
// Compute intrinsic gas
|
||||
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
|
||||
isEIP1559 := env.ChainConfig().IsEIP1559(env.Context.BlockNumber)
|
||||
// after update core.IntrinsicGas, use isIstanbul in it
|
||||
// isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
|
||||
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead)
|
||||
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isEIP1559)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
|
|||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
msg, err := tx.AsMessage(signer, nil, nil)
|
||||
msg, err := tx.AsMessage(signer, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
|
|
@ -239,7 +239,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
|
|||
}
|
||||
evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
|
||||
msg, err := tx.AsMessage(signer, nil, nil)
|
||||
msg, err := tx.AsMessage(signer, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
|
|
@ -336,7 +336,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
|||
//EnableReturnData: false,
|
||||
})
|
||||
evm := vm.NewEVM(context, txContext, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
|
||||
msg, err := tx.AsMessage(signer, nil, nil)
|
||||
msg, err := tx.AsMessage(signer, nil, nil, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -274,17 +274,6 @@ func (ec *Client) GetTransactionReceiptResult(ctx context.Context, txHash common
|
|||
return r, result, err
|
||||
}
|
||||
|
||||
func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
}
|
||||
pending := big.NewInt(-1)
|
||||
if number.Cmp(pending) == 0 {
|
||||
return "pending"
|
||||
}
|
||||
return hexutil.EncodeBig(number)
|
||||
}
|
||||
|
||||
type rpcProgress struct {
|
||||
StartingBlock hexutil.Uint64
|
||||
CurrentBlock hexutil.Uint64
|
||||
|
|
@ -436,8 +425,6 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
|
|||
return uint(num), err
|
||||
}
|
||||
|
||||
// TODO: SubscribePendingTransactions (needs server side)
|
||||
|
||||
// Contract Calling
|
||||
|
||||
// CallContract executes a message call transaction, which is directly executed in the VM
|
||||
|
|
@ -476,6 +463,48 @@ func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
|||
return (*big.Int)(&hex), nil
|
||||
}
|
||||
|
||||
// SuggestGasTipCap retrieves the currently suggested gas tip cap after 1559 to
|
||||
// allow a timely execution of a transaction.
|
||||
func (ec *Client) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||
var hex hexutil.Big
|
||||
if err := ec.c.CallContext(ctx, &hex, "eth_maxPriorityFeePerGas"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*big.Int)(&hex), nil
|
||||
}
|
||||
|
||||
type feeHistoryResultMarshaling struct {
|
||||
OldestBlock *hexutil.Big `json:"oldestBlock"`
|
||||
Reward [][]*hexutil.Big `json:"reward,omitempty"`
|
||||
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
|
||||
GasUsedRatio []float64 `json:"gasUsedRatio"`
|
||||
}
|
||||
|
||||
// FeeHistory retrieves the fee market history.
|
||||
func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) {
|
||||
var res feeHistoryResultMarshaling
|
||||
if err := ec.c.CallContext(ctx, &res, "eth_feeHistory", hexutil.Uint(blockCount), toBlockNumArg(lastBlock), rewardPercentiles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reward := make([][]*big.Int, len(res.Reward))
|
||||
for i, r := range res.Reward {
|
||||
reward[i] = make([]*big.Int, len(r))
|
||||
for j, r := range r {
|
||||
reward[i][j] = (*big.Int)(r)
|
||||
}
|
||||
}
|
||||
baseFee := make([]*big.Int, len(res.BaseFee))
|
||||
for i, b := range res.BaseFee {
|
||||
baseFee[i] = (*big.Int)(b)
|
||||
}
|
||||
return ðereum.FeeHistory{
|
||||
OldestBlock: (*big.Int)(res.OldestBlock),
|
||||
Reward: reward,
|
||||
BaseFee: baseFee,
|
||||
GasUsedRatio: res.GasUsedRatio,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
|
||||
// the current pending state of the backend blockchain. There is no guarantee that this is
|
||||
// the true gas limit requirement as other transactions may be added or removed by miners,
|
||||
|
|
@ -501,6 +530,17 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er
|
|||
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
|
||||
}
|
||||
|
||||
func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
}
|
||||
pending := big.NewInt(-1)
|
||||
if number.Cmp(pending) == 0 {
|
||||
return "pending"
|
||||
}
|
||||
return hexutil.EncodeBig(number)
|
||||
}
|
||||
|
||||
// SendOrderTransaction injects a signed transaction into the pending pool for execution.
|
||||
//
|
||||
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
||||
|
|
@ -539,5 +579,14 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
|||
if msg.GasPrice != nil {
|
||||
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
|
||||
}
|
||||
if msg.GasFeeCap != nil {
|
||||
arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
|
||||
}
|
||||
if msg.GasTipCap != nil {
|
||||
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
|
||||
}
|
||||
if msg.AccessList != nil {
|
||||
arg["accessList"] = msg.AccessList
|
||||
}
|
||||
return arg
|
||||
}
|
||||
|
|
|
|||
244
ethclient/gethclient/gethclient.go
Normal file
244
ethclient/gethclient/gethclient.go
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package gethclient provides an RPC client for geth-specific APIs.
|
||||
package gethclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
|
||||
ethereum "github.com/XinFinOrg/XDPoSChain"
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/p2p"
|
||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||
)
|
||||
|
||||
// Client is a wrapper around rpc.Client that implements geth-specific functionality.
|
||||
//
|
||||
// If you want to use the standardized Ethereum RPC functionality, use ethclient.Client instead.
|
||||
type Client struct {
|
||||
c *rpc.Client
|
||||
}
|
||||
|
||||
// New creates a client that uses the given RPC client.
|
||||
func New(c *rpc.Client) *Client {
|
||||
return &Client{c}
|
||||
}
|
||||
|
||||
// CreateAccessList tries to create an access list for a specific transaction based on the
|
||||
// current pending state of the blockchain.
|
||||
func (ec *Client) CreateAccessList(ctx context.Context, msg ethereum.CallMsg) (*types.AccessList, uint64, string, error) {
|
||||
type accessListResult struct {
|
||||
Accesslist *types.AccessList `json:"accessList"`
|
||||
Error string `json:"error,omitempty"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed"`
|
||||
}
|
||||
var result accessListResult
|
||||
if err := ec.c.CallContext(ctx, &result, "eth_createAccessList", toCallArg(msg)); err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
return result.Accesslist, uint64(result.GasUsed), result.Error, nil
|
||||
}
|
||||
|
||||
// AccountResult is the result of a GetProof operation.
|
||||
type AccountResult struct {
|
||||
Address common.Address `json:"address"`
|
||||
AccountProof []string `json:"accountProof"`
|
||||
Balance *big.Int `json:"balance"`
|
||||
CodeHash common.Hash `json:"codeHash"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
StorageHash common.Hash `json:"storageHash"`
|
||||
StorageProof []StorageResult `json:"storageProof"`
|
||||
}
|
||||
|
||||
// StorageResult provides a proof for a key-value pair.
|
||||
type StorageResult struct {
|
||||
Key string `json:"key"`
|
||||
Value *big.Int `json:"value"`
|
||||
Proof []string `json:"proof"`
|
||||
}
|
||||
|
||||
// GetProof returns the account and storage values of the specified account including the Merkle-proof.
|
||||
// The block number can be nil, in which case the value is taken from the latest known block.
|
||||
func (ec *Client) GetProof(ctx context.Context, account common.Address, keys []string, blockNumber *big.Int) (*AccountResult, error) {
|
||||
|
||||
type storageResult struct {
|
||||
Key string `json:"key"`
|
||||
Value *hexutil.Big `json:"value"`
|
||||
Proof []string `json:"proof"`
|
||||
}
|
||||
|
||||
type accountResult struct {
|
||||
Address common.Address `json:"address"`
|
||||
AccountProof []string `json:"accountProof"`
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
CodeHash common.Hash `json:"codeHash"`
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
StorageHash common.Hash `json:"storageHash"`
|
||||
StorageProof []storageResult `json:"storageProof"`
|
||||
}
|
||||
|
||||
var res accountResult
|
||||
err := ec.c.CallContext(ctx, &res, "eth_getProof", account, keys, toBlockNumArg(blockNumber))
|
||||
// Turn hexutils back to normal datatypes
|
||||
storageResults := make([]StorageResult, 0, len(res.StorageProof))
|
||||
for _, st := range res.StorageProof {
|
||||
storageResults = append(storageResults, StorageResult{
|
||||
Key: st.Key,
|
||||
Value: st.Value.ToInt(),
|
||||
Proof: st.Proof,
|
||||
})
|
||||
}
|
||||
result := AccountResult{
|
||||
Address: res.Address,
|
||||
AccountProof: res.AccountProof,
|
||||
Balance: res.Balance.ToInt(),
|
||||
Nonce: uint64(res.Nonce),
|
||||
CodeHash: res.CodeHash,
|
||||
StorageHash: res.StorageHash,
|
||||
}
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// OverrideAccount specifies the state of an account to be overridden.
|
||||
type OverrideAccount struct {
|
||||
Nonce uint64 `json:"nonce"`
|
||||
Code []byte `json:"code"`
|
||||
Balance *big.Int `json:"balance"`
|
||||
State map[common.Hash]common.Hash `json:"state"`
|
||||
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
|
||||
}
|
||||
|
||||
// CallContract executes a message call transaction, which is directly executed in the VM
|
||||
// of the node, but never mined into the blockchain.
|
||||
//
|
||||
// blockNumber selects the block height at which the call runs. It can be nil, in which
|
||||
// case the code is taken from the latest known block. Note that state from very old
|
||||
// blocks might not be available.
|
||||
//
|
||||
// overrides specifies a map of contract states that should be overwritten before executing
|
||||
// the message call.
|
||||
// Please use ethclient.CallContract instead if you don't need the override functionality.
|
||||
func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int, overrides *map[common.Address]OverrideAccount) ([]byte, error) {
|
||||
var hex hexutil.Bytes
|
||||
err := ec.c.CallContext(
|
||||
ctx, &hex, "eth_call", toCallArg(msg),
|
||||
toBlockNumArg(blockNumber), toOverrideMap(overrides),
|
||||
)
|
||||
return hex, err
|
||||
}
|
||||
|
||||
// GCStats retrieves the current garbage collection stats from a geth node.
|
||||
func (ec *Client) GCStats(ctx context.Context) (*debug.GCStats, error) {
|
||||
var result debug.GCStats
|
||||
err := ec.c.CallContext(ctx, &result, "debug_gcStats")
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// MemStats retrieves the current memory stats from a geth node.
|
||||
func (ec *Client) MemStats(ctx context.Context) (*runtime.MemStats, error) {
|
||||
var result runtime.MemStats
|
||||
err := ec.c.CallContext(ctx, &result, "debug_memStats")
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// SetHead sets the current head of the local chain by block number.
|
||||
// Note, this is a destructive action and may severely damage your chain.
|
||||
// Use with extreme caution.
|
||||
func (ec *Client) SetHead(ctx context.Context, number *big.Int) error {
|
||||
return ec.c.CallContext(ctx, nil, "debug_setHead", toBlockNumArg(number))
|
||||
}
|
||||
|
||||
// GetNodeInfo retrieves the node info of a geth node.
|
||||
func (ec *Client) GetNodeInfo(ctx context.Context) (*p2p.NodeInfo, error) {
|
||||
var result p2p.NodeInfo
|
||||
err := ec.c.CallContext(ctx, &result, "admin_nodeInfo")
|
||||
return &result, err
|
||||
}
|
||||
|
||||
// SubscribePendingTransactions subscribes to new pending transactions.
|
||||
func (ec *Client) SubscribePendingTransactions(ctx context.Context, ch chan<- common.Hash) (*rpc.ClientSubscription, error) {
|
||||
return ec.c.EthSubscribe(ctx, ch, "newPendingTransactions")
|
||||
}
|
||||
|
||||
func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
}
|
||||
pending := big.NewInt(-1)
|
||||
if number.Cmp(pending) == 0 {
|
||||
return "pending"
|
||||
}
|
||||
return hexutil.EncodeBig(number)
|
||||
}
|
||||
|
||||
func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||
arg := map[string]interface{}{
|
||||
"from": msg.From,
|
||||
"to": msg.To,
|
||||
}
|
||||
if len(msg.Data) > 0 {
|
||||
arg["data"] = hexutil.Bytes(msg.Data)
|
||||
}
|
||||
if msg.Value != nil {
|
||||
arg["value"] = (*hexutil.Big)(msg.Value)
|
||||
}
|
||||
if msg.Gas != 0 {
|
||||
arg["gas"] = hexutil.Uint64(msg.Gas)
|
||||
}
|
||||
if msg.GasPrice != nil {
|
||||
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
|
||||
}
|
||||
if msg.GasFeeCap != nil {
|
||||
arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
|
||||
}
|
||||
if msg.GasTipCap != nil {
|
||||
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
|
||||
}
|
||||
if msg.AccessList != nil {
|
||||
arg["accessList"] = msg.AccessList
|
||||
}
|
||||
return arg
|
||||
}
|
||||
|
||||
func toOverrideMap(overrides *map[common.Address]OverrideAccount) interface{} {
|
||||
if overrides == nil {
|
||||
return nil
|
||||
}
|
||||
type overrideAccount struct {
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
Code hexutil.Bytes `json:"code"`
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
State map[common.Hash]common.Hash `json:"state"`
|
||||
StateDiff map[common.Hash]common.Hash `json:"stateDiff"`
|
||||
}
|
||||
result := make(map[common.Address]overrideAccount)
|
||||
for addr, override := range *overrides {
|
||||
result[addr] = overrideAccount{
|
||||
Nonce: hexutil.Uint64(override.Nonce),
|
||||
Code: override.Code,
|
||||
Balance: (*hexutil.Big)(override.Balance),
|
||||
State: override.State,
|
||||
StateDiff: override.StateDiff,
|
||||
}
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
|
@ -816,8 +816,11 @@ func (s *Service) reportStats(conn *connWrapper) error {
|
|||
sync := s.eth.Downloader().Progress()
|
||||
syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
|
||||
|
||||
price, _ := s.eth.ApiBackend.SuggestPrice(context.Background())
|
||||
price, _ := s.eth.ApiBackend.SuggestGasTipCap(context.Background())
|
||||
gasprice = int(price.Uint64())
|
||||
if basefee := s.eth.ApiBackend.CurrentHeader().BaseFee; basefee != nil {
|
||||
gasprice += int(basefee.Uint64())
|
||||
}
|
||||
} else {
|
||||
sync := s.les.Downloader().Progress()
|
||||
syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -63,6 +63,7 @@ require (
|
|||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect
|
||||
github.com/maruel/ut v1.0.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
|
|
|
|||
|
|
@ -117,10 +117,13 @@ type CallMsg struct {
|
|||
To *common.Address // the destination contract (nil for contract creation)
|
||||
Gas uint64 // if 0, the call executes with near-infinite gas
|
||||
GasPrice *big.Int // wei <-> gas exchange ratio
|
||||
GasFeeCap *big.Int // EIP-1559 fee cap per gas.
|
||||
GasTipCap *big.Int // EIP-1559 tip per gas.
|
||||
Value *big.Int // amount of wei sent along with the call
|
||||
Data []byte // input data, usually an ABI-encoded contract method invocation
|
||||
BalanceTokenFee *big.Int
|
||||
AccessList types.AccessList // EIP-2930 access list.
|
||||
|
||||
AccessList types.AccessList // EIP-2930 access list.
|
||||
}
|
||||
|
||||
// A ContractCaller provides contract calls, essentially transactions that are executed by
|
||||
|
|
@ -180,6 +183,15 @@ type GasPricer interface {
|
|||
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
||||
}
|
||||
|
||||
// FeeHistory provides recent fee market data that consumers can use to determine
|
||||
// a reasonable maxPriorityFeePerGas value.
|
||||
type FeeHistory struct {
|
||||
OldestBlock *big.Int // block coresponding to first response value
|
||||
Reward [][]*big.Int // list every txs priority fee per block
|
||||
BaseFee []*big.Int // list of each block's base fee
|
||||
GasUsedRatio []float64 // ratio of gas used out of the total available limit
|
||||
}
|
||||
|
||||
// A PendingStateReader provides access to the pending state, which is the result of all
|
||||
// known executable transactions which have not yet been included in the blockchain. It is
|
||||
// commonly used to display the result of ’unconfirmed’ actions (e.g. wallet value
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
|
||||
"github.com/XinFinOrg/XDPoSChain/core"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||
|
|
@ -80,10 +81,60 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI {
|
|||
return &PublicEthereumAPI{b}
|
||||
}
|
||||
|
||||
// GasPrice returns a suggestion for a gas price.
|
||||
// GasPrice returns a suggestion for a gas price for legacy transactions.
|
||||
func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) {
|
||||
price, err := s.b.SuggestPrice(ctx)
|
||||
return (*hexutil.Big)(price), err
|
||||
tipcap, err := s.b.SuggestGasTipCap(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if head := s.b.CurrentHeader(); head.BaseFee != nil {
|
||||
tipcap.Add(tipcap, head.BaseFee)
|
||||
}
|
||||
return (*hexutil.Big)(tipcap), err
|
||||
}
|
||||
|
||||
// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic transactions.
|
||||
func (s *PublicEthereumAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) {
|
||||
tipcap, err := s.b.SuggestGasTipCap(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*hexutil.Big)(tipcap), err
|
||||
}
|
||||
|
||||
type feeHistoryResult struct {
|
||||
OldestBlock *hexutil.Big `json:"oldestBlock"`
|
||||
Reward [][]*hexutil.Big `json:"reward,omitempty"`
|
||||
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
|
||||
GasUsedRatio []float64 `json:"gasUsedRatio"`
|
||||
}
|
||||
|
||||
// FeeHistory returns the fee market history.
|
||||
func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount hexutil.Uint, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
|
||||
oldest, reward, baseFee, gasUsed, err := s.b.FeeHistory(ctx, uint64(blockCount), lastBlock, rewardPercentiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results := &feeHistoryResult{
|
||||
OldestBlock: (*hexutil.Big)(oldest),
|
||||
GasUsedRatio: gasUsed,
|
||||
}
|
||||
if reward != nil {
|
||||
results.Reward = make([][]*hexutil.Big, len(reward))
|
||||
for i, w := range reward {
|
||||
results.Reward[i] = make([]*hexutil.Big, len(w))
|
||||
for j, v := range w {
|
||||
results.Reward[i][j] = (*hexutil.Big)(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
if baseFee != nil {
|
||||
results.BaseFee = make([]*hexutil.Big, len(baseFee))
|
||||
for i, v := range baseFee {
|
||||
results.BaseFee[i] = (*hexutil.Big)(v)
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// ProtocolVersion returns the current Ethereum protocol version this node supports
|
||||
|
|
@ -132,12 +183,12 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac
|
|||
"queued": make(map[string]map[string]*RPCTransaction),
|
||||
}
|
||||
pending, queue := s.b.TxPoolContent()
|
||||
|
||||
curHeader := s.b.CurrentHeader()
|
||||
// Flatten the pending transactions
|
||||
for account, txs := range pending {
|
||||
dump := make(map[string]*RPCTransaction)
|
||||
for _, tx := range txs {
|
||||
dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx)
|
||||
dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
|
||||
}
|
||||
content["pending"][account.Hex()] = dump
|
||||
}
|
||||
|
|
@ -145,13 +196,36 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransac
|
|||
for account, txs := range queue {
|
||||
dump := make(map[string]*RPCTransaction)
|
||||
for _, tx := range txs {
|
||||
dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx)
|
||||
dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
|
||||
}
|
||||
content["queued"][account.Hex()] = dump
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// ContentFrom returns the transactions contained within the transaction pool.
|
||||
func (s *PublicTxPoolAPI) ContentFrom(addr common.Address) map[string]map[string]*RPCTransaction {
|
||||
content := make(map[string]map[string]*RPCTransaction, 2)
|
||||
pending, queue := s.b.TxPoolContentFrom(addr)
|
||||
curHeader := s.b.CurrentHeader()
|
||||
|
||||
// Build the pending transactions
|
||||
dump := make(map[string]*RPCTransaction, len(pending))
|
||||
for _, tx := range pending {
|
||||
dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
|
||||
}
|
||||
content["pending"] = dump
|
||||
|
||||
// Build the queued transactions
|
||||
dump = make(map[string]*RPCTransaction, len(queue))
|
||||
for _, tx := range queue {
|
||||
dump[fmt.Sprintf("%d", tx.Nonce())] = newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig())
|
||||
}
|
||||
content["queued"] = dump
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
// Status returns the number of pending and queued transaction in the pool.
|
||||
func (s *PublicTxPoolAPI) Status() map[string]hexutil.Uint {
|
||||
pending, queue := s.b.Stats()
|
||||
|
|
@ -366,7 +440,7 @@ func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args *Transacti
|
|||
return nil, err
|
||||
}
|
||||
// Set some sanity defaults and terminate on failure
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
if err := args.setDefaults(ctx, s.b, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Assemble the transaction and sign with the wallet
|
||||
|
|
@ -410,14 +484,15 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args Transactio
|
|||
if args.Gas == nil {
|
||||
return nil, errors.New("gas not specified")
|
||||
}
|
||||
if args.GasPrice == nil {
|
||||
return nil, errors.New("gasPrice not specified")
|
||||
if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) {
|
||||
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
|
||||
}
|
||||
if args.Nonce == nil {
|
||||
return nil, errors.New("nonce not specified")
|
||||
}
|
||||
// Before actually sign the transaction, ensure the transaction fee is reasonable.
|
||||
if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil {
|
||||
tx := args.toTransaction()
|
||||
if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signed, err := s.signTransaction(ctx, &args, passwd)
|
||||
|
|
@ -1247,7 +1322,7 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno
|
|||
return candidatesWithStakeInfo, nil
|
||||
}
|
||||
|
||||
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, vmCfg vm.Config, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) {
|
||||
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) {
|
||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||
|
||||
statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
|
|
@ -1261,9 +1336,6 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
|||
return nil, 0, false, err, nil
|
||||
}
|
||||
|
||||
msg := args.ToMessage(b, header.Number, globalGasCap)
|
||||
msg.SetBalanceTokenFeeForCall()
|
||||
|
||||
// Setup context so it may be cancelled the call has completed
|
||||
// or, in case of unmetered gas, setup a context with a timeout.
|
||||
var cancel context.CancelFunc
|
||||
|
|
@ -1292,8 +1364,16 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
|||
return nil, 0, false, err, nil
|
||||
}
|
||||
|
||||
// TODO: replace header.BaseFee with blockCtx.BaseFee
|
||||
// reference: https://github.com/ethereum/go-ethereum/pull/29051
|
||||
msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee)
|
||||
if err != nil {
|
||||
return nil, 0, false, err, nil
|
||||
}
|
||||
msg.SetBalanceTokenFeeForCall()
|
||||
|
||||
// Get a new instance of the EVM.
|
||||
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vmCfg)
|
||||
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true})
|
||||
if err != nil {
|
||||
return nil, 0, false, err, nil
|
||||
}
|
||||
|
|
@ -1363,7 +1443,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl
|
|||
if args.To != nil && *args.To == common.MasternodeVotingSMCBinary {
|
||||
timeout = 0
|
||||
}
|
||||
result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, vm.Config{}, timeout, s.b.RPCGasCap())
|
||||
result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1419,7 +1499,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
|||
executable := func(gas uint64) (bool, []byte, error, error) {
|
||||
args.Gas = (*hexutil.Uint64)(&gas)
|
||||
|
||||
res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap)
|
||||
res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
|
||||
if err != nil {
|
||||
if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) {
|
||||
return false, nil, nil, nil // Special case, raise gas limit
|
||||
|
|
@ -1558,14 +1638,11 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
|
|||
return formatted
|
||||
}
|
||||
|
||||
// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
|
||||
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
|
||||
// transaction hashes.
|
||||
func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, ctx context.Context) (map[string]interface{}, error) {
|
||||
head := b.Header() // copies the header once
|
||||
fields := map[string]interface{}{
|
||||
// RPCMarshalHeader converts the given header to the RPC output .
|
||||
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
||||
result := map[string]interface{}{
|
||||
"number": (*hexutil.Big)(head.Number),
|
||||
"hash": b.Hash(),
|
||||
"hash": head.Hash(),
|
||||
"parentHash": head.ParentHash,
|
||||
"nonce": head.Nonce,
|
||||
"mixHash": head.MixDigest,
|
||||
|
|
@ -1574,9 +1651,8 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
|
|||
"stateRoot": head.Root,
|
||||
"miner": head.Coinbase,
|
||||
"difficulty": (*hexutil.Big)(head.Difficulty),
|
||||
"totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())),
|
||||
"extraData": hexutil.Bytes(head.Extra),
|
||||
"size": hexutil.Uint64(b.Size()),
|
||||
"size": hexutil.Uint64(head.Size()),
|
||||
"gasLimit": hexutil.Uint64(head.GasLimit),
|
||||
"gasUsed": hexutil.Uint64(head.GasUsed),
|
||||
"timestamp": (*hexutil.Big)(head.Time),
|
||||
|
|
@ -1587,6 +1663,21 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
|
|||
"penalties": hexutil.Bytes(head.Penalties),
|
||||
}
|
||||
|
||||
if head.BaseFee != nil {
|
||||
result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
|
||||
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
|
||||
// transaction hashes.
|
||||
func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, ctx context.Context) (map[string]interface{}, error) {
|
||||
fields := RPCMarshalHeader(b.Header())
|
||||
fields["size"] = hexutil.Uint64(b.Size())
|
||||
fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(b.Hash()))
|
||||
|
||||
if inclTx {
|
||||
formatTx := func(tx *types.Transaction) (interface{}, error) {
|
||||
return tx.Hash(), nil
|
||||
|
|
@ -1773,6 +1864,8 @@ type RPCTransaction struct {
|
|||
From common.Address `json:"from"`
|
||||
Gas hexutil.Uint64 `json:"gas"`
|
||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
|
||||
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
Input hexutil.Bytes `json:"input"`
|
||||
Nonce hexutil.Uint64 `json:"nonce"`
|
||||
|
|
@ -1789,7 +1882,7 @@ type RPCTransaction struct {
|
|||
|
||||
// newRPCTransaction returns a transaction that will serialize to the RPC
|
||||
// representation, with the given location metadata set (if available).
|
||||
func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction {
|
||||
func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int) *RPCTransaction {
|
||||
// Determine the signer. For replay-protected transactions, use the most permissive
|
||||
// signer, because we assume that signers are backwards-compatible with old
|
||||
// transactions. For non-protected transactions, the homestead signer signer is used
|
||||
|
|
@ -1800,7 +1893,6 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
|||
} else {
|
||||
signer = types.HomesteadSigner{}
|
||||
}
|
||||
|
||||
from, _ := types.Sender(signer, tx)
|
||||
v, r, s := tx.RawSignatureValues()
|
||||
result := &RPCTransaction{
|
||||
|
|
@ -1822,17 +1914,36 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
|
|||
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
|
||||
result.TransactionIndex = (*hexutil.Uint64)(&index)
|
||||
}
|
||||
if tx.Type() == types.AccessListTxType {
|
||||
switch tx.Type() {
|
||||
case types.AccessListTxType:
|
||||
al := tx.AccessList()
|
||||
result.Accesses = &al
|
||||
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||
case types.DynamicFeeTxType:
|
||||
al := tx.AccessList()
|
||||
result.Accesses = &al
|
||||
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
|
||||
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
|
||||
// if the transaction has been mined, compute the effective gas price
|
||||
if baseFee != nil && blockHash != (common.Hash{}) {
|
||||
// price = min(tip, gasFeeCap - baseFee) + baseFee
|
||||
price := math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
|
||||
result.GasPrice = (*hexutil.Big)(price)
|
||||
} else {
|
||||
result.GasPrice = (*hexutil.Big)(tx.GasFeeCap())
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
|
||||
func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
|
||||
return newRPCTransaction(tx, common.Hash{}, 0, 0)
|
||||
func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction {
|
||||
var baseFee *big.Int
|
||||
if current != nil {
|
||||
baseFee = eip1559.CalcBaseFee(config, current)
|
||||
}
|
||||
return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFee)
|
||||
}
|
||||
|
||||
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
|
||||
|
|
@ -1841,7 +1952,7 @@ func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransacti
|
|||
if index >= uint64(len(txs)) {
|
||||
return nil
|
||||
}
|
||||
return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index)
|
||||
return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee())
|
||||
}
|
||||
|
||||
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index.
|
||||
|
|
@ -1917,12 +2028,8 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
|||
}
|
||||
owner := common.Address{}
|
||||
|
||||
// If the gas amount is not set, extract this as it will depend on access
|
||||
// lists and we'll need to reestimate every time
|
||||
nogas := args.Gas == nil
|
||||
|
||||
// Ensure any missing fields are filled, extract the recipient and input data
|
||||
if err := args.setDefaults(ctx, b); err != nil {
|
||||
if err := args.setDefaults(ctx, b, true); err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
var to common.Address
|
||||
|
|
@ -1944,27 +2051,25 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
|||
accessList := prevTracer.AccessList()
|
||||
log.Trace("Creating access list", "input", accessList)
|
||||
|
||||
// If no gas amount was specified, each unique access list needs it's own
|
||||
// gas calculation. This is quite expensive, but we need to be accurate
|
||||
// and it's convered by the sender only anyway.
|
||||
if nogas {
|
||||
args.Gas = nil
|
||||
if err := args.setDefaults(ctx, b); err != nil {
|
||||
return nil, 0, nil, err // shouldn't happen, just in case
|
||||
}
|
||||
}
|
||||
// Copy the original db so we don't modify it
|
||||
statedb := db.Copy()
|
||||
// Set the accesslist to the last al
|
||||
args.AccessList = &accessList
|
||||
msg, err := args.ToMessage(b, block.Number(), b.RPCGasCap(), header.BaseFee)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
}
|
||||
|
||||
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
|
||||
var balanceTokenFee *big.Int
|
||||
if value, ok := feeCapacity[to]; ok {
|
||||
balanceTokenFee = value
|
||||
}
|
||||
msg := types.NewMessage(args.from(), args.To, uint64(*args.Nonce), args.Value.ToInt(), uint64(*args.Gas), args.GasPrice.ToInt(), args.data(), accessList, false, balanceTokenFee, header.Number)
|
||||
msg.SetBalanceTokenFee(balanceTokenFee)
|
||||
|
||||
// Apply the transaction with the access list tracer
|
||||
tracer := vm.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
||||
config := vm.Config{Tracer: tracer, Debug: true}
|
||||
config := vm.Config{Tracer: tracer, Debug: true, NoBaseFee: true}
|
||||
vmenv, _, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &config)
|
||||
if err != nil {
|
||||
return nil, 0, nil, err
|
||||
|
|
@ -2077,17 +2182,23 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr
|
|||
}
|
||||
|
||||
// GetTransactionByHash returns the transaction for the given hash
|
||||
func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction {
|
||||
func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||
// Try to return an already finalized transaction
|
||||
if tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash); tx != nil {
|
||||
return newRPCTransaction(tx, blockHash, blockNumber, index)
|
||||
tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
|
||||
if tx != nil {
|
||||
header, err := s.b.HeaderByHash(ctx, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee), nil
|
||||
}
|
||||
// No finalized transaction, try to retrieve it from the pool
|
||||
if tx := s.b.GetPoolTransaction(hash); tx != nil {
|
||||
return newRPCPendingTransaction(tx)
|
||||
return newRPCPendingTransaction(tx, s.b.CurrentHeader(), s.b.ChainConfig()), nil
|
||||
}
|
||||
|
||||
// Transaction unknown, return as such
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
||||
|
|
@ -2108,13 +2219,15 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context,
|
|||
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
||||
tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
|
||||
if tx == nil {
|
||||
// When the transaction doesn't exist, the RPC method should return JSON null
|
||||
// as per specification.
|
||||
return nil, nil
|
||||
}
|
||||
receipts, err := s.b.GetReceipts(ctx, blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(receipts) <= int(index) {
|
||||
if uint64(len(receipts)) <= index {
|
||||
return nil, nil
|
||||
}
|
||||
receipt := receipts[index]
|
||||
|
|
@ -2122,37 +2235,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
|||
// Derive the sender.
|
||||
bigblock := new(big.Int).SetUint64(blockNumber)
|
||||
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
|
||||
from, _ := types.Sender(signer, tx)
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
"blockNumber": hexutil.Uint64(blockNumber),
|
||||
"transactionHash": hash,
|
||||
"transactionIndex": hexutil.Uint64(index),
|
||||
"from": from,
|
||||
"to": tx.To(),
|
||||
"gasUsed": hexutil.Uint64(receipt.GasUsed),
|
||||
"cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
|
||||
"contractAddress": nil,
|
||||
"logs": receipt.Logs,
|
||||
"logsBloom": receipt.Bloom,
|
||||
"type": hexutil.Uint(tx.Type()),
|
||||
}
|
||||
|
||||
// Assign receipt status or post state.
|
||||
if len(receipt.PostState) > 0 {
|
||||
fields["root"] = hexutil.Bytes(receipt.PostState)
|
||||
} else {
|
||||
fields["status"] = hexutil.Uint(receipt.Status)
|
||||
}
|
||||
if receipt.Logs == nil {
|
||||
fields["logs"] = [][]*types.Log{}
|
||||
}
|
||||
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
||||
if receipt.ContractAddress != (common.Address{}) {
|
||||
fields["contractAddress"] = receipt.ContractAddress
|
||||
}
|
||||
return fields, nil
|
||||
return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil
|
||||
}
|
||||
|
||||
// marshalReceipt marshals a transaction receipt into a JSON object.
|
||||
|
|
@ -2172,8 +2255,7 @@ func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber u
|
|||
"logs": receipt.Logs,
|
||||
"logsBloom": receipt.Bloom,
|
||||
"type": hexutil.Uint(tx.Type()),
|
||||
// uncomment below line after EIP-1559
|
||||
// TODO: "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
|
||||
"effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
|
||||
}
|
||||
|
||||
// Assign receipt status or post state.
|
||||
|
|
@ -2279,7 +2361,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra
|
|||
}
|
||||
|
||||
// Set some sanity defaults and terminate on failure
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
if err := args.setDefaults(ctx, s.b, false); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Assemble the transaction and sign with the wallet
|
||||
|
|
@ -2296,11 +2378,12 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Tra
|
|||
return SubmitTransaction(ctx, s.b, signed)
|
||||
}
|
||||
|
||||
// FillTransaction fills the defaults (nonce, gas, gasPrice) on a given unsigned transaction,
|
||||
// and returns it to the caller for further processing (signing + broadcast)
|
||||
// FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields)
|
||||
// on a given unsigned transaction, and returns it to the caller for further
|
||||
// processing (signing + broadcast).
|
||||
func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
|
||||
// Set some sanity defaults and terminate on failure
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
if err := args.setDefaults(ctx, s.b, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Assemble the transaction and obtain rlp
|
||||
|
|
@ -3199,24 +3282,25 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Tra
|
|||
if args.Gas == nil {
|
||||
return nil, errors.New("not specify Gas")
|
||||
}
|
||||
if args.GasPrice == nil {
|
||||
return nil, errors.New("not specify GasPrice")
|
||||
if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
|
||||
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
|
||||
}
|
||||
if args.Nonce == nil {
|
||||
return nil, errors.New("not specify Nonce")
|
||||
}
|
||||
if err := args.setDefaults(ctx, s.b); err != nil {
|
||||
if err := args.setDefaults(ctx, s.b, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Before actually sign the transaction, ensure the transaction fee is reasonable.
|
||||
if err := checkTxFee(args.GasPrice.ToInt(), uint64(*args.Gas), s.b.RPCTxFeeCap()); err != nil {
|
||||
tx := args.toTransaction()
|
||||
if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tx, err := s.sign(args.from(), args.toTransaction())
|
||||
signed, err := s.sign(args.from(), tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := tx.MarshalBinary()
|
||||
data, err := signed.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -3236,11 +3320,12 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err
|
|||
accounts[account.Address] = struct{}{}
|
||||
}
|
||||
}
|
||||
curHeader := s.b.CurrentHeader()
|
||||
transactions := make([]*RPCTransaction, 0, len(pending))
|
||||
for _, tx := range pending {
|
||||
from, _ := types.Sender(s.signer, tx)
|
||||
if _, exists := accounts[from]; exists {
|
||||
transactions = append(transactions, newRPCPendingTransaction(tx))
|
||||
transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()))
|
||||
}
|
||||
}
|
||||
return transactions, nil
|
||||
|
|
@ -3252,7 +3337,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs Transact
|
|||
if sendArgs.Nonce == nil {
|
||||
return common.Hash{}, errors.New("missing transaction nonce in transaction spec")
|
||||
}
|
||||
if err := sendArgs.setDefaults(ctx, s.b); err != nil {
|
||||
if err := sendArgs.setDefaults(ctx, s.b, false); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
matchTx := sendArgs.toTransaction()
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ type Backend interface {
|
|||
// General Ethereum API
|
||||
Downloader() *downloader.Downloader
|
||||
ProtocolVersion() int
|
||||
SuggestPrice(ctx context.Context) (*big.Int, error)
|
||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
||||
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error)
|
||||
ChainDb() ethdb.Database
|
||||
AccountManager() *accounts.Manager
|
||||
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
|
||||
|
|
@ -61,6 +62,7 @@ type Backend interface {
|
|||
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
|
||||
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
||||
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error)
|
||||
CurrentHeader() *types.Header
|
||||
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
|
||||
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
||||
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error)
|
||||
|
|
@ -82,6 +84,7 @@ type Backend interface {
|
|||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
||||
Stats() (pending int, queued int)
|
||||
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
||||
TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions)
|
||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||
|
||||
// Order Pool Transaction
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue