mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-16 01:40:44 +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).
|
// have any code associated with it (i.e. suicided).
|
||||||
ErrNoCode = errors.New("no contract code at given address")
|
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.
|
// on a backend that doesn't implement PendingContractCaller.
|
||||||
ErrNoPendingState = errors.New("backend does not support pending state")
|
ErrNoPendingState = errors.New("backend does not support pending state")
|
||||||
|
|
||||||
// This error is returned by WaitDeployed if contract creation leaves an
|
// ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
|
||||||
// empty contract behind.
|
// an empty contract behind.
|
||||||
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
|
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
|
// 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.
|
// between contract internal errors and the local chain being out of sync.
|
||||||
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
|
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.
|
// input.
|
||||||
CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error)
|
CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +59,7 @@ type ContractCaller interface {
|
||||||
type PendingContractCaller interface {
|
type PendingContractCaller interface {
|
||||||
// PendingCodeAt returns the code of the given account in the pending state.
|
// PendingCodeAt returns the code of the given account in the pending state.
|
||||||
PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
|
PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
|
||||||
|
|
||||||
// PendingCallContract executes an Ethereum contract call against the pending state.
|
// PendingCallContract executes an Ethereum contract call against the pending state.
|
||||||
PendingCallContract(ctx context.Context, call XDPoSChain.CallMsg) ([]byte, error)
|
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
|
// used when the user does not provide some needed values, but rather leaves it up
|
||||||
// to the transactor to decide.
|
// to the transactor to decide.
|
||||||
type ContractTransactor interface {
|
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 returns the code of the given account in the pending state.
|
||||||
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
|
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
|
||||||
|
|
||||||
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
||||||
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
||||||
|
|
||||||
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
||||||
// execution of a transaction.
|
// execution of a transaction.
|
||||||
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
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
|
// EstimateGas tries to estimate the gas needed to execute a specific
|
||||||
// transaction based on the current pending state of the backend blockchain.
|
// 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
|
// 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
|
// transactions may be added or removed by miners, but it should provide a basis
|
||||||
// for setting a reasonable default.
|
// for setting a reasonable default.
|
||||||
EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (gas uint64, err error)
|
EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (gas uint64, err error)
|
||||||
|
|
||||||
// SendTransaction injects the transaction into the pending pool for execution.
|
// SendTransaction injects the transaction into the pending pool for execution.
|
||||||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,19 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
|
||||||
return receipt, nil
|
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.
|
// PendingCodeAt returns the code associated with an account in the pending state.
|
||||||
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
|
|
@ -300,8 +313,17 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
|
// 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) {
|
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
|
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.
|
// callContract implements common code between normal and pending contract calls.
|
||||||
// state is modified during execution, make sure to copy it if necessary.
|
// 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) {
|
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.
|
// Gas prices post 1559 need to be initialized
|
||||||
if call.GasPrice == nil {
|
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||||
call.GasPrice = big.NewInt(1)
|
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 {
|
if call.Gas == 0 {
|
||||||
call.Gas = 50000000
|
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)
|
evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil)
|
||||||
// Create a new environment which holds all relevant information
|
// Create a new environment which holds all relevant information
|
||||||
// about the transaction and calling mechanisms.
|
// 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)
|
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
|
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) From() common.Address { return m.CallMsg.From }
|
||||||
func (m callMsg) Nonce() uint64 { return 0 }
|
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) To() *common.Address { return m.CallMsg.To }
|
||||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
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) Gas() uint64 { return m.CallMsg.Gas }
|
||||||
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
||||||
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
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) {
|
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) {
|
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)
|
Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state)
|
||||||
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
Signer SignerFn // Method to use for signing the transaction (mandatory)
|
||||||
|
|
||||||
Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
|
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)
|
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)
|
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)
|
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
|
// 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)
|
return c.transact(opts, &c.address, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// transact executes an actual transaction invocation, first deriving any missing
|
func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Address, input []byte, head *types.Header) (*types.Transaction, error) {
|
||||||
// authorization fields, and then scheduling the transaction for execution.
|
// Normalize value
|
||||||
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
|
|
||||||
value := opts.Value
|
value := opts.Value
|
||||||
if value == nil {
|
if value == nil {
|
||||||
value = new(big.Int)
|
value = new(big.Int)
|
||||||
}
|
}
|
||||||
var nonce uint64
|
// Estimate TipCap
|
||||||
if opts.Nonce == nil {
|
gasTipCap := opts.GasTipCap
|
||||||
nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
|
if gasTipCap == nil {
|
||||||
|
tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
gasTipCap = tip
|
||||||
nonce = opts.Nonce.Uint64()
|
|
||||||
}
|
}
|
||||||
// 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
|
gasPrice := opts.GasPrice
|
||||||
if gasPrice == nil {
|
if gasPrice == nil {
|
||||||
gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to suggest gas price: %v", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
gasPrice = price
|
||||||
}
|
}
|
||||||
|
// Estimate GasLimit
|
||||||
gasLimit := opts.GasLimit
|
gasLimit := opts.GasLimit
|
||||||
if gasLimit == 0 {
|
if opts.GasLimit == 0 {
|
||||||
// Gas estimation cannot succeed without code for method invocations
|
var err error
|
||||||
if contract != nil {
|
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
|
||||||
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 err != nil {
|
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
|
// create the transaction
|
||||||
var rawTx *types.Transaction
|
nonce, err := c.getNonce(opts)
|
||||||
if contract == nil {
|
if err != nil {
|
||||||
rawTx = types.NewContractCreation(nonce, value, gasLimit, gasPrice, input)
|
return nil, err
|
||||||
} else {
|
|
||||||
rawTx = types.NewTransaction(nonce, c.address, value, gasLimit, gasPrice, input)
|
|
||||||
}
|
}
|
||||||
|
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 {
|
if opts.Signer == nil {
|
||||||
return nil, errors.New("no signer to authorize the transaction with")
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if opts.NoSend {
|
||||||
|
return signedTx, nil
|
||||||
|
}
|
||||||
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
||||||
return nil, err
|
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
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy an interaction tester contract and call a transaction on it
|
||||||
_, _, interactor, err := DeployInteractor(auth, sim, "Deploy string")
|
_, _, interactor, err := DeployInteractor(auth, sim, "Deploy string")
|
||||||
|
|
@ -270,7 +270,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy a tuple tester contract and execute a structured call on it
|
||||||
_, _, getter, err := DeployGetter(auth, sim)
|
_, _, getter, err := DeployGetter(auth, sim)
|
||||||
|
|
@ -302,7 +302,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy a tuple tester contract and execute a structured call on it
|
||||||
_, _, tupler, err := DeployTupler(auth, sim)
|
_, _, tupler, err := DeployTupler(auth, sim)
|
||||||
|
|
@ -344,7 +344,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy a slice tester contract and execute a n array call on it
|
||||||
_, _, slicer, err := DeploySlicer(auth, sim)
|
_, _, slicer, err := DeploySlicer(auth, sim)
|
||||||
|
|
@ -378,7 +378,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy a default method invoker contract and execute its default method
|
||||||
_, _, defaulter, err := DeployDefaulter(auth, sim)
|
_, _, defaulter, err := DeployDefaulter(auth, sim)
|
||||||
|
|
@ -447,7 +447,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy a funky gas pattern contract
|
||||||
_, _, limiter, err := DeployFunkyGasPattern(auth, sim)
|
_, _, limiter, err := DeployFunkyGasPattern(auth, sim)
|
||||||
|
|
@ -482,7 +482,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy a sender tester contract and execute a structured call on it
|
||||||
_, _, callfrom, err := DeployCallFrom(auth, sim)
|
_, _, callfrom, err := DeployCallFrom(auth, sim)
|
||||||
|
|
@ -542,7 +542,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy a underscorer tester contract and execute a structured call on it
|
||||||
_, _, underscorer, err := DeployUnderscorer(auth, sim)
|
_, _, underscorer, err := DeployUnderscorer(auth, sim)
|
||||||
|
|
@ -612,7 +612,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
// Deploy an eventer contract
|
||||||
_, _, eventer, err := DeployEventer(auth, sim)
|
_, _, eventer, err := DeployEventer(auth, sim)
|
||||||
|
|
@ -761,7 +761,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
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
|
//deploy the test contract
|
||||||
_, _, testContract, err := DeployDeeplyNestedArray(auth, sim)
|
_, _, testContract, err := DeployDeeplyNestedArray(auth, sim)
|
||||||
|
|
|
||||||
|
|
@ -53,15 +53,20 @@ var waitDeployedTests = map[string]struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWaitDeployed(t *testing.T) {
|
func TestWaitDeployed(t *testing.T) {
|
||||||
|
config := *params.TestXDPoSMockChainConfig
|
||||||
|
config.Eip1559Block = big.NewInt(0)
|
||||||
for name, test := range waitDeployedTests {
|
for name, test := range waitDeployedTests {
|
||||||
backend := backends.NewXDCSimulatedBackend(
|
backend := backends.NewXDCSimulatedBackend(
|
||||||
core.GenesisAlloc{
|
core.GenesisAlloc{
|
||||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
|
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(100000000000000000)},
|
||||||
}, 10000000, params.TestXDPoSMockChainConfig,
|
}, 10000000, &config,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create the transaction.
|
// Create the transaction
|
||||||
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))
|
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)
|
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
|
|
||||||
// Wait for it to get mined in the background.
|
// 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/XDPoS"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||||
|
|
@ -261,12 +262,12 @@ var (
|
||||||
TxPoolJournalFlag = cli.StringFlag{
|
TxPoolJournalFlag = cli.StringFlag{
|
||||||
Name: "txpool.journal",
|
Name: "txpool.journal",
|
||||||
Usage: "Disk journal for local transaction to survive node restarts",
|
Usage: "Disk journal for local transaction to survive node restarts",
|
||||||
Value: core.DefaultTxPoolConfig.Journal,
|
Value: txpool.DefaultConfig.Journal,
|
||||||
}
|
}
|
||||||
TxPoolRejournalFlag = cli.DurationFlag{
|
TxPoolRejournalFlag = cli.DurationFlag{
|
||||||
Name: "txpool.rejournal",
|
Name: "txpool.rejournal",
|
||||||
Usage: "Time interval to regenerate the local transaction journal",
|
Usage: "Time interval to regenerate the local transaction journal",
|
||||||
Value: core.DefaultTxPoolConfig.Rejournal,
|
Value: txpool.DefaultConfig.Rejournal,
|
||||||
}
|
}
|
||||||
TxPoolPriceLimitFlag = cli.Uint64Flag{
|
TxPoolPriceLimitFlag = cli.Uint64Flag{
|
||||||
Name: "txpool.pricelimit",
|
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
|
// If we are running the light client, apply another group
|
||||||
// settings for gas oracle.
|
// settings for gas oracle.
|
||||||
if light {
|
if light {
|
||||||
cfg.Blocks = ethconfig.LightClientGPO.Blocks
|
*cfg = ethconfig.LightClientGPO
|
||||||
cfg.Percentile = ethconfig.LightClientGPO.Percentile
|
|
||||||
}
|
}
|
||||||
if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
|
if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
|
||||||
cfg.Blocks = ctx.GlobalInt(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) {
|
if ctx.GlobalIsSet(TxPoolNoLocalsFlag.Name) {
|
||||||
cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name)
|
cfg.NoLocals = ctx.GlobalBool(TxPoolNoLocalsFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ var BerlinBlock = big.NewInt(16832700)
|
||||||
var LondonBlock = big.NewInt(16832700)
|
var LondonBlock = big.NewInt(16832700)
|
||||||
var MergeBlock = big.NewInt(16832700)
|
var MergeBlock = big.NewInt(16832700)
|
||||||
var ShanghaiBlock = big.NewInt(16832700)
|
var ShanghaiBlock = big.NewInt(16832700)
|
||||||
var Eip1559Block = big.NewInt(9999999999)
|
var Eip1559Block = big.NewInt(23580000)
|
||||||
|
|
||||||
var TIPXDCXTestnet = big.NewInt(0)
|
var TIPXDCXTestnet = big.NewInt(0)
|
||||||
var IsTestnet bool = false
|
var IsTestnet bool = false
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import "math/big"
|
||||||
|
|
||||||
var MinGasPrice50x = big.NewInt(12500000000)
|
var MinGasPrice50x = big.NewInt(12500000000)
|
||||||
var GasPrice50x = big.NewInt(12500000000)
|
var GasPrice50x = big.NewInt(12500000000)
|
||||||
|
var BaseFee = big.NewInt(12500000000)
|
||||||
|
|
||||||
func GetGasFee(blockNumber, gas uint64) *big.Int {
|
func GetGasFee(blockNumber, gas uint64) *big.Int {
|
||||||
fee := new(big.Int).SetUint64(gas)
|
fee := new(big.Int).SetUint64(gas)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"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() {
|
if parent.Time.Uint64()+x.config.Period > header.Time.Uint64() {
|
||||||
return utils.ErrInvalidTimestamp
|
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 {
|
if number%x.config.Epoch != 0 {
|
||||||
return x.verifySeal(chain, header, parents, fullVerify)
|
return x.verifySeal(chain, header, parents, fullVerify)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
lru "github.com/hashicorp/golang-lru"
|
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) {
|
func sigHash(header *types.Header) (hash common.Hash) {
|
||||||
hasher := sha3.NewKeccak256()
|
hasher := sha3.NewKeccak256()
|
||||||
|
|
||||||
err := rlp.Encode(hasher, []interface{}{
|
enc := []interface{}{
|
||||||
header.ParentHash,
|
header.ParentHash,
|
||||||
header.UncleHash,
|
header.UncleHash,
|
||||||
header.Coinbase,
|
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.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
|
||||||
header.MixDigest,
|
header.MixDigest,
|
||||||
header.Nonce,
|
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])
|
hasher.Sum(hash[:0])
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import (
|
||||||
func sigHash(header *types.Header) (hash common.Hash) {
|
func sigHash(header *types.Header) (hash common.Hash) {
|
||||||
hasher := sha3.NewKeccak256()
|
hasher := sha3.NewKeccak256()
|
||||||
|
|
||||||
err := rlp.Encode(hasher, []interface{}{
|
enc := []interface{}{
|
||||||
header.ParentHash,
|
header.ParentHash,
|
||||||
header.UncleHash,
|
header.UncleHash,
|
||||||
header.Coinbase,
|
header.Coinbase,
|
||||||
|
|
@ -38,10 +38,11 @@ func sigHash(header *types.Header) (hash common.Hash) {
|
||||||
header.Nonce,
|
header.Nonce,
|
||||||
header.Validators,
|
header.Validators,
|
||||||
header.Penalties,
|
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])
|
hasher.Sum(hash[:0])
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"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 {
|
if header.UncleHash != utils.UncleHash {
|
||||||
return utils.ErrInvalidUncleHash
|
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 {
|
if header.Difficulty.Cmp(big.NewInt(1)) != 0 {
|
||||||
return utils.ErrInvalidDifficulty
|
return utils.ErrInvalidDifficulty
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ type SignerFn func(accounts.Account, []byte) ([]byte, error)
|
||||||
func sigHash(header *types.Header) (hash common.Hash) {
|
func sigHash(header *types.Header) (hash common.Hash) {
|
||||||
hasher := sha3.NewKeccak256()
|
hasher := sha3.NewKeccak256()
|
||||||
|
|
||||||
rlp.Encode(hasher, []interface{}{
|
enc := []interface{}{
|
||||||
header.ParentHash,
|
header.ParentHash,
|
||||||
header.UncleHash,
|
header.UncleHash,
|
||||||
header.Coinbase,
|
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.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
|
||||||
header.MixDigest,
|
header.MixDigest,
|
||||||
header.Nonce,
|
header.Nonce,
|
||||||
})
|
}
|
||||||
|
if header.BaseFee != nil {
|
||||||
|
enc = append(enc, header.BaseFee)
|
||||||
|
}
|
||||||
|
rlp.Encode(hasher, enc)
|
||||||
hasher.Sum(hash[:0])
|
hasher.Sum(hash[:0])
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
|
@ -253,6 +254,10 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
|
||||||
if header.GasUsed > header.GasLimit {
|
if header.GasUsed > header.GasLimit {
|
||||||
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", 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
|
// Verify that the gas limit remains within allowed bounds
|
||||||
diff := int64(parent.GasLimit) - int64(header.GasLimit)
|
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"
|
randomizeContract "github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
|
|
@ -60,7 +61,7 @@ type RewardLog struct {
|
||||||
var TxSignMu sync.RWMutex
|
var TxSignMu sync.RWMutex
|
||||||
|
|
||||||
// Send tx sign for block number to smart contract blockSigner.
|
// 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()
|
TxSignMu.Lock()
|
||||||
defer TxSignMu.Unlock()
|
defer TxSignMu.Unlock()
|
||||||
if chainConfig.XDPoS != nil {
|
if chainConfig.XDPoS != nil {
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||||
return func(i int, gen *BlockGen) {
|
return func(i int, gen *BlockGen) {
|
||||||
toaddr := common.Address{}
|
toaddr := common.Address{}
|
||||||
data := make([]byte, nbytes)
|
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)
|
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,15 +60,15 @@ var (
|
||||||
CheckpointCh = make(chan int)
|
CheckpointCh = make(chan int)
|
||||||
ErrNoGenesis = errors.New("Genesis not found in chain")
|
ErrNoGenesis = errors.New("Genesis not found in chain")
|
||||||
|
|
||||||
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
|
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
|
||||||
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
|
||||||
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
||||||
blockReorgInvalidatedTx = metrics.NewRegisteredMeter("chain/reorg/invalidTx", nil)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bodyCacheLimit = 256
|
bodyCacheLimit = 256
|
||||||
blockCacheLimit = 256
|
blockCacheLimit = 256
|
||||||
|
receiptsCacheLimit = 32
|
||||||
maxFutureBlocks = 256
|
maxFutureBlocks = 256
|
||||||
maxTimeFutureBlocks = 30
|
maxTimeFutureBlocks = 30
|
||||||
badBlockLimit = 10
|
badBlockLimit = 10
|
||||||
|
|
@ -142,6 +142,7 @@ type BlockChain struct {
|
||||||
|
|
||||||
bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies
|
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
|
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
|
blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks
|
||||||
resultProcess *lru.Cache[common.Hash, *ResultProcessBlock] // Cache for processed blocks
|
resultProcess *lru.Cache[common.Hash, *ResultProcessBlock] // Cache for processed blocks
|
||||||
calculatingBlock *lru.Cache[common.Hash, *CalculatedBlock] // Cache for processing 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{}),
|
quit: make(chan struct{}),
|
||||||
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
||||||
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](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),
|
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
||||||
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
||||||
resultProcess: lru.NewCache[common.Hash, *ResultProcessBlock](blockCacheLimit),
|
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
|
// Clear out any stale content from the caches
|
||||||
bc.bodyCache.Purge()
|
bc.bodyCache.Purge()
|
||||||
bc.bodyRLPCache.Purge()
|
bc.bodyRLPCache.Purge()
|
||||||
|
bc.receiptsCache.Purge()
|
||||||
bc.blockCache.Purge()
|
bc.blockCache.Purge()
|
||||||
bc.futureBlocks.Purge()
|
bc.futureBlocks.Purge()
|
||||||
bc.blocksHashCache.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.
|
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
|
||||||
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
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.
|
// 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
|
// InsertReceiptChain attempts to complete an already existing header chain with
|
||||||
// transaction and receipt data.
|
// transaction and receipt data.
|
||||||
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
|
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 {
|
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
blockHash, blockNumber := block.Hash(), block.NumberU64()
|
||||||
// Short circuit if the owner header is unknown
|
// Short circuit if the owner header is unknown
|
||||||
if !bc.HasHeader(block.Hash(), block.NumberU64()) {
|
if !bc.HasHeader(blockHash, blockNumber) {
|
||||||
return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4])
|
return i, fmt.Errorf("containing header #%d [%x…] unknown", blockNumber, blockHash.Bytes()[:4])
|
||||||
}
|
}
|
||||||
// Skip if the entire data is already known
|
// Skip if the entire data is already known
|
||||||
if bc.HasBlock(block.Hash(), block.NumberU64()) {
|
if bc.HasBlock(blockHash, blockNumber) {
|
||||||
stats.ignored++
|
stats.ignored++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Compute all the non-consensus fields of the receipts
|
// 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)
|
return i, fmt.Errorf("failed to set receipts data: %v", err)
|
||||||
}
|
}
|
||||||
// Write all the data out into the database
|
// Write all the data out into the database
|
||||||
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
|
rawdb.WriteBody(batch, blockHash, blockNumber, block.Body())
|
||||||
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
|
if err := WriteBlockReceipts(batch, blockHash, blockNumber, receipts); err != nil {
|
||||||
return i, fmt.Errorf("failed to write block receipts: %v", err)
|
return i, fmt.Errorf("failed to write block receipts: %v", err)
|
||||||
}
|
}
|
||||||
if err := WriteTxLookupEntries(batch, block); err != nil {
|
if err := WriteTxLookupEntries(batch, block); err != nil {
|
||||||
|
|
@ -1482,7 +1455,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
defer close(abort)
|
defer close(abort)
|
||||||
|
|
||||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
// 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
|
// Iterate over the blocks and insert when the verifier permits
|
||||||
for i, block := range chain {
|
for i, block := range chain {
|
||||||
|
|
@ -2131,6 +2104,25 @@ func countTransactions(chain []*types.Block) (c int) {
|
||||||
return c
|
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
|
// 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
|
// to be part of the new canonical chain and accumulates potential missing transactions and post an
|
||||||
// event about them
|
// event about them
|
||||||
|
|
@ -2141,20 +2133,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||||
commonBlock *types.Block
|
commonBlock *types.Block
|
||||||
deletedTxs types.Transactions
|
deletedTxs types.Transactions
|
||||||
deletedLogs []*types.Log
|
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())
|
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) {
|
for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) {
|
||||||
oldChain = append(oldChain, oldBlock)
|
oldChain = append(oldChain, oldBlock)
|
||||||
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
||||||
|
if logs := bc.collectLogs(oldBlock, true); len(logs) > 0 {
|
||||||
collectLogs(oldBlock.Hash())
|
deletedLogs = append(deletedLogs, logs...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// reduce new chain and append new chain blocks for inserting later on
|
// 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)
|
oldChain = append(oldChain, oldBlock)
|
||||||
newChain = append(newChain, newBlock)
|
newChain = append(newChain, newBlock)
|
||||||
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
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)
|
oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1)
|
||||||
if oldBlock == nil {
|
if oldBlock == nil {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
@ -555,10 +556,11 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
gendb = rawdb.NewMemoryDatabase()
|
gendb = rawdb.NewMemoryDatabase()
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000000000)
|
||||||
gspec = &Genesis{
|
gspec = &Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(gendb)
|
genesis = gspec.MustCommit(gendb)
|
||||||
signer = types.LatestSigner(gspec.Config)
|
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 the block number is multiple of 3, send a few bonus transactions to the miner
|
||||||
if i%3 == 2 {
|
if i%3 == 2 {
|
||||||
for j := 0; j < i%4+1; j++ {
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -643,8 +645,12 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
gendb = rawdb.NewMemoryDatabase()
|
gendb = rawdb.NewMemoryDatabase()
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000000000)
|
||||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
gspec = &Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
}
|
||||||
genesis = gspec.MustCommit(gendb)
|
genesis = gspec.MustCommit(gendb)
|
||||||
)
|
)
|
||||||
height := uint64(1024)
|
height := uint64(1024)
|
||||||
|
|
@ -730,9 +736,9 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
GasLimit: 3141592,
|
GasLimit: 3141592,
|
||||||
Alloc: GenesisAlloc{
|
Alloc: GenesisAlloc{
|
||||||
addr1: {Balance: big.NewInt(1000000)},
|
addr1: {Balance: big.NewInt(1000000000000000)},
|
||||||
addr2: {Balance: big.NewInt(1000000)},
|
addr2: {Balance: big.NewInt(1000000000000000)},
|
||||||
addr3: {Balance: big.NewInt(1000000)},
|
addr3: {Balance: big.NewInt(1000000000000000)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
|
|
@ -742,8 +748,8 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
// Create two transactions shared between the chains:
|
// Create two transactions shared between the chains:
|
||||||
// - postponed: transaction included at a later block in the forked chain
|
// - postponed: transaction included at a later block in the forked chain
|
||||||
// - swapped: transaction included at the same block number 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)
|
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, nil, 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:
|
// Create two transactions that will be dropped by the forked chain:
|
||||||
// - pastDrop: transaction dropped retroactively from a past block
|
// - 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) {
|
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
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(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
|
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
|
||||||
|
|
||||||
case 2:
|
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(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
|
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) {
|
chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
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
|
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
|
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
|
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
|
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
|
||||||
|
|
||||||
case 3:
|
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
|
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -840,7 +846,7 @@ func TestLogReorgs(t *testing.T) {
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
// this code generates a log
|
// this code generates a log
|
||||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
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)
|
genesis = gspec.MustCommit(db)
|
||||||
signer = types.LatestSigner(gspec.Config)
|
signer = types.LatestSigner(gspec.Config)
|
||||||
)
|
)
|
||||||
|
|
@ -852,7 +858,7 @@ func TestLogReorgs(t *testing.T) {
|
||||||
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
||||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
||||||
if i == 1 {
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tx: %v", err)
|
t.Fatalf("failed to create tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -886,7 +892,7 @@ func TestLogReorgs(t *testing.T) {
|
||||||
// addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
// addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
// gspec = &Genesis{
|
// gspec = &Genesis{
|
||||||
// Config: params.TestChainConfig,
|
// Config: params.TestChainConfig,
|
||||||
// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}},
|
// Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
|
||||||
// }
|
// }
|
||||||
// genesis = gspec.MustCommit(db)
|
// genesis = gspec.MustCommit(db)
|
||||||
// signer = types.LatestSigner(gspec.Config)
|
// 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) {
|
// 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 {
|
// if i == 2 {
|
||||||
// gen.OffsetTime(-9)
|
// gen.OffsetTime(-9)
|
||||||
// }
|
// }
|
||||||
|
|
@ -1107,8 +1113,8 @@ func TestEIP155Transition(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
_, err := blockchain.InsertChain(blocks)
|
_, err := blockchain.InsertChain(blocks)
|
||||||
if err != types.ErrInvalidChainId {
|
if have, want := err, types.ErrInvalidChainId; !errors.Is(have, want) {
|
||||||
t.Error("expected error:", types.ErrInvalidChainId)
|
t.Errorf("have %v, want %v", have, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1188,7 +1194,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
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}) })
|
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
|
// 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
|
// Import the canonical and fork chain side by side, verifying the current block
|
||||||
// and current header consistency
|
// and current header consistency
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
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{})
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1233,7 +1239,7 @@ func TestTrieForkGC(t *testing.T) {
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
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}) })
|
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
|
// 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
|
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
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{})
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1279,7 +1285,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
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}) })
|
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}) })
|
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
|
// Import the shared chain and the original canonical one
|
||||||
diskdb := rawdb.NewMemoryDatabase()
|
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{})
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1431,8 +1437,9 @@ func TestEIP2718Transition(t *testing.T) {
|
||||||
// A sender who makes transactions, has some funds
|
// A sender who makes transactions, has some funds
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
// funds = big.NewInt(math.MaxInt64)
|
||||||
gspec = &Genesis{
|
funds = big.NewInt(1000000000000000)
|
||||||
|
gspec = &Genesis{
|
||||||
Config: ¶ms.ChainConfig{
|
Config: ¶ms.ChainConfig{
|
||||||
ChainId: new(big.Int).SetBytes([]byte("eip1559")),
|
ChainId: new(big.Int).SetBytes([]byte("eip1559")),
|
||||||
HomesteadBlock: big.NewInt(0),
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
|
@ -1458,7 +1465,7 @@ func TestEIP2718Transition(t *testing.T) {
|
||||||
byte(vm.SLOAD),
|
byte(vm.SLOAD),
|
||||||
},
|
},
|
||||||
Nonce: 0,
|
Nonce: 0,
|
||||||
Balance: big.NewInt(0),
|
Balance: big.NewInt(50000000000),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -1475,7 +1482,7 @@ func TestEIP2718Transition(t *testing.T) {
|
||||||
Nonce: 0,
|
Nonce: 0,
|
||||||
To: &aa,
|
To: &aa,
|
||||||
Gas: 30000,
|
Gas: 30000,
|
||||||
GasPrice: big.NewInt(1),
|
GasPrice: b.header.BaseFee,
|
||||||
AccessList: types.AccessList{{
|
AccessList: types.AccessList{{
|
||||||
Address: aa,
|
Address: aa,
|
||||||
StorageKeys: []common.Hash{{0}},
|
StorageKeys: []common.Hash{{0}},
|
||||||
|
|
@ -1499,7 +1506,8 @@ func TestEIP2718Transition(t *testing.T) {
|
||||||
block := chain.GetBlockByNumber(1)
|
block := chain.GetBlockByNumber(1)
|
||||||
|
|
||||||
// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
|
// 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 {
|
if block.GasUsed() != expected {
|
||||||
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
|
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
"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/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
|
|
@ -127,6 +127,11 @@ func (b *BlockGen) Number() *big.Int {
|
||||||
return new(big.Int).Set(b.header.Number)
|
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
|
// AddUncheckedReceipt forcefully adds a receipts to the block without a
|
||||||
// backing transaction.
|
// 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
|
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())),
|
Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())),
|
||||||
ParentHash: parent.Hash(),
|
ParentHash: parent.Hash(),
|
||||||
Coinbase: parent.Coinbase(),
|
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),
|
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||||
Time: time,
|
Time: time,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header.BaseFee = eip1559.CalcBaseFee(chain.Config(), header)
|
||||||
|
|
||||||
|
return header
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCanonical creates a chain database, and injects a deterministic canonical
|
// 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
|
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
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"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
|
// Generate a common prefix for both pro-forkers and non-forkers
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
gspec := new(Genesis)
|
gspec := &Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}
|
||||||
genesis := gspec.MustCommit(db)
|
genesis := gspec.MustCommit(db)
|
||||||
prefix, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
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 is returned when a block to import is already known locally.
|
||||||
ErrKnownBlock = errors.New("block already known")
|
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 is returned if a block to import is on the blacklist.
|
||||||
ErrBlacklistedHash = errors.New("blacklisted hash")
|
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
|
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
||||||
// next one expected based on the local chain.
|
// next one expected based on the local chain.
|
||||||
ErrNonceTooHigh = errors.New("nonce too high")
|
ErrNonceTooHigh = errors.New("nonce too high")
|
||||||
|
|
@ -41,16 +41,51 @@ var (
|
||||||
// maximum allowed value and would become invalid if incremented.
|
// maximum allowed value and would become invalid if incremented.
|
||||||
ErrNonceMax = errors.New("nonce has max value")
|
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
|
// ErrTxTypeNotSupported is returned if a transaction is not supported in the
|
||||||
// current network configuration.
|
// current network configuration.
|
||||||
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported
|
ErrTxTypeNotSupported = types.ErrTxTypeNotSupported
|
||||||
|
|
||||||
// ErrGasUintOverflow is returned when calculating gas usage.
|
// ErrTipAboveFeeCap is a sanity error to ensure no one is able to specify a
|
||||||
ErrGasUintOverflow = errors.New("gas uint64 overflow")
|
// 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
|
// If we don't have an explicit author (i.e. not mining), extract from the header
|
||||||
var (
|
var (
|
||||||
beneficiary common.Address
|
beneficiary common.Address
|
||||||
|
baseFee *big.Int
|
||||||
random common.Hash
|
random common.Hash
|
||||||
)
|
)
|
||||||
if author == nil {
|
if author == nil {
|
||||||
|
|
@ -38,6 +39,9 @@ func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, auth
|
||||||
} else {
|
} else {
|
||||||
beneficiary = *author
|
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
|
// since xdpos chain do not use difficulty and mixdigest, we use hash of the block number as random
|
||||||
random = crypto.Keccak256Hash(header.Number.Bytes())
|
random = crypto.Keccak256Hash(header.Number.Bytes())
|
||||||
return vm.BlockContext{
|
return vm.BlockContext{
|
||||||
|
|
@ -48,6 +52,7 @@ func NewEVMBlockContext(header *types.Header, chain consensus.ChainContext, auth
|
||||||
BlockNumber: new(big.Int).Set(header.Number),
|
BlockNumber: new(big.Int).Set(header.Number),
|
||||||
Time: new(big.Int).Set(header.Time),
|
Time: new(big.Int).Set(header.Time),
|
||||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||||
|
BaseFee: baseFee,
|
||||||
GasLimit: header.GasLimit,
|
GasLimit: header.GasLimit,
|
||||||
Random: &random,
|
Random: &random,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
var _ = (*genesisSpecMarshaling)(nil)
|
var _ = (*genesisSpecMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
func (g Genesis) MarshalJSON() ([]byte, error) {
|
func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||||
type Genesis struct {
|
type Genesis struct {
|
||||||
Config *params.ChainConfig `json:"config"`
|
Config *params.ChainConfig `json:"config"`
|
||||||
|
|
@ -29,6 +30,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||||
Number math.HexOrDecimal64 `json:"number"`
|
Number math.HexOrDecimal64 `json:"number"`
|
||||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
ParentHash common.Hash `json:"parentHash"`
|
ParentHash common.Hash `json:"parentHash"`
|
||||||
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||||
}
|
}
|
||||||
var enc Genesis
|
var enc Genesis
|
||||||
enc.Config = g.Config
|
enc.Config = g.Config
|
||||||
|
|
@ -48,9 +50,11 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||||
enc.Number = math.HexOrDecimal64(g.Number)
|
enc.Number = math.HexOrDecimal64(g.Number)
|
||||||
enc.GasUsed = math.HexOrDecimal64(g.GasUsed)
|
enc.GasUsed = math.HexOrDecimal64(g.GasUsed)
|
||||||
enc.ParentHash = g.ParentHash
|
enc.ParentHash = g.ParentHash
|
||||||
|
enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee)
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||||
type Genesis struct {
|
type Genesis struct {
|
||||||
Config *params.ChainConfig `json:"config"`
|
Config *params.ChainConfig `json:"config"`
|
||||||
|
|
@ -65,6 +69,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||||
Number *math.HexOrDecimal64 `json:"number"`
|
Number *math.HexOrDecimal64 `json:"number"`
|
||||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
ParentHash *common.Hash `json:"parentHash"`
|
ParentHash *common.Hash `json:"parentHash"`
|
||||||
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||||
}
|
}
|
||||||
var dec Genesis
|
var dec Genesis
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
|
@ -112,5 +117,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||||
if dec.ParentHash != nil {
|
if dec.ParentHash != nil {
|
||||||
g.ParentHash = *dec.ParentHash
|
g.ParentHash = *dec.ParentHash
|
||||||
}
|
}
|
||||||
|
if dec.BaseFee != nil {
|
||||||
|
g.BaseFee = (*big.Int)(dec.BaseFee)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
var _ = (*genesisAccountMarshaling)(nil)
|
var _ = (*genesisAccountMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
func (g GenesisAccount) MarshalJSON() ([]byte, error) {
|
func (g GenesisAccount) MarshalJSON() ([]byte, error) {
|
||||||
type GenesisAccount struct {
|
type GenesisAccount struct {
|
||||||
Code hexutil.Bytes `json:"code,omitempty"`
|
Code hexutil.Bytes `json:"code,omitempty"`
|
||||||
|
|
@ -36,6 +37,7 @@ func (g GenesisAccount) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
|
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
|
||||||
type GenesisAccount struct {
|
type GenesisAccount struct {
|
||||||
Code *hexutil.Bytes `json:"code,omitempty"`
|
Code *hexutil.Bytes `json:"code,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ type Genesis struct {
|
||||||
Number uint64 `json:"number"`
|
Number uint64 `json:"number"`
|
||||||
GasUsed uint64 `json:"gasUsed"`
|
GasUsed uint64 `json:"gasUsed"`
|
||||||
ParentHash common.Hash `json:"parentHash"`
|
ParentHash common.Hash `json:"parentHash"`
|
||||||
|
BaseFee *big.Int `json:"baseFeePerGas"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenesisAlloc specifies the initial state that is part of the genesis block.
|
// GenesisAlloc specifies the initial state that is part of the genesis block.
|
||||||
|
|
@ -96,6 +97,7 @@ type genesisSpecMarshaling struct {
|
||||||
GasUsed math.HexOrDecimal64
|
GasUsed math.HexOrDecimal64
|
||||||
Number math.HexOrDecimal64
|
Number math.HexOrDecimal64
|
||||||
Difficulty *math.HexOrDecimal256
|
Difficulty *math.HexOrDecimal256
|
||||||
|
BaseFee *math.HexOrDecimal256
|
||||||
Alloc map[common.UnprefixedAddress]GenesisAccount
|
Alloc map[common.UnprefixedAddress]GenesisAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,6 +255,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||||
Extra: g.ExtraData,
|
Extra: g.ExtraData,
|
||||||
GasLimit: g.GasLimit,
|
GasLimit: g.GasLimit,
|
||||||
GasUsed: g.GasUsed,
|
GasUsed: g.GasUsed,
|
||||||
|
BaseFee: g.BaseFee,
|
||||||
Difficulty: g.Difficulty,
|
Difficulty: g.Difficulty,
|
||||||
MixDigest: g.Mixhash,
|
MixDigest: g.Mixhash,
|
||||||
Coinbase: g.Coinbase,
|
Coinbase: g.Coinbase,
|
||||||
|
|
@ -264,6 +267,13 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||||
if g.Difficulty == nil {
|
if g.Difficulty == nil {
|
||||||
head.Difficulty = params.GenesisDifficulty
|
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.Commit(false)
|
||||||
statedb.Database().TrieDB().Commit(root, true)
|
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.
|
// 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 {
|
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)
|
return g.MustCommit(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -365,6 +378,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
|
||||||
Config: &config,
|
Config: &config,
|
||||||
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...),
|
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...),
|
||||||
GasLimit: 6283185,
|
GasLimit: 6283185,
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
Difficulty: big.NewInt(1),
|
Difficulty: big.NewInt(1),
|
||||||
Alloc: map[common.Address]GenesisAccount{
|
Alloc: map[common.Address]GenesisAccount{
|
||||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
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"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"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.
|
// ReadHeaderNumber returns the header number assigned to a hash.
|
||||||
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
|
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
|
||||||
data, _ := db.Get(headerNumberKey(hash))
|
data, _ := db.Get(headerNumberKey(hash))
|
||||||
|
|
@ -55,6 +47,15 @@ func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
|
||||||
return &number
|
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.
|
// WriteHeadBlockHash stores the head block's hash.
|
||||||
func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||||
if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
|
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-
|
// WriteHeader stores a block header into the database and also stores the hash-
|
||||||
// to-number mapping.
|
// to-number mapping.
|
||||||
func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) {
|
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))
|
receipts := make(types.Receipts, len(storageReceipts))
|
||||||
for i, storageReceipt := range storageReceipts {
|
for i, storageReceipt := range storageReceipts {
|
||||||
receipts[i] = (*types.Receipt)(storageReceipt)
|
receipts[i] = (*types.Receipt)(storageReceipt)
|
||||||
|
receipts[i].BlockHash = hash
|
||||||
|
receipts[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||||
|
receipts[i].TransactionIndex = uint(i)
|
||||||
}
|
}
|
||||||
return receipts
|
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)
|
log.Error("Missing body but have receipt", "hash", hash, "number", number)
|
||||||
return nil
|
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)
|
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ import (
|
||||||
|
|
||||||
// The fields below define the low level database schema prefixing.
|
// The fields below define the low level database schema prefixing.
|
||||||
var (
|
var (
|
||||||
|
// headBlockKey tracks the latest known full block's hash.
|
||||||
headBlockKey = []byte("LastBlock")
|
headBlockKey = []byte("LastBlock")
|
||||||
|
|
||||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
// 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
|
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||||
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
|
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// senderCacher is a concurrent tranaction sender recoverer anc cacher.
|
// SenderCacher is a concurrent transaction sender recoverer and cacher.
|
||||||
var senderCacher = newTxSenderCacher(runtime.NumCPU())
|
var SenderCacher = newTxSenderCacher(runtime.NumCPU())
|
||||||
|
|
||||||
// txSenderCacherRequest is a request for recovering transaction senders with a
|
// txSenderCacherRequest is a request for recovering transaction senders with a
|
||||||
// specific signature scheme and caching it into the transactions themselves.
|
// 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
|
// 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.
|
// 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 there's nothing to recover, abort
|
||||||
if len(txs) == 0 {
|
if len(txs) == 0 {
|
||||||
return
|
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
|
// 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.
|
// 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
|
count := 0
|
||||||
for _, block := range blocks {
|
for _, block := range blocks {
|
||||||
count += len(block.Transactions())
|
count += len(block.Transactions())
|
||||||
|
|
@ -101,5 +101,5 @@ func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*t
|
||||||
for _, block := range blocks {
|
for _, block := range blocks {
|
||||||
txs = append(txs, block.Transactions()...)
|
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) {
|
func (s *StateDB) Prepare(thash common.Hash, ti int) {
|
||||||
s.thash = thash
|
s.thash = thash
|
||||||
s.txIndex = ti
|
s.txIndex = ti
|
||||||
s.accessList = newAccessList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSuicides flags the suicided objects for deletion so that it
|
// 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.
|
// 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) {
|
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)
|
s.AddAddressToAccessList(sender)
|
||||||
if dst != nil {
|
if dst != nil {
|
||||||
s.AddAddressToAccessList(*dst)
|
s.AddAddressToAccessList(*dst)
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statedb.Prepare(tx.Hash(), i)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -198,7 +198,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statedb.Prepare(tx.Hash(), i)
|
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 {
|
if err != nil {
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +220,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
||||||
return receipts, allLogs, *usedGas, nil
|
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()
|
to := tx.To()
|
||||||
if to != nil {
|
if to != nil {
|
||||||
if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) {
|
if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) {
|
||||||
|
|
@ -246,7 +246,8 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
balanceFee = value
|
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 {
|
if err != nil {
|
||||||
return nil, 0, err, false
|
return nil, 0, err, false
|
||||||
}
|
}
|
||||||
|
|
@ -465,7 +466,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
blockContext := NewEVMBlockContext(header, bc, author)
|
blockContext := NewEVMBlockContext(header, bc, author)
|
||||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg)
|
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg)
|
||||||
coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author)
|
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) {
|
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"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
cmath "github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var emptyCodeHash = crypto.Keccak256Hash(nil)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
|
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
|
||||||
)
|
)
|
||||||
|
|
@ -57,6 +61,8 @@ type StateTransition struct {
|
||||||
msg Message
|
msg Message
|
||||||
gas uint64
|
gas uint64
|
||||||
gasPrice *big.Int
|
gasPrice *big.Int
|
||||||
|
gasFeeCap *big.Int
|
||||||
|
gasTipCap *big.Int
|
||||||
initialGas uint64
|
initialGas uint64
|
||||||
value *big.Int
|
value *big.Int
|
||||||
data []byte
|
data []byte
|
||||||
|
|
@ -71,18 +77,20 @@ type Message interface {
|
||||||
To() *common.Address
|
To() *common.Address
|
||||||
|
|
||||||
GasPrice() *big.Int
|
GasPrice() *big.Int
|
||||||
|
GasFeeCap() *big.Int
|
||||||
|
GasTipCap() *big.Int
|
||||||
Gas() uint64
|
Gas() uint64
|
||||||
Value() *big.Int
|
Value() *big.Int
|
||||||
|
|
||||||
Nonce() uint64
|
Nonce() uint64
|
||||||
CheckNonce() bool
|
IsFake() bool
|
||||||
Data() []byte
|
Data() []byte
|
||||||
BalanceTokenFee() *big.Int
|
BalanceTokenFee() *big.Int
|
||||||
AccessList() types.AccessList
|
AccessList() types.AccessList
|
||||||
}
|
}
|
||||||
|
|
||||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
// 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
|
// Set the starting gas for the raw transaction
|
||||||
var gas uint64
|
var gas uint64
|
||||||
if isContractCreation && isHomestead {
|
if isContractCreation && isHomestead {
|
||||||
|
|
@ -90,8 +98,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
|
||||||
} else {
|
} else {
|
||||||
gas = params.TxGas
|
gas = params.TxGas
|
||||||
}
|
}
|
||||||
|
dataLen := uint64(len(data))
|
||||||
// Bump the required gas by the amount of transactional 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
|
// Zero and non-zero bytes are priced differently
|
||||||
var nz uint64
|
var nz uint64
|
||||||
for _, byt := range data {
|
for _, byt := range data {
|
||||||
|
|
@ -105,11 +114,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
|
||||||
}
|
}
|
||||||
gas += nz * params.TxDataNonZeroGas
|
gas += nz * params.TxDataNonZeroGas
|
||||||
|
|
||||||
z := uint64(len(data)) - nz
|
z := dataLen - nz
|
||||||
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
|
||||||
return 0, ErrGasUintOverflow
|
return 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
gas += z * params.TxDataZeroGas
|
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 {
|
if accessList != nil {
|
||||||
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
gas += uint64(len(accessList)) * params.TxAccessListAddressGas
|
||||||
|
|
@ -118,16 +135,27 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation,
|
||||||
return gas, nil
|
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.
|
// NewStateTransition initialises and returns a new state transition object.
|
||||||
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
|
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
|
||||||
return &StateTransition{
|
return &StateTransition{
|
||||||
gp: gp,
|
gp: gp,
|
||||||
evm: evm,
|
evm: evm,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
gasPrice: msg.GasPrice(),
|
gasPrice: msg.GasPrice(),
|
||||||
value: msg.Value(),
|
gasFeeCap: msg.GasFeeCap(),
|
||||||
data: msg.Data(),
|
gasTipCap: msg.GasTipCap(),
|
||||||
state: evm.StateDB,
|
value: msg.Value(),
|
||||||
|
data: msg.Data(),
|
||||||
|
state: evm.StateDB,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,15 +199,18 @@ func (st *StateTransition) to() vm.AccountRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) buyGas() error {
|
func (st *StateTransition) buyGas() error {
|
||||||
var (
|
mgval := new(big.Int).SetUint64(st.msg.Gas())
|
||||||
state = st.state
|
mgval = mgval.Mul(mgval, st.gasPrice)
|
||||||
balanceTokenFee = st.balanceTokenFee()
|
balanceTokenFee := st.balanceTokenFee()
|
||||||
from = st.from()
|
|
||||||
)
|
|
||||||
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
|
|
||||||
if balanceTokenFee == nil {
|
if balanceTokenFee == nil {
|
||||||
if state.GetBalance(from.Address()).Cmp(mgval) < 0 {
|
balanceCheck := mgval
|
||||||
return errInsufficientBalanceForGas
|
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 {
|
} else if balanceTokenFee.Cmp(mgval) < 0 {
|
||||||
return errInsufficientBalanceForGas
|
return errInsufficientBalanceForGas
|
||||||
|
|
@ -191,25 +222,55 @@ func (st *StateTransition) buyGas() error {
|
||||||
|
|
||||||
st.initialGas = st.msg.Gas()
|
st.initialGas = st.msg.Gas()
|
||||||
if balanceTokenFee == nil {
|
if balanceTokenFee == nil {
|
||||||
state.SubBalance(from.Address(), mgval)
|
st.state.SubBalance(st.msg.From(), mgval)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) preCheck() error {
|
func (st *StateTransition) preCheck() error {
|
||||||
// Make sure this transaction's nonce is correct
|
// Only check transactions that are not fake
|
||||||
if st.msg.CheckNonce() {
|
msg := st.msg
|
||||||
|
if !msg.IsFake() {
|
||||||
// Make sure this transaction's nonce is correct.
|
// Make sure this transaction's nonce is correct.
|
||||||
stNonce := st.state.GetNonce(st.from().Address())
|
stNonce := st.state.GetNonce(msg.From())
|
||||||
if msgNonce := st.msg.Nonce(); stNonce < msgNonce {
|
if msgNonce := msg.Nonce(); stNonce < msgNonce {
|
||||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
|
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 {
|
} else if stNonce > msgNonce {
|
||||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
|
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 {
|
} else if stNonce+1 < stNonce {
|
||||||
return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
|
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()
|
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
|
// Check clauses 1-3, buy gas if everything is correct
|
||||||
if err = st.preCheck(); err != nil {
|
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)
|
var (
|
||||||
contractCreation := msg.To() == nil
|
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
|
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead)
|
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, rules.IsEIP1559)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, 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
|
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())
|
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
|
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 st.evm.Context.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 {
|
||||||
if (owner != common.Address{}) {
|
if (owner != common.Address{}) {
|
||||||
st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
return ret, st.gasUsed(), vmerr != nil, nil, vmerr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) refundGas() {
|
func (st *StateTransition) refundGas(refundQuotient uint64) {
|
||||||
// Apply refund counter, capped to half of the used gas.
|
// Apply refund counter, capped to a refund quotient
|
||||||
refund := st.gasUsed() / 2
|
refund := st.gasUsed() / refundQuotient
|
||||||
if refund > st.state.GetRefund() {
|
if refund > st.state.GetRefund() {
|
||||||
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) From() common.Address { return m.CallMsg.From }
|
||||||
func (m callMsg) Nonce() uint64 { return 0 }
|
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) To() *common.Address { return m.CallMsg.To }
|
||||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
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) Gas() uint64 { return m.CallMsg.Gas }
|
||||||
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
||||||
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
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
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -40,23 +40,23 @@ type devNull struct{}
|
||||||
func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil }
|
func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil }
|
||||||
func (*devNull) Close() error { return 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.
|
// 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
|
path string // Filesystem path to store the transactions at
|
||||||
writer io.WriteCloser // Output stream to write new transactions into
|
writer io.WriteCloser // Output stream to write new transactions into
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTxJournal creates a new transaction journal to
|
// newTxJournal creates a new transaction journal to
|
||||||
func newTxJournal(path string) *txJournal {
|
func newTxJournal(path string) *journal {
|
||||||
return &txJournal{
|
return &journal{
|
||||||
path: path,
|
path: path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load parses a transaction journal dump from disk, loading its contents into
|
// load parses a transaction journal dump from disk, loading its contents into
|
||||||
// the specified pool.
|
// 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
|
// Skip the parsing if the journal file doens't exist at all
|
||||||
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
|
if _, err := os.Stat(journal.path); os.IsNotExist(err) {
|
||||||
return nil
|
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.
|
// 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 {
|
if journal.writer == nil {
|
||||||
return errNoActiveJournal
|
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
|
// rotate regenerates the transaction journal based on the current contents of
|
||||||
// the transaction pool.
|
// 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)
|
// Close the current journal (if any is open)
|
||||||
if journal.writer != nil {
|
if journal.writer != nil {
|
||||||
if err := journal.writer.Close(); err != 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.
|
// 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
|
var err error
|
||||||
|
|
||||||
if journal.writer != nil {
|
if journal.writer != nil {
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/event"
|
"github.com/XinFinOrg/XDPoSChain/event"
|
||||||
|
|
@ -75,7 +76,7 @@ type blockChainLending interface {
|
||||||
GetBlock(hash common.Hash, number uint64) *types.Block
|
GetBlock(hash common.Hash, number uint64) *types.Block
|
||||||
LendingStateAt(block *types.Block) (*lendingstate.LendingStateDB, error)
|
LendingStateAt(block *types.Block) (*lendingstate.LendingStateDB, error)
|
||||||
StateAt(root common.Hash) (*state.StateDB, error)
|
StateAt(root common.Hash) (*state.StateDB, error)
|
||||||
SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
Engine() consensus.Engine
|
Engine() consensus.Engine
|
||||||
// GetHeader returns the hash corresponding to their hash.
|
// GetHeader returns the hash corresponding to their hash.
|
||||||
GetHeader(common.Hash, uint64) *types.Header
|
GetHeader(common.Hash, uint64) *types.Header
|
||||||
|
|
@ -124,7 +125,7 @@ type LendingPool struct {
|
||||||
|
|
||||||
txFeed event.Feed
|
txFeed event.Feed
|
||||||
scope event.SubscriptionScope
|
scope event.SubscriptionScope
|
||||||
chainHeadCh chan ChainHeadEvent
|
chainHeadCh chan core.ChainHeadEvent
|
||||||
chainHeadSub event.Subscription
|
chainHeadSub event.Subscription
|
||||||
signer types.LendingSigner
|
signer types.LendingSigner
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
@ -161,7 +162,7 @@ func NewLendingPool(chainconfig *params.ChainConfig, chain blockChainLending) *L
|
||||||
queue: make(map[common.Address]*lendingtxList),
|
queue: make(map[common.Address]*lendingtxList),
|
||||||
beats: make(map[common.Address]time.Time),
|
beats: make(map[common.Address]time.Time),
|
||||||
all: make(map[common.Hash]*types.LendingTransaction),
|
all: make(map[common.Hash]*types.LendingTransaction),
|
||||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||||
}
|
}
|
||||||
pool.locals = newLendingAccountSet(pool.signer)
|
pool.locals = newLendingAccountSet(pool.signer)
|
||||||
pool.reset(nil, chain.CurrentBlock())
|
pool.reset(nil, chain.CurrentBlock())
|
||||||
|
|
@ -334,7 +335,7 @@ func (pool *LendingPool) Stop() {
|
||||||
|
|
||||||
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
|
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
|
||||||
// starts sending event to the given channel.
|
// 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))
|
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 {
|
func (pool *LendingPool) validateBalance(cloneStateDb *state.StateDB, cloneLendingStateDb *lendingstate.LendingStateDB, tx *types.LendingTransaction, collateralToken common.Address) error {
|
||||||
XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS)
|
XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS)
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrNotXDPoS
|
return core.ErrNotXDPoS
|
||||||
}
|
}
|
||||||
XDCXServ := XDPoSEngine.GetXDCXService()
|
XDCXServ := XDPoSEngine.GetXDCXService()
|
||||||
lendingServ := XDPoSEngine.GetLendingService()
|
lendingServ := XDPoSEngine.GetLendingService()
|
||||||
|
|
@ -641,10 +642,10 @@ func (pool *LendingPool) validateTx(tx *types.LendingTransaction, local bool) er
|
||||||
}
|
}
|
||||||
// Ensure the transaction adheres to nonce lending
|
// Ensure the transaction adheres to nonce lending
|
||||||
if pool.currentLendingState.GetNonce(from.Hash()) > tx.Nonce() {
|
if pool.currentLendingState.GetNonce(from.Hash()) > tx.Nonce() {
|
||||||
return ErrNonceTooLow
|
return core.ErrNonceTooLow
|
||||||
}
|
}
|
||||||
if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
||||||
return ErrNonceTooHigh
|
return core.ErrNonceTooHigh
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -778,7 +779,7 @@ func (pool *LendingPool) promoteTx(addr common.Address, hash common.Hash, tx *ty
|
||||||
pool.beats[addr] = time.Now()
|
pool.beats[addr] = time.Now()
|
||||||
pool.pendingState.SetNonce(addr.Hash(), tx.Nonce()+1)
|
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
|
// AddLocal enqueues a single transaction into the pool if it is valid, marking
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
|
@ -48,30 +49,30 @@ func (h *nonceHeap) Pop() interface{} {
|
||||||
return x
|
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.
|
// 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
|
items map[uint64]*types.Transaction // Hash map storing the transaction data
|
||||||
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
|
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
|
||||||
cache types.Transactions // Cache of the transactions already sorted
|
cache types.Transactions // Cache of the transactions already sorted
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTxSortedMap creates a new nonce-sorted transaction map.
|
// newSortedMap creates a new nonce-sorted transaction map.
|
||||||
func newTxSortedMap() *txSortedMap {
|
func newSortedMap() *sortedMap {
|
||||||
return &txSortedMap{
|
return &sortedMap{
|
||||||
items: make(map[uint64]*types.Transaction),
|
items: make(map[uint64]*types.Transaction),
|
||||||
index: new(nonceHeap),
|
index: new(nonceHeap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get retrieves the current transactions associated with the given nonce.
|
// Get retrieves the current transactions associated with the given nonce.
|
||||||
func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
|
func (m *sortedMap) Get(nonce uint64) *types.Transaction {
|
||||||
return m.items[nonce]
|
return m.items[nonce]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put inserts a new transaction into the map, also updating the map's nonce
|
// Put inserts a new transaction into the map, also updating the map's nonce
|
||||||
// index. If a transaction already exists with the same nonce, it's overwritten.
|
// index. If a transaction already exists with the same nonce, it's overwritten.
|
||||||
func (m *txSortedMap) Put(tx *types.Transaction) {
|
func (m *sortedMap) Put(tx *types.Transaction) {
|
||||||
nonce := tx.Nonce()
|
nonce := tx.Nonce()
|
||||||
if m.items[nonce] == nil {
|
if m.items[nonce] == nil {
|
||||||
heap.Push(m.index, nonce)
|
heap.Push(m.index, nonce)
|
||||||
|
|
@ -82,7 +83,7 @@ func (m *txSortedMap) Put(tx *types.Transaction) {
|
||||||
// Forward removes all transactions from the map with a nonce lower than the
|
// Forward removes all transactions from the map with a nonce lower than the
|
||||||
// provided threshold. Every removed transaction is returned for any post-removal
|
// provided threshold. Every removed transaction is returned for any post-removal
|
||||||
// maintenance.
|
// maintenance.
|
||||||
func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
|
func (m *sortedMap) Forward(threshold uint64) types.Transactions {
|
||||||
var removed types.Transactions
|
var removed types.Transactions
|
||||||
|
|
||||||
// Pop off heap items until the threshold is reached
|
// Pop off heap items until the threshold is reached
|
||||||
|
|
@ -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.
|
// 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
|
// If you want to do several consecutive filterings, it's therefore better to first
|
||||||
// do a .filter(func1) followed by .Filter(func2) or reheap()
|
// do a .filter(func1) followed by .Filter(func2) or reheap()
|
||||||
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
|
func (m *sortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||||
removed := m.filter(filter)
|
removed := m.filter(filter)
|
||||||
// If transactions were removed, the heap and cache are ruined
|
// If transactions were removed, the heap and cache are ruined
|
||||||
if len(removed) > 0 {
|
if len(removed) > 0 {
|
||||||
|
|
@ -112,7 +113,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
|
||||||
return removed
|
return removed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *txSortedMap) reheap() {
|
func (m *sortedMap) reheap() {
|
||||||
*m.index = make([]uint64, 0, len(m.items))
|
*m.index = make([]uint64, 0, len(m.items))
|
||||||
for nonce := range m.items {
|
for nonce := range m.items {
|
||||||
*m.index = append(*m.index, nonce)
|
*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
|
// 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()
|
// 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
|
var removed types.Transactions
|
||||||
|
|
||||||
// Collect all the transactions to filter out
|
// 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
|
// Cap places a hard limit on the number of items, returning all transactions
|
||||||
// exceeding that limit.
|
// exceeding that limit.
|
||||||
func (m *txSortedMap) Cap(threshold int) types.Transactions {
|
func (m *sortedMap) Cap(threshold int) types.Transactions {
|
||||||
// Short circuit if the number of items is under the limit
|
// Short circuit if the number of items is under the limit
|
||||||
if len(m.items) <= threshold {
|
if len(m.items) <= threshold {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -166,7 +167,7 @@ func (m *txSortedMap) Cap(threshold int) types.Transactions {
|
||||||
|
|
||||||
// Remove deletes a transaction from the maintained map, returning whether the
|
// Remove deletes a transaction from the maintained map, returning whether the
|
||||||
// transaction was found.
|
// transaction was found.
|
||||||
func (m *txSortedMap) Remove(nonce uint64) bool {
|
func (m *sortedMap) Remove(nonce uint64) bool {
|
||||||
// Short circuit if no transaction is present
|
// Short circuit if no transaction is present
|
||||||
_, ok := m.items[nonce]
|
_, ok := m.items[nonce]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -192,7 +193,7 @@ func (m *txSortedMap) Remove(nonce uint64) bool {
|
||||||
// Note, all transactions with nonces lower than start will also be returned to
|
// 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
|
// prevent getting into and invalid state. This is not something that should ever
|
||||||
// happen but better to be self correcting than failing!
|
// happen but better to be self correcting than failing!
|
||||||
func (m *txSortedMap) Ready(start uint64) types.Transactions {
|
func (m *sortedMap) Ready(start uint64) types.Transactions {
|
||||||
// Short circuit if no transactions are available
|
// Short circuit if no transactions are available
|
||||||
if m.index.Len() == 0 || (*m.index)[0] > start {
|
if m.index.Len() == 0 || (*m.index)[0] > start {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -210,11 +211,11 @@ func (m *txSortedMap) Ready(start uint64) types.Transactions {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the length of the transaction map.
|
// Len returns the length of the transaction map.
|
||||||
func (m *txSortedMap) Len() int {
|
func (m *sortedMap) Len() int {
|
||||||
return len(m.items)
|
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 the sorting was not cached yet, create and cache it
|
||||||
if m.cache == nil {
|
if m.cache == nil {
|
||||||
m.cache = make(types.Transactions, 0, len(m.items))
|
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
|
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||||
// sorted internal representation. The result of the sorting is cached in case
|
// sorted internal representation. The result of the sorting is cached in case
|
||||||
// it's requested again before any modifications are made to the contents.
|
// it's requested again before any modifications are made to the contents.
|
||||||
func (m *txSortedMap) Flatten() types.Transactions {
|
func (m *sortedMap) Flatten() types.Transactions {
|
||||||
// Copy the cache to prevent accidental modifications
|
// Copy the cache to prevent accidental modifications
|
||||||
cache := m.flatten()
|
cache := m.flatten()
|
||||||
txs := make(types.Transactions, len(cache))
|
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
|
// LastElement returns the last element of a flattened list, thus, the
|
||||||
// transaction with the highest nonce
|
// transaction with the highest nonce
|
||||||
func (m *txSortedMap) LastElement() *types.Transaction {
|
func (m *sortedMap) LastElement() *types.Transaction {
|
||||||
cache := m.flatten()
|
cache := m.flatten()
|
||||||
return cache[len(cache)-1]
|
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
|
// 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-
|
// the executable/pending queue; and for storing gapped transactions for the non-
|
||||||
// executable/future queue, with minor behavioral changes.
|
// executable/future queue, with minor behavioral changes.
|
||||||
type txList struct {
|
type list struct {
|
||||||
strict bool // Whether nonces are strictly continuous or not
|
strict bool // Whether nonces are strictly continuous or not
|
||||||
txs *txSortedMap // Heap indexed sorted hash map of the transactions
|
txs *sortedMap // Heap indexed sorted hash map of the transactions
|
||||||
|
|
||||||
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
|
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)
|
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.
|
// gapped, sortable transaction lists.
|
||||||
func newTxList(strict bool) *txList {
|
func newList(strict bool) *list {
|
||||||
return &txList{
|
return &list{
|
||||||
strict: strict,
|
strict: strict,
|
||||||
txs: newTxSortedMap(),
|
txs: newSortedMap(),
|
||||||
costcap: new(big.Int),
|
costcap: new(big.Int),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overlaps returns whether the transaction specified has the same nonce as one
|
// Overlaps returns whether the transaction specified has the same nonce as one
|
||||||
// already contained within the list.
|
// 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
|
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
|
// If the new transaction is accepted into the list, the lists' cost and gas
|
||||||
// thresholds are also potentially updated.
|
// 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
|
// If there's an older better transaction, abort
|
||||||
old := l.txs.Get(tx.Nonce())
|
old := l.txs.Get(tx.Nonce())
|
||||||
if old != nil && old.IsSpecialTransaction() {
|
if old != nil && old.IsSpecialTransaction() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
if old != 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 := 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)
|
b := big.NewInt(100)
|
||||||
threshold := a.Div(a, b)
|
thresholdFeeCap := aFeeCap.Div(aFeeCap, b)
|
||||||
// Have to ensure that the new gas price is higher than the old gas
|
thresholdTip := aTip.Div(aTip, b)
|
||||||
// price as well as checking the percentage threshold to ensure that
|
|
||||||
|
// 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
|
// 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
|
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
|
// Forward removes all transactions from the list with a nonce lower than the
|
||||||
// provided threshold. Every removed transaction is returned for any post-removal
|
// provided threshold. Every removed transaction is returned for any post-removal
|
||||||
// maintenance.
|
// maintenance.
|
||||||
func (l *txList) Forward(threshold uint64) types.Transactions {
|
func (l *list) Forward(threshold uint64) types.Transactions {
|
||||||
return l.txs.Forward(threshold)
|
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
|
// a point in calculating all the costs or if the balance covers all. If the threshold
|
||||||
// is lower than the costgas cap, the caps will be reset to a new high after removing
|
// is lower than the costgas cap, the caps will be reset to a new high after removing
|
||||||
// the newly invalidated transactions.
|
// the newly invalidated transactions.
|
||||||
func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, 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 all transactions are below the threshold, short circuit
|
||||||
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
|
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
|
||||||
return nil, nil
|
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
|
// Cap places a hard limit on the number of items, returning all transactions
|
||||||
// exceeding that limit.
|
// exceeding that limit.
|
||||||
func (l *txList) Cap(threshold int) types.Transactions {
|
func (l *list) Cap(threshold int) types.Transactions {
|
||||||
return l.txs.Cap(threshold)
|
return l.txs.Cap(threshold)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove deletes a transaction from the maintained list, returning whether the
|
// Remove deletes a transaction from the maintained list, returning whether the
|
||||||
// transaction was found, and also returning any transaction invalidated due to
|
// transaction was found, and also returning any transaction invalidated due to
|
||||||
// the deletion (strict mode only).
|
// 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
|
// Remove the transaction from the set
|
||||||
nonce := tx.Nonce()
|
nonce := tx.Nonce()
|
||||||
if removed := l.txs.Remove(nonce); !removed {
|
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
|
// 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
|
// prevent getting into and invalid state. This is not something that should ever
|
||||||
// happen but better to be self correcting than failing!
|
// happen but better to be self correcting than failing!
|
||||||
func (l *txList) Ready(start uint64) types.Transactions {
|
func (l *list) Ready(start uint64) types.Transactions {
|
||||||
return l.txs.Ready(start)
|
return l.txs.Ready(start)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the length of the transaction list.
|
// Len returns the length of the transaction list.
|
||||||
func (l *txList) Len() int {
|
func (l *list) Len() int {
|
||||||
return l.txs.Len()
|
return l.txs.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty returns whether the list of transactions is empty or not.
|
// 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
|
return l.Len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||||
// sorted internal representation. The result of the sorting is cached in case
|
// sorted internal representation. The result of the sorting is cached in case
|
||||||
// it's requested again before any modifications are made to the contents.
|
// it's requested again before any modifications are made to the contents.
|
||||||
func (l *txList) Flatten() types.Transactions {
|
func (l *list) Flatten() types.Transactions {
|
||||||
return l.txs.Flatten()
|
return l.txs.Flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastElement returns the last element of a flattened list, thus, the
|
// LastElement returns the last element of a flattened list, thus, the
|
||||||
// transaction with the highest nonce
|
// transaction with the highest nonce
|
||||||
func (l *txList) LastElement() *types.Transaction {
|
func (l *list) LastElement() *types.Transaction {
|
||||||
return l.txs.LastElement()
|
return l.txs.LastElement()
|
||||||
}
|
}
|
||||||
|
|
||||||
// priceHeap is a heap.Interface implementation over transactions for retrieving
|
// priceHeap is a heap.Interface implementation over transactions for retrieving
|
||||||
// price-sorted transactions to discard when the pool fills up.
|
// price-sorted transactions to discard when the pool fills up. If baseFee is set
|
||||||
type priceHeap []*types.Transaction
|
// 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) Len() int { return len(h.list) }
|
||||||
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
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 {
|
func (h *priceHeap) Less(i, j int) bool {
|
||||||
// Sort primarily by price, returning the cheaper one
|
switch h.cmp(h.list[i], h.list[j]) {
|
||||||
switch h[i].GasPriceCmp(h[j]) {
|
|
||||||
case -1:
|
case -1:
|
||||||
return true
|
return true
|
||||||
case 1:
|
case 1:
|
||||||
return false
|
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{}) {
|
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{} {
|
func (h *priceHeap) Pop() interface{} {
|
||||||
old := *h
|
old := h.list
|
||||||
n := len(old)
|
n := len(old)
|
||||||
x := old[n-1]
|
x := old[n-1]
|
||||||
*h = old[0 : n-1]
|
old[n-1] = nil
|
||||||
|
h.list = old[0 : n-1]
|
||||||
return x
|
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
|
// 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
|
// in txpool but only interested in the remote part. It means only remote transactions
|
||||||
// will be considered for tracking, sorting, eviction, etc.
|
// will be considered for tracking, sorting, eviction, etc.
|
||||||
type txPricedList struct {
|
//
|
||||||
all *txLookup // Pointer to the map of all transactions
|
// Two heaps are used for sorting: the urgent heap (based on effective tip in the next
|
||||||
remotes *priceHeap // Heap of prices of all the stored **remote** transactions
|
// block) and the floating heap (based on gasFeeCap). Always the bigger heap is chosen for
|
||||||
stales int64 // Number of stale price points to (re-heap trigger)
|
// eviction. Transactions evicted from the urgent heap are first demoted into the floating heap.
|
||||||
reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list
|
// 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.
|
const (
|
||||||
func newTxPricedList(all *txLookup) *txPricedList {
|
// urgentRatio : floatingRatio is the capacity ratio of the two queues
|
||||||
return &txPricedList{
|
urgentRatio = 4
|
||||||
all: all,
|
floatingRatio = 1
|
||||||
remotes: new(priceHeap),
|
)
|
||||||
|
|
||||||
|
// 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.
|
// 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 {
|
if local {
|
||||||
return
|
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
|
// 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
|
// 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.
|
// 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%)
|
// Bump the stale counter, but exit if still too low (< 25%)
|
||||||
stales := atomic.AddInt64(&l.stales, int64(count))
|
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
|
return
|
||||||
}
|
}
|
||||||
// Seems we've reached a critical number of stale transactions, reheap
|
// Seems we've reached a critical number of stale transactions, reheap
|
||||||
l.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
|
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
|
||||||
// lowest priced (remote) transaction currently being tracked.
|
// 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
|
// Discard stale price points if found at the heap start
|
||||||
for len(*l.remotes) > 0 {
|
for len(h.list) > 0 {
|
||||||
head := []*types.Transaction(*l.remotes)[0]
|
head := h.list[0]
|
||||||
if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated
|
if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated
|
||||||
atomic.AddInt64(&l.stales, -1)
|
atomic.AddInt64(&l.stales, -1)
|
||||||
heap.Pop(l.remotes)
|
heap.Pop(h)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Check if the transaction is underpriced or not
|
// 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.
|
return false // There is no remote transaction at all.
|
||||||
}
|
}
|
||||||
// If the remote transaction is even cheaper than the
|
// If the remote transaction is even cheaper than the
|
||||||
// cheapest one tracked locally, reject it.
|
// cheapest one tracked locally, reject it.
|
||||||
cheapest := []*types.Transaction(*l.remotes)[0]
|
return h.cmp(h.list[0], tx) >= 0
|
||||||
return cheapest.GasPriceCmp(tx) >= 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discard finds a number of most underpriced transactions, removes them from the
|
// Discard finds a number of most underpriced transactions, removes them from the
|
||||||
// priced list and returns them for further removal from the entire pool.
|
// priced list and returns them for further removal from the entire pool.
|
||||||
//
|
//
|
||||||
// Note local transaction won't be considered for eviction.
|
// 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
|
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
|
||||||
for len(*l.remotes) > 0 && slots > 0 {
|
for slots > 0 {
|
||||||
// Discard stale transactions if found during cleanup
|
if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 {
|
||||||
tx := heap.Pop(l.remotes).(*types.Transaction)
|
// Discard stale transactions if found during cleanup
|
||||||
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
|
tx := heap.Pop(&l.urgent).(*types.Transaction)
|
||||||
atomic.AddInt64(&l.stales, -1)
|
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
|
||||||
continue
|
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 we still can't make enough room for the new transaction
|
||||||
if slots > 0 && !force {
|
if slots > 0 && !force {
|
||||||
for _, tx := range drop {
|
for _, tx := range drop {
|
||||||
heap.Push(l.remotes, tx)
|
heap.Push(&l.urgent, tx)
|
||||||
}
|
}
|
||||||
return nil, false
|
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.
|
// Reheap forcibly rebuilds the heap based on the current remote transaction set.
|
||||||
func (l *txPricedList) Reheap() {
|
func (l *pricedList) Reheap() {
|
||||||
l.reheapMu.Lock()
|
l.reheapMu.Lock()
|
||||||
defer l.reheapMu.Unlock()
|
defer l.reheapMu.Unlock()
|
||||||
reheap := make(priceHeap, 0, l.all.RemoteCount())
|
start := time.Now()
|
||||||
|
|
||||||
atomic.StoreInt64(&l.stales, 0)
|
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.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
|
return true
|
||||||
}, false, true) // Only iterate remotes
|
}, 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
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
|
|
||||||
// Tests that transactions can be added to strict lists and list contents and
|
// Tests that transactions can be added to strict lists and list contents and
|
||||||
// nonce boundaries are correctly maintained.
|
// nonce boundaries are correctly maintained.
|
||||||
func TestStrictTxListAdd(t *testing.T) {
|
func TestStrictListAdd(t *testing.T) {
|
||||||
// Generate a list of transactions to insert
|
// Generate a list of transactions to insert
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
||||||
|
|
@ -36,9 +36,9 @@ func TestStrictTxListAdd(t *testing.T) {
|
||||||
txs[i] = transaction(uint64(i), 0, key)
|
txs[i] = transaction(uint64(i), 0, key)
|
||||||
}
|
}
|
||||||
// Insert the transactions in a random order
|
// Insert the transactions in a random order
|
||||||
list := newTxList(true)
|
list := newList(true)
|
||||||
for _, v := range rand.Perm(len(txs)) {
|
for _, v := range rand.Perm(len(txs)) {
|
||||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
list.Add(txs[v], DefaultConfig.PriceBump)
|
||||||
}
|
}
|
||||||
// Verify internal state
|
// Verify internal state
|
||||||
if len(list.txs.items) != len(txs) {
|
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
|
// Generate a list of transactions to insert
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
||||||
|
|
@ -60,11 +60,11 @@ func BenchmarkTxListAdd(t *testing.B) {
|
||||||
txs[i] = transaction(uint64(i), 0, key)
|
txs[i] = transaction(uint64(i), 0, key)
|
||||||
}
|
}
|
||||||
// Insert the transactions in a random order
|
// Insert the transactions in a random order
|
||||||
list := newTxList(true)
|
list := newList(true)
|
||||||
priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit))
|
priceLimit := big.NewInt(int64(DefaultConfig.PriceLimit))
|
||||||
t.ResetTimer()
|
t.ResetTimer()
|
||||||
for _, v := range rand.Perm(len(txs)) {
|
for _, v := range rand.Perm(len(txs)) {
|
||||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
list.Add(txs[v], DefaultConfig.PriceBump)
|
||||||
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump, nil, nil)
|
list.Filter(priceLimit, DefaultConfig.PriceBump, nil, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -23,18 +23,18 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"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
|
// accounts in the pool, falling back to reading from a real state database if
|
||||||
// an account is unknown.
|
// an account is unknown.
|
||||||
type txNoncer struct {
|
type noncer struct {
|
||||||
fallback *state.StateDB
|
fallback *state.StateDB
|
||||||
nonces map[common.Address]uint64
|
nonces map[common.Address]uint64
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTxNoncer creates a new virtual state database to track the pool nonces.
|
// newNoncer creates a new virtual state database to track the pool nonces.
|
||||||
func newTxNoncer(statedb *state.StateDB) *txNoncer {
|
func newNoncer(statedb *state.StateDB) *noncer {
|
||||||
return &txNoncer{
|
return &noncer{
|
||||||
fallback: statedb.Copy(),
|
fallback: statedb.Copy(),
|
||||||
nonces: make(map[common.Address]uint64),
|
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
|
// get returns the current nonce of an account, falling back to a real state
|
||||||
// database if the account is unknown.
|
// 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
|
// We use mutex for get operation is the underlying
|
||||||
// state will mutate db even for read access.
|
// state will mutate db even for read access.
|
||||||
txn.lock.Lock()
|
txn.lock.Lock()
|
||||||
defer txn.lock.Unlock()
|
defer txn.lock.Unlock()
|
||||||
|
|
||||||
if _, ok := txn.nonces[addr]; !ok {
|
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]
|
return txn.nonces[addr]
|
||||||
}
|
}
|
||||||
|
|
||||||
// set inserts a new virtual nonce into the virtual state database to be returned
|
// 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.
|
// 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()
|
txn.lock.Lock()
|
||||||
defer txn.lock.Unlock()
|
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
|
// setIfLower updates a new virtual nonce into the virtual state database if the
|
||||||
// the new one is lower.
|
// 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()
|
txn.lock.Lock()
|
||||||
defer txn.lock.Unlock()
|
defer txn.lock.Unlock()
|
||||||
|
|
||||||
if _, ok := txn.nonces[addr]; !ok {
|
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 {
|
if txn.nonces[addr] <= nonce {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
txn.nonces[addr] = nonce
|
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
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/event"
|
"github.com/XinFinOrg/XDPoSChain/event"
|
||||||
|
|
@ -84,7 +85,7 @@ type blockChainXDCx interface {
|
||||||
GetBlock(hash common.Hash, number uint64) *types.Block
|
GetBlock(hash common.Hash, number uint64) *types.Block
|
||||||
OrderStateAt(block *types.Block) (*tradingstate.TradingStateDB, error)
|
OrderStateAt(block *types.Block) (*tradingstate.TradingStateDB, error)
|
||||||
StateAt(root common.Hash) (*state.StateDB, error)
|
StateAt(root common.Hash) (*state.StateDB, error)
|
||||||
SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
Engine() consensus.Engine
|
Engine() consensus.Engine
|
||||||
// GetHeader returns the hash corresponding to their hash.
|
// GetHeader returns the hash corresponding to their hash.
|
||||||
GetHeader(common.Hash, uint64) *types.Header
|
GetHeader(common.Hash, uint64) *types.Header
|
||||||
|
|
@ -133,7 +134,7 @@ type OrderPool struct {
|
||||||
|
|
||||||
txFeed event.Feed
|
txFeed event.Feed
|
||||||
scope event.SubscriptionScope
|
scope event.SubscriptionScope
|
||||||
chainHeadCh chan ChainHeadEvent
|
chainHeadCh chan core.ChainHeadEvent
|
||||||
chainHeadSub event.Subscription
|
chainHeadSub event.Subscription
|
||||||
signer types.OrderSigner
|
signer types.OrderSigner
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
@ -170,7 +171,7 @@ func NewOrderPool(chainconfig *params.ChainConfig, chain blockChainXDCx) *OrderP
|
||||||
queue: make(map[common.Address]*ordertxList),
|
queue: make(map[common.Address]*ordertxList),
|
||||||
beats: make(map[common.Address]time.Time),
|
beats: make(map[common.Address]time.Time),
|
||||||
all: make(map[common.Hash]*types.OrderTransaction),
|
all: make(map[common.Hash]*types.OrderTransaction),
|
||||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||||
}
|
}
|
||||||
pool.locals = newOrderAccountSet(pool.signer)
|
pool.locals = newOrderAccountSet(pool.signer)
|
||||||
pool.reset(nil, chain.CurrentBlock())
|
pool.reset(nil, chain.CurrentBlock())
|
||||||
|
|
@ -340,7 +341,7 @@ func (pool *OrderPool) Stop() {
|
||||||
|
|
||||||
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
|
// SubscribeTxPreEvent registers a subscription of TxPreEvent and
|
||||||
// starts sending event to the given channel.
|
// 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))
|
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,7 +465,7 @@ func (pool *OrderPool) validateOrder(tx *types.OrderTransaction) error {
|
||||||
if orderType == OrderTypeLimit {
|
if orderType == OrderTypeLimit {
|
||||||
XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS)
|
XDPoSEngine, ok := pool.chain.Engine().(*XDPoS.XDPoS)
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrNotXDPoS
|
return core.ErrNotXDPoS
|
||||||
}
|
}
|
||||||
XDCXServ := XDPoSEngine.GetXDCXService()
|
XDCXServ := XDPoSEngine.GetXDCXService()
|
||||||
if XDCXServ == nil {
|
if XDCXServ == nil {
|
||||||
|
|
@ -550,10 +551,10 @@ func (pool *OrderPool) validateTx(tx *types.OrderTransaction, local bool) error
|
||||||
}
|
}
|
||||||
// Ensure the transaction adheres to nonce ordering
|
// Ensure the transaction adheres to nonce ordering
|
||||||
if pool.currentOrderState.GetNonce(from.Hash()) > tx.Nonce() {
|
if pool.currentOrderState.GetNonce(from.Hash()) > tx.Nonce() {
|
||||||
return ErrNonceTooLow
|
return core.ErrNonceTooLow
|
||||||
}
|
}
|
||||||
if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
if pool.pendingState.GetNonce(from.Hash())+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
||||||
return ErrNonceTooHigh
|
return core.ErrNonceTooHigh
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -603,7 +604,7 @@ func (pool *OrderPool) add(tx *types.OrderTransaction, local bool) (bool, error)
|
||||||
pool.journalTx(from, tx)
|
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())
|
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
|
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.beats[addr] = time.Now()
|
||||||
pool.pendingState.SetNonce(addr.Hash(), tx.Nonce()+1)
|
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())
|
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
|
// AddLocal enqueues a single transaction into the pool if it is valid, marking
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package core
|
package txpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -29,6 +29,8 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
"github.com/XinFinOrg/XDPoSChain/common/prque"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"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/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/event"
|
"github.com/XinFinOrg/XDPoSChain/event"
|
||||||
|
|
@ -62,10 +64,6 @@ var (
|
||||||
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
// ErrInvalidSender is returned if the transaction contains an invalid signature.
|
||||||
ErrInvalidSender = errors.New("invalid sender")
|
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
|
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
|
||||||
// configured for the transaction pool.
|
// configured for the transaction pool.
|
||||||
ErrUnderpriced = errors.New("transaction underpriced")
|
ErrUnderpriced = errors.New("transaction underpriced")
|
||||||
|
|
@ -78,14 +76,6 @@ var (
|
||||||
// with a different one without the required price bump.
|
// with a different one without the required price bump.
|
||||||
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
|
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
|
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
|
||||||
// maximum allowance of the current block.
|
// maximum allowance of the current block.
|
||||||
ErrGasLimit = errors.New("exceeds block gas limit")
|
ErrGasLimit = errors.New("exceeds block gas limit")
|
||||||
|
|
@ -134,10 +124,21 @@ var (
|
||||||
underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil)
|
underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil)
|
||||||
overflowedTxMeter = metrics.NewRegisteredMeter("txpool/overflowed", 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)
|
pendingGauge = metrics.NewRegisteredGauge("txpool/pending", nil)
|
||||||
queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil)
|
queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil)
|
||||||
localGauge = metrics.NewRegisteredGauge("txpool/local", nil)
|
localGauge = metrics.NewRegisteredGauge("txpool/local", nil)
|
||||||
slotsGauge = metrics.NewRegisteredGauge("txpool/slots", 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.
|
// TxStatus is the current status of a transaction as seen by the pool.
|
||||||
|
|
@ -156,7 +157,7 @@ type blockChain interface {
|
||||||
CurrentBlock() *types.Block
|
CurrentBlock() *types.Block
|
||||||
GetBlock(hash common.Hash, number uint64) *types.Block
|
GetBlock(hash common.Hash, number uint64) *types.Block
|
||||||
StateAt(root common.Hash) (*state.StateDB, error)
|
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 retrieves the chain's consensus engine.
|
||||||
Engine() consensus.Engine
|
Engine() consensus.Engine
|
||||||
|
|
@ -171,8 +172,8 @@ type blockChain interface {
|
||||||
Config() *params.ChainConfig
|
Config() *params.ChainConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxPoolConfig are the configuration parameters of the transaction pool.
|
// Config are the configuration parameters of the transaction pool.
|
||||||
type TxPoolConfig struct {
|
type Config struct {
|
||||||
Locals []common.Address // Addresses that should be treated by default as local
|
Locals []common.Address // Addresses that should be treated by default as local
|
||||||
NoLocals bool // Whether local transaction handling should be disabled
|
NoLocals bool // Whether local transaction handling should be disabled
|
||||||
Journal string // Journal of local transactions to survive node restarts
|
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
|
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.
|
// pool.
|
||||||
var DefaultTxPoolConfig = TxPoolConfig{
|
var DefaultConfig = Config{
|
||||||
Journal: "transactions.rlp",
|
Journal: "transactions.rlp",
|
||||||
Rejournal: time.Hour,
|
Rejournal: time.Hour,
|
||||||
|
|
||||||
|
|
@ -199,7 +200,7 @@ var DefaultTxPoolConfig = TxPoolConfig{
|
||||||
PriceBump: 10,
|
PriceBump: 10,
|
||||||
|
|
||||||
AccountSlots: 16,
|
AccountSlots: 16,
|
||||||
GlobalSlots: 4096,
|
GlobalSlots: 4096 + 1024, // urgent + floating queue capacity with 4:1 ratio
|
||||||
AccountQueue: 64,
|
AccountQueue: 64,
|
||||||
GlobalQueue: 1024,
|
GlobalQueue: 1024,
|
||||||
|
|
||||||
|
|
@ -208,39 +209,39 @@ var DefaultTxPoolConfig = TxPoolConfig{
|
||||||
|
|
||||||
// sanitize checks the provided user configurations and changes anything that's
|
// sanitize checks the provided user configurations and changes anything that's
|
||||||
// unreasonable or unworkable.
|
// unreasonable or unworkable.
|
||||||
func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
func (config *Config) sanitize() Config {
|
||||||
conf := *config
|
conf := *config
|
||||||
if conf.Rejournal < time.Second {
|
if conf.Rejournal < time.Second {
|
||||||
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
|
log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second)
|
||||||
conf.Rejournal = time.Second
|
conf.Rejournal = time.Second
|
||||||
}
|
}
|
||||||
if conf.PriceLimit < 1 {
|
if conf.PriceLimit < 1 {
|
||||||
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit)
|
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
|
||||||
conf.PriceLimit = DefaultTxPoolConfig.PriceLimit
|
conf.PriceLimit = DefaultConfig.PriceLimit
|
||||||
}
|
}
|
||||||
if conf.PriceBump < 1 {
|
if conf.PriceBump < 1 {
|
||||||
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump)
|
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultConfig.PriceBump)
|
||||||
conf.PriceBump = DefaultTxPoolConfig.PriceBump
|
conf.PriceBump = DefaultConfig.PriceBump
|
||||||
}
|
}
|
||||||
if conf.AccountSlots < 1 {
|
if conf.AccountSlots < 1 {
|
||||||
log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultTxPoolConfig.AccountSlots)
|
log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultConfig.AccountSlots)
|
||||||
conf.AccountSlots = DefaultTxPoolConfig.AccountSlots
|
conf.AccountSlots = DefaultConfig.AccountSlots
|
||||||
}
|
}
|
||||||
if conf.GlobalSlots < 1 {
|
if conf.GlobalSlots < 1 {
|
||||||
log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultTxPoolConfig.GlobalSlots)
|
log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultConfig.GlobalSlots)
|
||||||
conf.GlobalSlots = DefaultTxPoolConfig.GlobalSlots
|
conf.GlobalSlots = DefaultConfig.GlobalSlots
|
||||||
}
|
}
|
||||||
if conf.AccountQueue < 1 {
|
if conf.AccountQueue < 1 {
|
||||||
log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultTxPoolConfig.AccountQueue)
|
log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultConfig.AccountQueue)
|
||||||
conf.AccountQueue = DefaultTxPoolConfig.AccountQueue
|
conf.AccountQueue = DefaultConfig.AccountQueue
|
||||||
}
|
}
|
||||||
if conf.GlobalQueue < 1 {
|
if conf.GlobalQueue < 1 {
|
||||||
log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue)
|
log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultConfig.GlobalQueue)
|
||||||
conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue
|
conf.GlobalQueue = DefaultConfig.GlobalQueue
|
||||||
}
|
}
|
||||||
if conf.Lifetime < 1 {
|
if conf.Lifetime < 1 {
|
||||||
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime)
|
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultConfig.Lifetime)
|
||||||
conf.Lifetime = DefaultTxPoolConfig.Lifetime
|
conf.Lifetime = DefaultConfig.Lifetime
|
||||||
}
|
}
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +254,7 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
||||||
// current state) and future transactions. Transactions move between those
|
// current state) and future transactions. Transactions move between those
|
||||||
// two states over time as they are received and processed.
|
// two states over time as they are received and processed.
|
||||||
type TxPool struct {
|
type TxPool struct {
|
||||||
config TxPoolConfig
|
config Config
|
||||||
chainconfig *params.ChainConfig
|
chainconfig *params.ChainConfig
|
||||||
chain blockChain
|
chain blockChain
|
||||||
gasPrice *big.Int
|
gasPrice *big.Int
|
||||||
|
|
@ -262,20 +263,23 @@ type TxPool struct {
|
||||||
signer types.Signer
|
signer types.Signer
|
||||||
mu sync.RWMutex
|
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
|
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
|
currentMaxGas uint64 // Current gas limit for transaction caps
|
||||||
|
|
||||||
locals *accountSet // Set of local transaction to exempt from eviction rules
|
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
|
pending map[common.Address]*list // All currently processable transactions
|
||||||
queue map[common.Address]*txList // Queued but non-processable transactions
|
queue map[common.Address]*list // Queued but non-processable transactions
|
||||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||||
all *txLookup // All transactions to allow lookups
|
all *lookup // All transactions to allow lookups
|
||||||
priced *txPricedList // All transactions sorted by price
|
priced *pricedList // All transactions sorted by price
|
||||||
|
|
||||||
chainHeadCh chan ChainHeadEvent
|
chainHeadCh chan core.ChainHeadEvent
|
||||||
chainHeadSub event.Subscription
|
chainHeadSub event.Subscription
|
||||||
reqResetCh chan *txpoolResetRequest
|
reqResetCh chan *txpoolResetRequest
|
||||||
reqPromoteCh chan *accountSet
|
reqPromoteCh chan *accountSet
|
||||||
|
|
@ -285,7 +289,8 @@ type TxPool struct {
|
||||||
wg sync.WaitGroup // tracks loop, scheduleReorgLoop
|
wg sync.WaitGroup // tracks loop, scheduleReorgLoop
|
||||||
initDoneCh chan struct{} // is closed once the pool is initialized (for tests)
|
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
|
IsSigner func(address common.Address) bool
|
||||||
trc21FeeCapacity map[common.Address]*big.Int
|
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
|
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
|
||||||
// transactions from the network.
|
// 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
|
// Sanitize the input to ensure no vulnerable gas prices are set
|
||||||
config = (&config).sanitize()
|
config = (&config).sanitize()
|
||||||
|
|
||||||
|
|
@ -306,11 +311,11 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
||||||
chainconfig: chainconfig,
|
chainconfig: chainconfig,
|
||||||
chain: chain,
|
chain: chain,
|
||||||
signer: types.LatestSigner(chainconfig),
|
signer: types.LatestSigner(chainconfig),
|
||||||
pending: make(map[common.Address]*txList),
|
pending: make(map[common.Address]*list),
|
||||||
queue: make(map[common.Address]*txList),
|
queue: make(map[common.Address]*list),
|
||||||
beats: make(map[common.Address]time.Time),
|
beats: make(map[common.Address]time.Time),
|
||||||
all: newTxLookup(),
|
all: newLookup(),
|
||||||
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
|
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||||
reqResetCh: make(chan *txpoolResetRequest),
|
reqResetCh: make(chan *txpoolResetRequest),
|
||||||
reqPromoteCh: make(chan *accountSet),
|
reqPromoteCh: make(chan *accountSet),
|
||||||
queueTxEventCh: make(chan *types.Transaction),
|
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)
|
log.Info("Setting new local account", "address", addr)
|
||||||
pool.locals.add(addr)
|
pool.locals.add(addr)
|
||||||
}
|
}
|
||||||
pool.priced = newTxPricedList(pool.all)
|
pool.priced = newPricedList(pool.all)
|
||||||
pool.reset(nil, chain.CurrentBlock().Header())
|
pool.reset(nil, chain.CurrentBlock().Header())
|
||||||
|
|
||||||
// Start the reorg loop early so it can handle requests generated during journal loading.
|
// 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
|
// SubscribeNewTxsEvent registers a subscription of NewTxsEvent and
|
||||||
// starts sending event to the given channel.
|
// 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))
|
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -466,10 +471,18 @@ func (pool *TxPool) SetGasPrice(price *big.Int) {
|
||||||
pool.mu.Lock()
|
pool.mu.Lock()
|
||||||
defer pool.mu.Unlock()
|
defer pool.mu.Unlock()
|
||||||
|
|
||||||
|
old := pool.gasPrice
|
||||||
pool.gasPrice = price
|
pool.gasPrice = price
|
||||||
for _, tx := range pool.priced.Cap(price) {
|
// if the min miner fee increased, remove transactions below the new threshold
|
||||||
pool.removeTx(tx.Hash(), false)
|
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)
|
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()
|
pool.mu.Lock()
|
||||||
defer pool.mu.Unlock()
|
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 {
|
for addr, list := range pool.pending {
|
||||||
pending[addr] = list.Flatten()
|
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 {
|
for addr, list := range pool.queue {
|
||||||
queued[addr] = list.Flatten()
|
queued[addr] = list.Flatten()
|
||||||
}
|
}
|
||||||
return pending, queued
|
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
|
// Pending retrieves all currently processable transactions, grouped by origin
|
||||||
// account and sorted by nonce. The returned transaction set is a copy and can be
|
// account and sorted by nonce. The returned transaction set is a copy and can be
|
||||||
// freely modified by calling code.
|
// 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()
|
pool.mu.Lock()
|
||||||
defer pool.mu.Unlock()
|
defer pool.mu.Unlock()
|
||||||
|
|
||||||
pending := make(map[common.Address]types.Transactions)
|
pending := make(map[common.Address]types.Transactions)
|
||||||
for addr, list := range pool.pending {
|
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.
|
// 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 {
|
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
// Accept only legacy transactions until EIP-2718/2930 activates.
|
// Accept only legacy transactions until EIP-2718/2930 activates.
|
||||||
if !pool.eip2718 && tx.Type() != types.LegacyTxType {
|
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
|
// Reject transactions over defined size to prevent DOS attacks
|
||||||
if uint64(tx.Size()) > txMaxSize {
|
if uint64(tx.Size()) > txMaxSize {
|
||||||
|
|
@ -596,23 +647,34 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
if pool.currentMaxGas < tx.Gas() {
|
if pool.currentMaxGas < tx.Gas() {
|
||||||
return ErrGasLimit
|
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.
|
// Make sure the transaction is signed properly.
|
||||||
from, err := types.Sender(pool.signer, tx)
|
from, err := types.Sender(pool.signer, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrInvalidSender
|
return ErrInvalidSender
|
||||||
}
|
}
|
||||||
// Drop non-local transactions under our own minimal accepted gas price
|
// Drop non-local transactions under our own minimal accepted gas price or tip
|
||||||
if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 {
|
if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 {
|
||||||
if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) {
|
if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) {
|
||||||
return ErrUnderpriced
|
return ErrUnderpriced
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ensure the transaction adheres to nonce ordering
|
// Ensure the transaction adheres to nonce ordering
|
||||||
if pool.currentState.GetNonce(from) > tx.Nonce() {
|
if pool.currentState.GetNonce(from) > tx.Nonce() {
|
||||||
return ErrNonceTooLow
|
return core.ErrNonceTooLow
|
||||||
}
|
}
|
||||||
if pool.pendingNonces.get(from)+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
if pool.pendingNonces.get(from)+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
||||||
return ErrNonceTooHigh
|
return core.ErrNonceTooHigh
|
||||||
}
|
}
|
||||||
// Transactor should have enough funds to cover the costs
|
// Transactor should have enough funds to cover the costs
|
||||||
// cost == V + GP * GL
|
// 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 {
|
if value, ok := pool.trc21FeeCapacity[*tx.To()]; ok {
|
||||||
feeCapacity = value
|
feeCapacity = value
|
||||||
if !state.ValidateTRC21Tx(pool.currentState, from, *tx.To(), tx.Data()) {
|
if !state.ValidateTRC21Tx(pool.currentState, from, *tx.To(), tx.Data()) {
|
||||||
return ErrInsufficientFunds
|
return core.ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
cost = tx.TxCost(number)
|
cost = tx.TxCost(number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if new(big.Int).Add(balance, feeCapacity).Cmp(cost) < 0 {
|
if new(big.Int).Add(balance, feeCapacity).Cmp(cost) < 0 {
|
||||||
return ErrInsufficientFunds
|
return core.ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.To() == nil || (tx.To() != nil && !tx.IsSpecialTransaction()) {
|
if tx.To() == nil || (tx.To() != nil && !tx.IsSpecialTransaction()) {
|
||||||
// Ensure the transaction has more gas than the basic tx fee.
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Exclude check smart contract sign address.
|
// Exclude check smart contract sign address.
|
||||||
if tx.Gas() < intrGas {
|
if tx.Gas() < intrGas {
|
||||||
return ErrIntrinsicGas
|
return core.ErrIntrinsicGas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check zero gas price.
|
// Check zero gas price.
|
||||||
|
|
@ -670,13 +732,13 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
// validate minFee slot for XDCZ
|
// validate minFee slot for XDCZ
|
||||||
if tx.IsXDCZApplyTransaction() {
|
if tx.IsXDCZApplyTransaction() {
|
||||||
copyState := pool.currentState.Copy()
|
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
|
// validate balance slot, token decimal for XDCX
|
||||||
if tx.IsXDCXApplyTransaction() {
|
if tx.IsXDCXApplyTransaction() {
|
||||||
copyState := pool.currentState.Copy()
|
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
|
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.
|
// 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
|
// 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
|
// be added to the allowlist, preventing any associated transaction from being dropped
|
||||||
// due to pricing constraints.
|
// out of the pool due to pricing constraints.
|
||||||
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
|
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
|
||||||
// If the transaction is already known, discard it
|
// If the transaction is already known, discard it
|
||||||
hash := tx.Hash()
|
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)
|
return pool.promoteSpecialTx(from, tx, isLocal)
|
||||||
}
|
}
|
||||||
// If the transaction pool is full, discard underpriced transactions
|
// If the transaction pool is full, discard underpriced transactions
|
||||||
if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
|
if uint64(pool.all.Slots()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue {
|
||||||
log.Debug("Add transaction to pool full", "hash", hash, "nonce", tx.Nonce())
|
|
||||||
// If the new transaction is underpriced, don't accept it
|
// If the new transaction is underpriced, don't accept it
|
||||||
if !isLocal && pool.priced.Underpriced(tx) {
|
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)
|
underpricedTxMeter.Mark(1)
|
||||||
return false, ErrUnderpriced
|
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.
|
// New transaction is better than our worse ones, make room for it.
|
||||||
// If it's a local transaction, forcibly discard all available transactions.
|
// 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.
|
// 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)
|
overflowedTxMeter.Mark(1)
|
||||||
return false, ErrTxPoolOverflow
|
return false, ErrTxPoolOverflow
|
||||||
}
|
}
|
||||||
|
// Bump the counter of rejections-since-reorg
|
||||||
|
pool.changesSinceReorg += len(drop)
|
||||||
// Kick out the underpriced remote transactions.
|
// Kick out the underpriced remote transactions.
|
||||||
for _, tx := range drop {
|
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)
|
underpricedTxMeter.Mark(1)
|
||||||
pool.removeTx(tx.Hash(), false)
|
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
|
// Try to insert the transaction into the future queue
|
||||||
from, _ := types.Sender(pool.signer, tx) // already validated
|
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||||
if pool.queue[from] == nil {
|
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)
|
inserted, old := pool.queue[from].Add(tx, pool.config.PriceBump)
|
||||||
if !inserted {
|
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 {
|
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
|
||||||
// Try to insert the transaction into the pending queue
|
// Try to insert the transaction into the pending queue
|
||||||
if pool.pending[addr] == nil {
|
if pool.pending[addr] == nil {
|
||||||
pool.pending[addr] = newTxList(true)
|
pool.pending[addr] = newList(true)
|
||||||
}
|
}
|
||||||
list := pool.pending[addr]
|
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) {
|
func (pool *TxPool) promoteSpecialTx(addr common.Address, tx *types.Transaction, isLocal bool) (bool, error) {
|
||||||
// Try to insert the transaction into the pending queue
|
// Try to insert the transaction into the pending queue
|
||||||
if pool.pending[addr] == nil {
|
if pool.pending[addr] == nil {
|
||||||
pool.pending[addr] = newTxList(true)
|
pool.pending[addr] = newList(true)
|
||||||
}
|
}
|
||||||
list := pool.pending[addr]
|
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
|
// Set the potentially new pending nonce and notify any subsystems of the new tx
|
||||||
pool.beats[addr] = time.Now()
|
pool.beats[addr] = time.Now()
|
||||||
pool.pendingNonces.set(addr, tx.Nonce()+1)
|
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
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1146,7 +1218,7 @@ func (pool *TxPool) scheduleReorgLoop() {
|
||||||
launchNextRun bool
|
launchNextRun bool
|
||||||
reset *txpoolResetRequest
|
reset *txpoolResetRequest
|
||||||
dirtyAccounts *accountSet
|
dirtyAccounts *accountSet
|
||||||
queuedEvents = make(map[common.Address]*txSortedMap)
|
queuedEvents = make(map[common.Address]*sortedMap)
|
||||||
)
|
)
|
||||||
for {
|
for {
|
||||||
// Launch next background reorg if needed
|
// Launch next background reorg if needed
|
||||||
|
|
@ -1159,7 +1231,7 @@ func (pool *TxPool) scheduleReorgLoop() {
|
||||||
launchNextRun = false
|
launchNextRun = false
|
||||||
|
|
||||||
reset, dirtyAccounts = nil, nil
|
reset, dirtyAccounts = nil, nil
|
||||||
queuedEvents = make(map[common.Address]*txSortedMap)
|
queuedEvents = make(map[common.Address]*sortedMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
@ -1188,7 +1260,7 @@ func (pool *TxPool) scheduleReorgLoop() {
|
||||||
// request one later if they want the events sent.
|
// request one later if they want the events sent.
|
||||||
addr, _ := types.Sender(pool.signer, tx)
|
addr, _ := types.Sender(pool.signer, tx)
|
||||||
if _, ok := queuedEvents[addr]; !ok {
|
if _, ok := queuedEvents[addr]; !ok {
|
||||||
queuedEvents[addr] = newTxSortedMap()
|
queuedEvents[addr] = newSortedMap()
|
||||||
}
|
}
|
||||||
queuedEvents[addr].Put(tx)
|
queuedEvents[addr].Put(tx)
|
||||||
|
|
||||||
|
|
@ -1207,7 +1279,10 @@ func (pool *TxPool) scheduleReorgLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// runReorg runs reset and promoteExecutables on behalf of 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)
|
defer close(done)
|
||||||
|
|
||||||
var promoteAddrs []common.Address
|
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).
|
// because of another transaction (e.g. higher gas price).
|
||||||
if reset != nil {
|
if reset != nil {
|
||||||
pool.demoteUnexecutables()
|
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.
|
// Ensure pool.queue and pool.pending sizes stay within the configured limits.
|
||||||
pool.truncatePending()
|
pool.truncatePending()
|
||||||
pool.truncateQueue()
|
pool.truncateQueue()
|
||||||
|
|
||||||
// Update all accounts to the latest known pending nonce
|
dropBetweenReorgHistogram.Update(int64(pool.changesSinceReorg))
|
||||||
for addr, list := range pool.pending {
|
pool.changesSinceReorg = 0 // Reset change counter
|
||||||
highestPending := list.LastElement()
|
|
||||||
pool.pendingNonces.set(addr, highestPending.Nonce()+1)
|
|
||||||
}
|
|
||||||
pool.mu.Unlock()
|
pool.mu.Unlock()
|
||||||
|
|
||||||
// Notify subsystems for newly added transactions
|
// Notify subsystems for newly added transactions
|
||||||
for _, tx := range promoted {
|
for _, tx := range promoted {
|
||||||
addr, _ := types.Sender(pool.signer, tx)
|
addr, _ := types.Sender(pool.signer, tx)
|
||||||
if _, ok := events[addr]; !ok {
|
if _, ok := events[addr]; !ok {
|
||||||
events[addr] = newTxSortedMap()
|
events[addr] = newSortedMap()
|
||||||
}
|
}
|
||||||
events[addr].Put(tx)
|
events[addr].Put(tx)
|
||||||
}
|
}
|
||||||
|
|
@ -1268,7 +1351,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
|
||||||
for _, set := range events {
|
for _, set := range events {
|
||||||
txs = append(txs, set.Flatten()...)
|
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 {
|
if rem == nil {
|
||||||
// This can happen if a setHead is performed, where we simply discard the old
|
// This can happen if a setHead is performed, where we simply discard the old
|
||||||
// head from the chain.
|
// 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
|
// there's nothing to add
|
||||||
if newNum >= oldNum {
|
if newNum >= oldNum {
|
||||||
// If we reorged to a same or higher number, then it's not a case of setHead
|
// 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.currentState = statedb
|
||||||
pool.trc21FeeCapacity = state.GetTRC21FeeCapacityFromStateWithCache(newHead.Root, statedb)
|
pool.trc21FeeCapacity = state.GetTRC21FeeCapacityFromStateWithCache(newHead.Root, statedb)
|
||||||
pool.pendingNonces = newTxNoncer(statedb)
|
pool.pendingNonces = newNoncer(statedb)
|
||||||
pool.currentMaxGas = newHead.GasLimit
|
pool.currentMaxGas = newHead.GasLimit
|
||||||
|
|
||||||
// Inject any transactions discarded due to reorgs
|
// Inject any transactions discarded due to reorgs
|
||||||
log.Debug("Reinjecting stale transactions", "count", len(reinject))
|
log.Debug("Reinjecting stale transactions", "count", len(reinject))
|
||||||
senderCacher.recover(pool.signer, reinject)
|
core.SenderCacher.Recover(pool.signer, reinject)
|
||||||
pool.addTxsLocked(reinject, false)
|
pool.addTxsLocked(reinject, false)
|
||||||
|
|
||||||
// Update all fork indicator by next pending block number.
|
// Update all fork indicator by next pending block number.
|
||||||
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
|
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
|
||||||
pool.eip2718 = pool.chainconfig.IsEIP1559(next)
|
pool.eip2718 = pool.chainconfig.IsEIP1559(next)
|
||||||
|
pool.eip1559 = pool.chainconfig.IsEIP1559(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
// promoteExecutables moves transactions that have become processable from the
|
// promoteExecutables moves transactions that have become processable from the
|
||||||
|
|
@ -1524,7 +1608,7 @@ func (pool *TxPool) truncatePending() {
|
||||||
pendingRateLimitMeter.Mark(int64(pendingBeforeCap - pending))
|
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() {
|
func (pool *TxPool) truncateQueue() {
|
||||||
queued := uint64(0)
|
queued := uint64(0)
|
||||||
for _, list := range pool.queue {
|
for _, list := range pool.queue {
|
||||||
|
|
@ -1541,7 +1625,7 @@ func (pool *TxPool) truncateQueue() {
|
||||||
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
|
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
|
// Drop transactions until the total is below the limit or only locals remain
|
||||||
for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; {
|
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
|
// demoteUnexecutables removes invalid and processed transactions from the pools
|
||||||
// executable/pending queue and any subsequent transactions that become unexecutable
|
// executable/pending queue and any subsequent transactions that become unexecutable
|
||||||
// are moved back into the future queue.
|
// 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() {
|
func (pool *TxPool) demoteUnexecutables() {
|
||||||
// Iterate over all accounts and demote any non-executable transactions
|
// Iterate over all accounts and demote any non-executable transactions
|
||||||
for addr, list := range pool.pending {
|
for addr, list := range pool.pending {
|
||||||
|
|
@ -1620,8 +1708,6 @@ func (pool *TxPool) demoteUnexecutables() {
|
||||||
pool.enqueueTx(hash, tx, false, false)
|
pool.enqueueTx(hash, tx, false, false)
|
||||||
}
|
}
|
||||||
pendingGauge.Dec(int64(len(gapped)))
|
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.
|
// Delete the entire pending entry if it became empty.
|
||||||
if list.Empty() {
|
if list.Empty() {
|
||||||
|
|
@ -1654,7 +1740,7 @@ type accountSet struct {
|
||||||
// derivations.
|
// derivations.
|
||||||
func newAccountSet(signer types.Signer, addrs ...common.Address) *accountSet {
|
func newAccountSet(signer types.Signer, addrs ...common.Address) *accountSet {
|
||||||
as := &accountSet{
|
as := &accountSet{
|
||||||
accounts: make(map[common.Address]struct{}),
|
accounts: make(map[common.Address]struct{}, len(addrs)),
|
||||||
signer: signer,
|
signer: signer,
|
||||||
}
|
}
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
|
|
@ -1716,7 +1802,7 @@ func (as *accountSet) merge(other *accountSet) {
|
||||||
as.cache = nil
|
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.
|
// lookup without mutex contention.
|
||||||
//
|
//
|
||||||
// Note, although this type is properly protected against concurrent access, it
|
// 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
|
// This lookup set combines the notion of "local transactions", which is useful
|
||||||
// to build upper-level structure.
|
// to build upper-level structure.
|
||||||
type txLookup struct {
|
type lookup struct {
|
||||||
slots int
|
slots int
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
locals map[common.Hash]*types.Transaction
|
locals map[common.Hash]*types.Transaction
|
||||||
remotes map[common.Hash]*types.Transaction
|
remotes map[common.Hash]*types.Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTxLookup returns a new txLookup structure.
|
// newLookup returns a new lookup structure.
|
||||||
func newTxLookup() *txLookup {
|
func newLookup() *lookup {
|
||||||
return &txLookup{
|
return &lookup{
|
||||||
locals: make(map[common.Hash]*types.Transaction),
|
locals: make(map[common.Hash]*types.Transaction),
|
||||||
remotes: 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
|
// 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.
|
// should return the indicator whether the iteration needs to be continued.
|
||||||
// Callers need to specify which set (or both) to be iterated.
|
// 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()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
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.
|
// 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()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
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.
|
// 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()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
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.
|
// 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()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
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.
|
// Count returns the current number of transactions in the lookup.
|
||||||
func (t *txLookup) Count() int {
|
func (t *lookup) Count() int {
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
|
@ -1802,7 +1888,7 @@ func (t *txLookup) Count() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalCount returns the current number of local transactions in the lookup.
|
// LocalCount returns the current number of local transactions in the lookup.
|
||||||
func (t *txLookup) LocalCount() int {
|
func (t *lookup) LocalCount() int {
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
|
@ -1810,7 +1896,7 @@ func (t *txLookup) LocalCount() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoteCount returns the current number of remote transactions in the lookup.
|
// RemoteCount returns the current number of remote transactions in the lookup.
|
||||||
func (t *txLookup) RemoteCount() int {
|
func (t *lookup) RemoteCount() int {
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
|
@ -1818,7 +1904,7 @@ func (t *txLookup) RemoteCount() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slots returns the current number of slots used in the lookup.
|
// Slots returns the current number of slots used in the lookup.
|
||||||
func (t *txLookup) Slots() int {
|
func (t *lookup) Slots() int {
|
||||||
t.lock.RLock()
|
t.lock.RLock()
|
||||||
defer t.lock.RUnlock()
|
defer t.lock.RUnlock()
|
||||||
|
|
||||||
|
|
@ -1826,7 +1912,7 @@ func (t *txLookup) Slots() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a transaction to the lookup.
|
// 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()
|
t.lock.Lock()
|
||||||
defer t.lock.Unlock()
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -1841,7 +1927,7 @@ func (t *txLookup) Add(tx *types.Transaction, local bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes a transaction from the lookup.
|
// Remove removes a transaction from the lookup.
|
||||||
func (t *txLookup) Remove(hash common.Hash) {
|
func (t *lookup) Remove(hash common.Hash) {
|
||||||
t.lock.Lock()
|
t.lock.Lock()
|
||||||
defer t.lock.Unlock()
|
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
|
// 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.
|
// 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()
|
t.lock.Lock()
|
||||||
defer t.lock.Unlock()
|
defer t.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -1877,6 +1963,18 @@ func (t *txLookup) RemoteToLocals(locals *accountSet) int {
|
||||||
return migrated
|
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.
|
// numSlots calculates the number of slots needed for a single transaction.
|
||||||
func numSlots(tx *types.Transaction) int {
|
func numSlots(tx *types.Transaction) int {
|
||||||
return int((tx.Size() + txSlotSize - 1) / txSlotSize)
|
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"`
|
Validators []byte `json:"validators" gencodec:"required"`
|
||||||
Validator []byte `json:"validator" gencodec:"required"`
|
Validator []byte `json:"validator" gencodec:"required"`
|
||||||
Penalties []byte `json:"penalties" 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
|
// field type overrides for gencodec
|
||||||
|
|
@ -91,6 +94,7 @@ type headerMarshaling struct {
|
||||||
GasUsed hexutil.Uint64
|
GasUsed hexutil.Uint64
|
||||||
Time *hexutil.Big
|
Time *hexutil.Big
|
||||||
Extra hexutil.Bytes
|
Extra hexutil.Bytes
|
||||||
|
BaseFee *hexutil.Big
|
||||||
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
|
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 {
|
if cpy.Number = new(big.Int); h.Number != nil {
|
||||||
cpy.Number.Set(h.Number)
|
cpy.Number.Set(h.Number)
|
||||||
}
|
}
|
||||||
|
if h.BaseFee != nil {
|
||||||
|
cpy.BaseFee = new(big.Int).Set(h.BaseFee)
|
||||||
|
}
|
||||||
if len(h.Extra) > 0 {
|
if len(h.Extra) > 0 {
|
||||||
cpy.Extra = make([]byte, len(h.Extra))
|
cpy.Extra = make([]byte, len(h.Extra))
|
||||||
copy(cpy.Extra, 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) Penalties() []byte { return common.CopyBytes(b.header.Penalties) }
|
||||||
func (b *Block) Validator() []byte { return common.CopyBytes(b.header.Validator) }
|
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) }
|
func (b *Block) Header() *Header { return CopyHeader(b.header) }
|
||||||
|
|
||||||
// Body returns the non-header content of the block.
|
// Body returns the non-header content of the block.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
var _ = (*headerMarshaling)(nil)
|
var _ = (*headerMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
func (h Header) MarshalJSON() ([]byte, error) {
|
func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
type Header struct {
|
type Header struct {
|
||||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
|
|
@ -30,6 +31,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||||
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
|
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
|
||||||
Nonce BlockNonce `json:"nonce" gencodec:"required"`
|
Nonce BlockNonce `json:"nonce" gencodec:"required"`
|
||||||
|
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
var enc Header
|
var enc Header
|
||||||
|
|
@ -48,10 +50,12 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
||||||
enc.Extra = h.Extra
|
enc.Extra = h.Extra
|
||||||
enc.MixDigest = h.MixDigest
|
enc.MixDigest = h.MixDigest
|
||||||
enc.Nonce = h.Nonce
|
enc.Nonce = h.Nonce
|
||||||
|
enc.BaseFee = (*hexutil.Big)(h.BaseFee)
|
||||||
enc.Hash = h.Hash()
|
enc.Hash = h.Hash()
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
func (h *Header) UnmarshalJSON(input []byte) error {
|
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||||
type Header struct {
|
type Header struct {
|
||||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
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"`
|
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||||
MixDigest *common.Hash `json:"mixHash" gencodec:"required"`
|
MixDigest *common.Hash `json:"mixHash" gencodec:"required"`
|
||||||
Nonce *BlockNonce `json:"nonce" gencodec:"required"`
|
Nonce *BlockNonce `json:"nonce" gencodec:"required"`
|
||||||
|
BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"`
|
||||||
}
|
}
|
||||||
var dec Header
|
var dec Header
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
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")
|
return errors.New("missing required field 'nonce' for Header")
|
||||||
}
|
}
|
||||||
h.Nonce = *dec.Nonce
|
h.Nonce = *dec.Nonce
|
||||||
|
if dec.BaseFee != nil {
|
||||||
|
h.BaseFee = (*big.Int)(dec.BaseFee)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||||
ContractAddress common.Address `json:"contractAddress"`
|
ContractAddress common.Address `json:"contractAddress"`
|
||||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
|
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
|
||||||
BlockHash common.Hash `json:"blockHash,omitempty"`
|
BlockHash common.Hash `json:"blockHash,omitempty"`
|
||||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||||
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
TransactionIndex hexutil.Uint `json:"transactionIndex"`
|
||||||
|
|
@ -39,6 +40,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) {
|
||||||
enc.TxHash = r.TxHash
|
enc.TxHash = r.TxHash
|
||||||
enc.ContractAddress = r.ContractAddress
|
enc.ContractAddress = r.ContractAddress
|
||||||
enc.GasUsed = hexutil.Uint64(r.GasUsed)
|
enc.GasUsed = hexutil.Uint64(r.GasUsed)
|
||||||
|
enc.EffectiveGasPrice = (*hexutil.Big)(r.EffectiveGasPrice)
|
||||||
enc.BlockHash = r.BlockHash
|
enc.BlockHash = r.BlockHash
|
||||||
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
enc.BlockNumber = (*hexutil.Big)(r.BlockNumber)
|
||||||
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
enc.TransactionIndex = hexutil.Uint(r.TransactionIndex)
|
||||||
|
|
@ -57,6 +59,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||||
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
|
||||||
ContractAddress *common.Address `json:"contractAddress"`
|
ContractAddress *common.Address `json:"contractAddress"`
|
||||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
|
EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"`
|
||||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||||
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
BlockNumber *hexutil.Big `json:"blockNumber,omitempty"`
|
||||||
TransactionIndex *hexutil.Uint `json:"transactionIndex"`
|
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")
|
return errors.New("missing required field 'gasUsed' for Receipt")
|
||||||
}
|
}
|
||||||
r.GasUsed = uint64(*dec.GasUsed)
|
r.GasUsed = uint64(*dec.GasUsed)
|
||||||
|
if dec.EffectiveGasPrice != nil {
|
||||||
|
r.EffectiveGasPrice = (*big.Int)(dec.EffectiveGasPrice)
|
||||||
|
}
|
||||||
if dec.BlockHash != nil {
|
if dec.BlockHash != nil {
|
||||||
r.BlockHash = *dec.BlockHash
|
r.BlockHash = *dec.BlockHash
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,7 @@ var (
|
||||||
receiptStatusSuccessfulRLP = []byte{0x01}
|
receiptStatusSuccessfulRLP = []byte{0x01}
|
||||||
)
|
)
|
||||||
|
|
||||||
// This error is returned when a typed receipt is decoded, but the string is empty.
|
var errShortTypedReceipt = errors.New("typed receipt too short")
|
||||||
var errEmptyTypedReceipt = errors.New("empty typed receipt bytes")
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
||||||
|
|
@ -60,10 +59,10 @@ type Receipt struct {
|
||||||
Logs []*Log `json:"logs" gencodec:"required"`
|
Logs []*Log `json:"logs" gencodec:"required"`
|
||||||
|
|
||||||
// Implementation fields: These fields are added by geth when processing a transaction.
|
// 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"`
|
||||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
ContractAddress common.Address `json:"contractAddress"`
|
||||||
ContractAddress common.Address `json:"contractAddress"`
|
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
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
|
// Inclusion information: These fields provide information about the inclusion of the
|
||||||
// transaction corresponding to this receipt.
|
// transaction corresponding to this receipt.
|
||||||
|
|
@ -78,6 +77,7 @@ type receiptMarshaling struct {
|
||||||
Status hexutil.Uint64
|
Status hexutil.Uint64
|
||||||
CumulativeGasUsed hexutil.Uint64
|
CumulativeGasUsed hexutil.Uint64
|
||||||
GasUsed hexutil.Uint64
|
GasUsed hexutil.Uint64
|
||||||
|
EffectiveGasPrice *hexutil.Big
|
||||||
BlockNumber *hexutil.Big
|
BlockNumber *hexutil.Big
|
||||||
TransactionIndex hexutil.Uint
|
TransactionIndex hexutil.Uint
|
||||||
}
|
}
|
||||||
|
|
@ -90,18 +90,8 @@ type receiptRLP struct {
|
||||||
Logs []*Log
|
Logs []*Log
|
||||||
}
|
}
|
||||||
|
|
||||||
// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
|
// receiptStorageRLP is the original storage encoding of a receipt including some unnecessary fields.
|
||||||
type v4StoredReceiptRLP struct {
|
type receiptStorageRLP 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 {
|
|
||||||
PostStateOrStatus []byte
|
PostStateOrStatus []byte
|
||||||
CumulativeGasUsed uint64
|
CumulativeGasUsed uint64
|
||||||
Bloom Bloom
|
Bloom Bloom
|
||||||
|
|
@ -134,20 +124,33 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
|
||||||
if r.Type == LegacyTxType {
|
if r.Type == LegacyTxType {
|
||||||
return rlp.Encode(w, data)
|
return rlp.Encode(w, data)
|
||||||
}
|
}
|
||||||
// It's an EIP-2718 typed TX receipt.
|
|
||||||
if r.Type != AccessListTxType {
|
|
||||||
return ErrTxTypeNotSupported
|
|
||||||
}
|
|
||||||
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
buf := encodeBufferPool.Get().(*bytes.Buffer)
|
||||||
defer encodeBufferPool.Put(buf)
|
defer encodeBufferPool.Put(buf)
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
buf.WriteByte(r.Type)
|
buf.WriteByte(r.Type)
|
||||||
if err := rlp.Encode(buf, data); err != nil {
|
if err := r.encodeTyped(data, buf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return rlp.Encode(w, buf.Bytes())
|
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
|
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
||||||
// from an RLP stream.
|
// from an RLP stream.
|
||||||
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||||
|
|
@ -163,26 +166,49 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
||||||
}
|
}
|
||||||
r.Type = LegacyTxType
|
r.Type = LegacyTxType
|
||||||
return r.setFromRLP(dec)
|
return r.setFromRLP(dec)
|
||||||
case kind == rlp.String:
|
default:
|
||||||
// It's an EIP-2718 typed tx receipt.
|
// It's an EIP-2718 typed tx receipt.
|
||||||
b, err := s.Bytes()
|
b, err := s.Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(b) == 0 {
|
return r.decodeTyped(b)
|
||||||
return errEmptyTypedReceipt
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]
|
r.Type = b[0]
|
||||||
if r.Type == AccessListTxType {
|
return r.setFromRLP(data)
|
||||||
var dec receiptRLP
|
|
||||||
if err := rlp.DecodeBytes(b[1:], &dec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return r.setFromRLP(dec)
|
|
||||||
}
|
|
||||||
return ErrTxTypeNotSupported
|
|
||||||
default:
|
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
|
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||||
// into an RLP stream.
|
// into an RLP stream.
|
||||||
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
||||||
enc := &v3StoredReceiptRLP{
|
enc := &receiptStorageRLP{
|
||||||
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
|
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
|
||||||
CumulativeGasUsed: r.CumulativeGasUsed,
|
CumulativeGasUsed: r.CumulativeGasUsed,
|
||||||
Bloom: r.Bloom,
|
Bloom: r.Bloom,
|
||||||
|
|
@ -264,17 +290,11 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Try decoding from the newest format for future proofness, then the older one
|
return decodeStoredReceiptRLP(r, blob)
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||||
var stored v3StoredReceiptRLP
|
var stored receiptStorageRLP
|
||||||
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -295,36 +315,15 @@ func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||||
return nil
|
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.
|
// Receipts implements DerivableList for receipts.
|
||||||
type Receipts []*Receipt
|
type Receipts []*Receipt
|
||||||
|
|
||||||
// Len returns the number of receipts in this list.
|
// 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.
|
// GetRlp returns the RLP encoding of one receipt from the list.
|
||||||
func (r Receipts) GetRlp(i int) []byte {
|
func (rs Receipts) GetRlp(i int) []byte {
|
||||||
bytes, err := rlp.EncodeToBytes(r[i])
|
bytes, err := rlp.EncodeToBytes(rs[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -333,42 +332,48 @@ func (r Receipts) GetRlp(i int) []byte {
|
||||||
|
|
||||||
// DeriveFields fills the receipts with their computed fields based on consensus
|
// DeriveFields fills the receipts with their computed fields based on consensus
|
||||||
// data and contextual infos like containing block and transactions.
|
// 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))
|
signer := MakeSigner(config, new(big.Int).SetUint64(number))
|
||||||
|
|
||||||
logIndex := uint(0)
|
logIndex := uint(0)
|
||||||
if len(txs) != len(r) {
|
if len(txs) != len(rs) {
|
||||||
return errors.New("transaction and receipt count mismatch")
|
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
|
// The transaction type and hash can be retrieved from the transaction itself
|
||||||
r[i].Type = txs[i].Type()
|
rs[i].Type = txs[i].Type()
|
||||||
r[i].TxHash = txs[i].Hash()
|
rs[i].TxHash = txs[i].Hash()
|
||||||
|
|
||||||
|
rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee)
|
||||||
|
|
||||||
// block location fields
|
// block location fields
|
||||||
r[i].BlockHash = hash
|
rs[i].BlockHash = hash
|
||||||
r[i].BlockNumber = new(big.Int).SetUint64(number)
|
rs[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||||
r[i].TransactionIndex = uint(i)
|
rs[i].TransactionIndex = uint(i)
|
||||||
|
|
||||||
// The contract address can be derived from the transaction itself
|
// The contract address can be derived from the transaction itself
|
||||||
if txs[i].To() == nil {
|
if txs[i].To() == nil {
|
||||||
// Deriving the signer is expensive, only do if it's actually needed
|
// Deriving the signer is expensive, only do if it's actually needed
|
||||||
from, _ := Sender(signer, txs[i])
|
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
|
// The used gas can be calculated based on previous r
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
r[i].GasUsed = r[i].CumulativeGasUsed
|
rs[i].GasUsed = rs[i].CumulativeGasUsed
|
||||||
} else {
|
} 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
|
// The derived log fields can simply be set from the block and transaction
|
||||||
for j := 0; j < len(r[i].Logs); j++ {
|
for j := 0; j < len(rs[i].Logs); j++ {
|
||||||
r[i].Logs[j].BlockNumber = number
|
rs[i].Logs[j].BlockNumber = number
|
||||||
r[i].Logs[j].BlockHash = hash
|
rs[i].Logs[j].BlockHash = hash
|
||||||
r[i].Logs[j].TxHash = r[i].TxHash
|
rs[i].Logs[j].TxHash = rs[i].TxHash
|
||||||
r[i].Logs[j].TxIndex = uint(i)
|
rs[i].Logs[j].TxIndex = uint(i)
|
||||||
r[i].Logs[j].Index = logIndex
|
rs[i].Logs[j].Index = logIndex
|
||||||
logIndex++
|
logIndex++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,35 +18,278 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"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) {
|
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||||
input := []byte{0x80}
|
input := []byte{0x80}
|
||||||
var r Receipt
|
var r Receipt
|
||||||
err := rlp.DecodeBytes(input, &r)
|
err := rlp.DecodeBytes(input, &r)
|
||||||
if err != errEmptyTypedReceipt {
|
if err != errShortTypedReceipt {
|
||||||
t.Fatal("wrong error:", err)
|
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) {
|
func TestLegacyReceiptDecoding(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
encode func(*Receipt) ([]byte, error)
|
encode func(*Receipt) ([]byte, error)
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
"V4StoredReceiptRLP",
|
|
||||||
encodeAsV4StoredReceiptRLP,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"V3StoredReceiptRLP",
|
"V3StoredReceiptRLP",
|
||||||
encodeAsV3StoredReceiptRLP,
|
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) {
|
func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||||
stored := &v3StoredReceiptRLP{
|
stored := &receiptStorageRLP{
|
||||||
PostStateOrStatus: want.statusEncoding(),
|
PostStateOrStatus: want.statusEncoding(),
|
||||||
CumulativeGasUsed: want.CumulativeGasUsed,
|
CumulativeGasUsed: want.CumulativeGasUsed,
|
||||||
Bloom: want.Bloom,
|
Bloom: want.Bloom,
|
||||||
|
|
@ -149,162 +377,212 @@ func TestDeriveFields(t *testing.T) {
|
||||||
// Create a few transactions to have receipts for
|
// Create a few transactions to have receipts for
|
||||||
to2 := common.HexToAddress("0x2")
|
to2 := common.HexToAddress("0x2")
|
||||||
to3 := common.HexToAddress("0x3")
|
to3 := common.HexToAddress("0x3")
|
||||||
|
to4 := common.HexToAddress("0x4")
|
||||||
|
to5 := common.HexToAddress("0x5")
|
||||||
txs := Transactions{
|
txs := Transactions{
|
||||||
NewTx(&LegacyTx{
|
NewTx(&LegacyTx{
|
||||||
Nonce: 1,
|
Nonce: 1,
|
||||||
Value: big.NewInt(1),
|
Value: big.NewInt(1),
|
||||||
Gas: 1,
|
Gas: 1,
|
||||||
GasPrice: big.NewInt(1),
|
GasPrice: big.NewInt(11),
|
||||||
}),
|
}),
|
||||||
NewTx(&LegacyTx{
|
NewTx(&LegacyTx{
|
||||||
To: &to2,
|
To: &to2,
|
||||||
Nonce: 2,
|
Nonce: 2,
|
||||||
Value: big.NewInt(2),
|
Value: big.NewInt(2),
|
||||||
Gas: 2,
|
Gas: 2,
|
||||||
GasPrice: big.NewInt(2),
|
GasPrice: big.NewInt(22),
|
||||||
}),
|
}),
|
||||||
NewTx(&AccessListTx{
|
NewTx(&AccessListTx{
|
||||||
To: &to3,
|
To: &to3,
|
||||||
Nonce: 3,
|
Nonce: 3,
|
||||||
Value: big.NewInt(3),
|
Value: big.NewInt(3),
|
||||||
Gas: 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
|
// Create the corresponding receipts
|
||||||
receipts := Receipts{
|
receipts := Receipts{
|
||||||
&Receipt{
|
&Receipt{
|
||||||
Status: ReceiptStatusFailed,
|
Status: ReceiptStatusFailed,
|
||||||
CumulativeGasUsed: 1,
|
CumulativeGasUsed: 1,
|
||||||
Logs: []*Log{
|
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(),
|
// derived fields:
|
||||||
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
TxHash: txs[0].Hash(),
|
||||||
GasUsed: 1,
|
ContractAddress: common.HexToAddress("0x5a443704dd4b594b382c22a083e2bd3090a6fef3"),
|
||||||
|
GasUsed: 1,
|
||||||
|
EffectiveGasPrice: big.NewInt(11),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 0,
|
||||||
},
|
},
|
||||||
&Receipt{
|
&Receipt{
|
||||||
PostState: common.Hash{2}.Bytes(),
|
PostState: common.Hash{2}.Bytes(),
|
||||||
CumulativeGasUsed: 3,
|
CumulativeGasUsed: 3,
|
||||||
Logs: []*Log{
|
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(),
|
// derived fields:
|
||||||
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
TxHash: txs[1].Hash(),
|
||||||
GasUsed: 2,
|
GasUsed: 2,
|
||||||
|
EffectiveGasPrice: big.NewInt(22),
|
||||||
|
BlockHash: blockHash,
|
||||||
|
BlockNumber: blockNumber,
|
||||||
|
TransactionIndex: 1,
|
||||||
},
|
},
|
||||||
&Receipt{
|
&Receipt{
|
||||||
Type: AccessListTxType,
|
Type: AccessListTxType,
|
||||||
PostState: common.Hash{3}.Bytes(),
|
PostState: common.Hash{3}.Bytes(),
|
||||||
CumulativeGasUsed: 6,
|
CumulativeGasUsed: 6,
|
||||||
Logs: []*Log{
|
Logs: []*Log{},
|
||||||
{Address: common.BytesToAddress([]byte{0x33})},
|
// derived fields:
|
||||||
{Address: common.BytesToAddress([]byte{0x03, 0x33})},
|
TxHash: txs[2].Hash(),
|
||||||
},
|
GasUsed: 3,
|
||||||
TxHash: txs[2].Hash(),
|
EffectiveGasPrice: big.NewInt(33),
|
||||||
ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
|
BlockHash: blockHash,
|
||||||
GasUsed: 3,
|
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)
|
// Re-derive receipts.
|
||||||
if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil {
|
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)
|
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)
|
// Check diff of receipts against derivedReceipts.
|
||||||
for i := range receipts {
|
r1, err := json.MarshalIndent(receipts, "", " ")
|
||||||
if receipts[i].Type != txs[i].Type() {
|
if err != nil {
|
||||||
t.Errorf("receipts[%d].Type = %d, want %d", i, receipts[i].Type, txs[i].Type())
|
t.Fatal("error marshaling input receipts:", err)
|
||||||
}
|
}
|
||||||
if receipts[i].TxHash != txs[i].Hash() {
|
r2, err := json.MarshalIndent(derivedReceipts, "", " ")
|
||||||
t.Errorf("receipts[%d].TxHash = %s, want %s", i, receipts[i].TxHash.String(), txs[i].Hash().String())
|
if err != nil {
|
||||||
}
|
t.Fatal("error marshaling derived receipts:", err)
|
||||||
if receipts[i].BlockHash != hash {
|
}
|
||||||
t.Errorf("receipts[%d].BlockHash = %s, want %s", i, receipts[i].BlockHash.String(), hash.String())
|
d := diff.Diff(string(r1), string(r2))
|
||||||
}
|
if d != "" {
|
||||||
if receipts[i].BlockNumber.Cmp(number) != 0 {
|
t.Fatal("receipts differ:", d)
|
||||||
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++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
|
func clearComputedFieldsOnReceipts(receipts []*Receipt) []*Receipt {
|
||||||
t.Helper()
|
r := make([]*Receipt, len(receipts))
|
||||||
|
for i, receipt := range receipts {
|
||||||
for _, receipt := range receipts {
|
r[i] = clearComputedFieldsOnReceipt(receipt)
|
||||||
clearComputedFieldsOnReceipt(t, receipt)
|
|
||||||
}
|
}
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearComputedFieldsOnReceipt(t *testing.T, receipt *Receipt) {
|
func clearComputedFieldsOnReceipt(receipt *Receipt) *Receipt {
|
||||||
t.Helper()
|
cpy := *receipt
|
||||||
|
cpy.TxHash = common.Hash{0xff, 0xff, 0x11}
|
||||||
receipt.TxHash = common.Hash{}
|
cpy.BlockHash = common.Hash{0xff, 0xff, 0x22}
|
||||||
receipt.BlockHash = common.Hash{}
|
cpy.BlockNumber = big.NewInt(math.MaxUint32)
|
||||||
receipt.BlockNumber = big.NewInt(math.MaxUint32)
|
cpy.TransactionIndex = math.MaxUint32
|
||||||
receipt.TransactionIndex = math.MaxUint32
|
cpy.ContractAddress = common.Address{0xff, 0xff, 0x33}
|
||||||
receipt.ContractAddress = common.Address{}
|
cpy.GasUsed = 0xffffffff
|
||||||
receipt.GasUsed = 0
|
cpy.Logs = clearComputedFieldsOnLogs(receipt.Logs)
|
||||||
|
return &cpy
|
||||||
clearComputedFieldsOnLogs(t, receipt.Logs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearComputedFieldsOnLogs(t *testing.T, logs []*Log) {
|
func clearComputedFieldsOnLogs(logs []*Log) []*Log {
|
||||||
t.Helper()
|
l := make([]*Log, len(logs))
|
||||||
|
for i, log := range logs {
|
||||||
for _, log := range logs {
|
cpy := *log
|
||||||
clearComputedFieldsOnLog(t, log)
|
cpy.BlockNumber = math.MaxUint32
|
||||||
|
cpy.BlockHash = common.Hash{}
|
||||||
|
cpy.TxHash = common.Hash{}
|
||||||
|
cpy.TxIndex = math.MaxUint32
|
||||||
|
cpy.Index = math.MaxUint32
|
||||||
|
l[i] = &cpy
|
||||||
}
|
}
|
||||||
}
|
return l
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
|
// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
|
||||||
|
|
@ -334,3 +612,38 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
|
||||||
check(bundle)
|
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"
|
"time"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
|
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
|
||||||
var (
|
var (
|
||||||
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||||
ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
|
ErrUnexpectedProtection = errors.New("transaction type does not supported EIP-155 protected signatures")
|
||||||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||||
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||||
errShortTypedTx = errors.New("typed transaction too short")
|
errShortTypedTx = errors.New("typed transaction too short")
|
||||||
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
|
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
|
||||||
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
|
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
|
||||||
errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
|
errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
|
||||||
errEmptyTypedTx = errors.New("empty typed transaction bytes")
|
errNoSigner = errors.New("missing signing methods")
|
||||||
errNoSigner = errors.New("missing signing methods")
|
ErrFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||||
|
|
||||||
skipNonceDestinationAddress = map[common.Address]bool{
|
skipNonceDestinationAddress = map[common.Address]bool{
|
||||||
common.XDCXAddrBinary: true,
|
common.XDCXAddrBinary: true,
|
||||||
common.TradingStateAddrBinary: true,
|
common.TradingStateAddrBinary: true,
|
||||||
|
|
@ -56,6 +58,7 @@ var (
|
||||||
const (
|
const (
|
||||||
LegacyTxType = iota
|
LegacyTxType = iota
|
||||||
AccessListTxType
|
AccessListTxType
|
||||||
|
DynamicFeeTxType
|
||||||
)
|
)
|
||||||
|
|
||||||
// Transaction is an Ethereum transaction.
|
// Transaction is an Ethereum transaction.
|
||||||
|
|
@ -88,12 +91,22 @@ type TxData interface {
|
||||||
data() []byte
|
data() []byte
|
||||||
gas() uint64
|
gas() uint64
|
||||||
gasPrice() *big.Int
|
gasPrice() *big.Int
|
||||||
|
gasTipCap() *big.Int
|
||||||
|
gasFeeCap() *big.Int
|
||||||
value() *big.Int
|
value() *big.Int
|
||||||
nonce() uint64
|
nonce() uint64
|
||||||
to() *common.Address
|
to() *common.Address
|
||||||
|
|
||||||
rawSignatureValues() (v, r, s *big.Int)
|
rawSignatureValues() (v, r, s *big.Int)
|
||||||
setSignatureValues(chainID, 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
|
// EncodeRLP implements rlp.Encoder
|
||||||
|
|
@ -143,7 +156,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||||
tx.setDecoded(&inner, int(rlp.ListSize(size)))
|
tx.setDecoded(&inner, int(rlp.ListSize(size)))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
case kind == rlp.String:
|
default:
|
||||||
// It's an EIP-2718 typed TX envelope.
|
// It's an EIP-2718 typed TX envelope.
|
||||||
var b []byte
|
var b []byte
|
||||||
if b, err = s.Bytes(); err != nil {
|
if b, err = s.Bytes(); err != nil {
|
||||||
|
|
@ -154,8 +167,6 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||||
tx.setDecoded(inner, len(b))
|
tx.setDecoded(inner, len(b))
|
||||||
}
|
}
|
||||||
return err
|
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.
|
// decodeTyped decodes a typed transaction from the canonical format.
|
||||||
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
|
||||||
if len(b) == 0 {
|
if len(b) <= 1 {
|
||||||
return nil, errEmptyTypedTx
|
return nil, errShortTypedTx
|
||||||
}
|
}
|
||||||
switch b[0] {
|
switch b[0] {
|
||||||
case AccessListTxType:
|
case AccessListTxType:
|
||||||
var inner AccessListTx
|
var inner AccessListTx
|
||||||
err := rlp.DecodeBytes(b[1:], &inner)
|
err := rlp.DecodeBytes(b[1:], &inner)
|
||||||
return &inner, err
|
return &inner, err
|
||||||
|
case DynamicFeeTxType:
|
||||||
|
var inner DynamicFeeTx
|
||||||
|
err := rlp.DecodeBytes(b[1:], &inner)
|
||||||
|
return &inner, err
|
||||||
default:
|
default:
|
||||||
return nil, ErrTxTypeNotSupported
|
return nil, ErrTxTypeNotSupported
|
||||||
}
|
}
|
||||||
|
|
@ -274,6 +289,12 @@ func (tx *Transaction) Gas() uint64 { return tx.inner.gas() }
|
||||||
// GasPrice returns the gas price of the transaction.
|
// GasPrice returns the gas price of the transaction.
|
||||||
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
|
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.inner.gasPrice()) }
|
||||||
|
|
||||||
|
// 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.
|
// Value returns the ether amount of the transaction.
|
||||||
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
|
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.inner.value()) }
|
||||||
|
|
||||||
|
|
@ -283,13 +304,7 @@ func (tx *Transaction) Nonce() uint64 { return tx.inner.nonce() }
|
||||||
// To returns the recipient address of the transaction.
|
// To returns the recipient address of the transaction.
|
||||||
// For contract-creation transactions, To returns nil.
|
// For contract-creation transactions, To returns nil.
|
||||||
func (tx *Transaction) To() *common.Address {
|
func (tx *Transaction) To() *common.Address {
|
||||||
// Copy the pointed-to address.
|
return copyAddressPtr(tx.inner.to())
|
||||||
ito := tx.inner.to()
|
|
||||||
if ito == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cpy := *ito
|
|
||||||
return &cpy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *Transaction) From() *common.Address {
|
func (tx *Transaction) From() *common.Address {
|
||||||
|
|
@ -306,20 +321,75 @@ func (tx *Transaction) From() *common.Address {
|
||||||
return &from
|
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.
|
// RawSignatureValues returns the V, R, S signature values of the transaction.
|
||||||
// The return values should not be modified by the caller.
|
// The return values should not be modified by the caller.
|
||||||
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
||||||
return tx.inner.rawSignatureValues()
|
return tx.inner.rawSignatureValues()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GasPriceCmp compares the gas prices of two transactions.
|
// GasFeeCapCmp compares the fee cap of two transactions.
|
||||||
func (tx *Transaction) GasPriceCmp(other *Transaction) int {
|
func (tx *Transaction) GasFeeCapCmp(other *Transaction) int {
|
||||||
return tx.inner.gasPrice().Cmp(other.inner.gasPrice())
|
return tx.inner.gasFeeCap().Cmp(other.inner.gasFeeCap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GasPriceIntCmp compares the gas price of the transaction against the given price.
|
// GasFeeCapIntCmp compares the fee cap of the transaction against the given fee cap.
|
||||||
func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
|
func (tx *Transaction) GasFeeCapIntCmp(other *big.Int) int {
|
||||||
return tx.inner.gasPrice().Cmp(other)
|
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.
|
// Hash returns the transaction hash.
|
||||||
|
|
@ -350,31 +420,43 @@ func (tx *Transaction) Size() common.StorageSize {
|
||||||
return common.StorageSize(c)
|
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.
|
// 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{
|
msg := Message{
|
||||||
nonce: tx.Nonce(),
|
nonce: tx.Nonce(),
|
||||||
gasLimit: tx.Gas(),
|
gasLimit: tx.Gas(),
|
||||||
gasPrice: new(big.Int).Set(tx.GasPrice()),
|
gasPrice: new(big.Int).Set(tx.GasPrice()),
|
||||||
|
gasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
|
||||||
|
gasTipCap: new(big.Int).Set(tx.GasTipCap()),
|
||||||
to: tx.To(),
|
to: tx.To(),
|
||||||
amount: tx.Value(),
|
amount: tx.Value(),
|
||||||
data: tx.Data(),
|
data: tx.Data(),
|
||||||
accessList: tx.AccessList(),
|
accessList: tx.AccessList(),
|
||||||
checkNonce: true,
|
isFake: false,
|
||||||
balanceTokenFee: balanceFee,
|
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
|
var err error
|
||||||
msg.from, err = Sender(s, tx)
|
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
|
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
|
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.
|
// TxCost returns gas * gasPrice + value.
|
||||||
func (tx *Transaction) TxCost(number *big.Int) *big.Int {
|
func (tx *Transaction) TxCost(number *big.Int) *big.Int {
|
||||||
total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.Gas()))
|
total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.Gas()))
|
||||||
|
|
@ -742,13 +817,15 @@ type Message struct {
|
||||||
amount *big.Int
|
amount *big.Int
|
||||||
gasLimit uint64
|
gasLimit uint64
|
||||||
gasPrice *big.Int
|
gasPrice *big.Int
|
||||||
|
gasFeeCap *big.Int
|
||||||
|
gasTipCap *big.Int
|
||||||
data []byte
|
data []byte
|
||||||
accessList AccessList
|
accessList AccessList
|
||||||
checkNonce bool
|
isFake bool
|
||||||
balanceTokenFee *big.Int
|
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 {
|
if balanceTokenFee != nil {
|
||||||
gasPrice = common.GetGasPrice(number)
|
gasPrice = common.GetGasPrice(number)
|
||||||
}
|
}
|
||||||
|
|
@ -759,9 +836,11 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
||||||
amount: amount,
|
amount: amount,
|
||||||
gasLimit: gasLimit,
|
gasLimit: gasLimit,
|
||||||
gasPrice: gasPrice,
|
gasPrice: gasPrice,
|
||||||
|
gasFeeCap: gasFeeCap,
|
||||||
|
gasTipCap: gasTipCap,
|
||||||
data: data,
|
data: data,
|
||||||
accessList: accessList,
|
accessList: accessList,
|
||||||
checkNonce: checkNonce,
|
isFake: isFake,
|
||||||
balanceTokenFee: balanceTokenFee,
|
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) BalanceTokenFee() *big.Int { return m.balanceTokenFee }
|
||||||
func (m Message) To() *common.Address { return m.to }
|
func (m Message) To() *common.Address { return m.to }
|
||||||
func (m Message) GasPrice() *big.Int { return m.gasPrice }
|
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) Value() *big.Int { return m.amount }
|
||||||
func (m Message) Gas() uint64 { return m.gasLimit }
|
func (m Message) Gas() uint64 { return m.gasLimit }
|
||||||
func (m Message) Nonce() uint64 { return m.nonce }
|
func (m Message) Nonce() uint64 { return m.nonce }
|
||||||
func (m Message) Data() []byte { return m.data }
|
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) AccessList() AccessList { return m.accessList }
|
||||||
|
|
||||||
func (m *Message) SetNonce(nonce uint64) { m.nonce = nonce }
|
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 = new(big.Int).SetUint64(m.gasLimit)
|
||||||
m.balanceTokenFee.Mul(m.balanceTokenFee, m.gasPrice)
|
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
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -14,15 +30,17 @@ type txJSON struct {
|
||||||
Type hexutil.Uint64 `json:"type"`
|
Type hexutil.Uint64 `json:"type"`
|
||||||
|
|
||||||
// Common transaction fields:
|
// Common transaction fields:
|
||||||
Nonce *hexutil.Uint64 `json:"nonce"`
|
Nonce *hexutil.Uint64 `json:"nonce"`
|
||||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||||
Gas *hexutil.Uint64 `json:"gas"`
|
MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
|
||||||
Value *hexutil.Big `json:"value"`
|
MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
|
||||||
Data *hexutil.Bytes `json:"input"`
|
Gas *hexutil.Uint64 `json:"gas"`
|
||||||
V *hexutil.Big `json:"v"`
|
Value *hexutil.Big `json:"value"`
|
||||||
R *hexutil.Big `json:"r"`
|
Data *hexutil.Bytes `json:"input"`
|
||||||
S *hexutil.Big `json:"s"`
|
V *hexutil.Big `json:"v"`
|
||||||
To *common.Address `json:"to"`
|
R *hexutil.Big `json:"r"`
|
||||||
|
S *hexutil.Big `json:"s"`
|
||||||
|
To *common.Address `json:"to"`
|
||||||
|
|
||||||
// Access list transaction fields:
|
// Access list transaction fields:
|
||||||
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
ChainID *hexutil.Big `json:"chainId,omitempty"`
|
||||||
|
|
@ -63,6 +81,19 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
|
||||||
enc.V = (*hexutil.Big)(tx.V)
|
enc.V = (*hexutil.Big)(tx.V)
|
||||||
enc.R = (*hexutil.Big)(tx.R)
|
enc.R = (*hexutil.Big)(tx.R)
|
||||||
enc.S = (*hexutil.Big)(tx.S)
|
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)
|
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:
|
default:
|
||||||
return ErrTxTypeNotSupported
|
return ErrTxTypeNotSupported
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||||
var signer Signer
|
var signer Signer
|
||||||
switch {
|
switch {
|
||||||
case config.IsEIP1559(blockNumber):
|
case config.IsEIP1559(blockNumber):
|
||||||
signer = NewEIP2930Signer(config.ChainId)
|
signer = NewLondonSigner(config.ChainId)
|
||||||
case config.IsEIP155(blockNumber):
|
case config.IsEIP155(blockNumber):
|
||||||
signer = NewEIP155Signer(config.ChainId)
|
signer = NewEIP155Signer(config.ChainId)
|
||||||
case config.IsHomestead(blockNumber):
|
case config.IsHomestead(blockNumber):
|
||||||
|
|
@ -63,7 +63,7 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||||
func LatestSigner(config *params.ChainConfig) Signer {
|
func LatestSigner(config *params.ChainConfig) Signer {
|
||||||
if config.ChainId != nil {
|
if config.ChainId != nil {
|
||||||
if common.Eip1559Block.Uint64() != 9999999999 || config.Eip1559Block != nil {
|
if common.Eip1559Block.Uint64() != 9999999999 || config.Eip1559Block != nil {
|
||||||
return NewEIP2930Signer(config.ChainId)
|
return NewLondonSigner(config.ChainId)
|
||||||
}
|
}
|
||||||
if config.EIP155Block != nil {
|
if config.EIP155Block != nil {
|
||||||
return NewEIP155Signer(config.ChainId)
|
return NewEIP155Signer(config.ChainId)
|
||||||
|
|
@ -83,7 +83,7 @@ func LatestSignerForChainID(chainID *big.Int) Signer {
|
||||||
if chainID == nil {
|
if chainID == nil {
|
||||||
return HomesteadSigner{}
|
return HomesteadSigner{}
|
||||||
}
|
}
|
||||||
return NewEIP2930Signer(chainID)
|
return NewLondonSigner(chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignTx signs the transaction using the given signer and private key.
|
// SignTx signs the transaction using the given signer and private key.
|
||||||
|
|
@ -170,6 +170,72 @@ type Signer interface {
|
||||||
Equal(Signer) bool
|
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 }
|
type eip2930Signer struct{ EIP155Signer }
|
||||||
|
|
||||||
// NewEIP2930Signer returns a signer that accepts EIP-2930 access list transactions,
|
// 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:
|
case LegacyTxType:
|
||||||
return s.EIP155Signer.Sender(tx)
|
return s.EIP155Signer.Sender(tx)
|
||||||
case AccessListTxType:
|
case AccessListTxType:
|
||||||
// ACL txs are defined to use 0 and 1 as their recovery id, add
|
// AL txs are defined to use 0 and 1 as their recovery
|
||||||
// 27 to become equivalent to unprotected Homestead signatures.
|
// id, add 27 to become equivalent to unprotected Homestead signatures.
|
||||||
V = new(big.Int).Add(V, big.NewInt(27))
|
V = new(big.Int).Add(V, big.NewInt(27))
|
||||||
default:
|
default:
|
||||||
return common.Address{}, ErrTxTypeNotSupported
|
return common.Address{}, ErrTxTypeNotSupported
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ func TestDecodeEmptyTypedTx(t *testing.T) {
|
||||||
input := []byte{0x80}
|
input := []byte{0x80}
|
||||||
var tx Transaction
|
var tx Transaction
|
||||||
err := rlp.DecodeBytes(input, &tx)
|
err := rlp.DecodeBytes(input, &tx)
|
||||||
if err != errEmptyTypedTx {
|
if err != errShortTypedTx {
|
||||||
t.Fatal("wrong error:", err)
|
t.Fatal("wrong error:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ type AccessListTx struct {
|
||||||
func (tx *AccessListTx) copy() TxData {
|
func (tx *AccessListTx) copy() TxData {
|
||||||
cpy := &AccessListTx{
|
cpy := &AccessListTx{
|
||||||
Nonce: tx.Nonce,
|
Nonce: tx.Nonce,
|
||||||
To: tx.To, // TODO: copy pointed-to address
|
To: copyAddressPtr(tx.To),
|
||||||
Data: common.CopyBytes(tx.Data),
|
Data: common.CopyBytes(tx.Data),
|
||||||
Gas: tx.Gas,
|
Gas: tx.Gas,
|
||||||
// These are copied below.
|
// These are copied below.
|
||||||
|
|
@ -94,18 +94,22 @@ func (tx *AccessListTx) copy() TxData {
|
||||||
}
|
}
|
||||||
|
|
||||||
// accessors for innerTx.
|
// accessors for innerTx.
|
||||||
|
|
||||||
func (tx *AccessListTx) txType() byte { return AccessListTxType }
|
func (tx *AccessListTx) txType() byte { return AccessListTxType }
|
||||||
func (tx *AccessListTx) chainID() *big.Int { return tx.ChainID }
|
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) accessList() AccessList { return tx.AccessList }
|
||||||
func (tx *AccessListTx) data() []byte { return tx.Data }
|
func (tx *AccessListTx) data() []byte { return tx.Data }
|
||||||
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
|
func (tx *AccessListTx) gas() uint64 { return tx.Gas }
|
||||||
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
|
func (tx *AccessListTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||||
|
func (tx *AccessListTx) 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) value() *big.Int { return tx.Value }
|
||||||
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
|
func (tx *AccessListTx) nonce() uint64 { return tx.Nonce }
|
||||||
func (tx *AccessListTx) to() *common.Address { return tx.To }
|
func (tx *AccessListTx) to() *common.Address { return tx.To }
|
||||||
|
|
||||||
|
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) {
|
func (tx *AccessListTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||||
return tx.V, tx.R, tx.S
|
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 {
|
func (tx *LegacyTx) copy() TxData {
|
||||||
cpy := &LegacyTx{
|
cpy := &LegacyTx{
|
||||||
Nonce: tx.Nonce,
|
Nonce: tx.Nonce,
|
||||||
To: tx.To, // TODO: copy pointed-to address
|
To: copyAddressPtr(tx.To),
|
||||||
Data: common.CopyBytes(tx.Data),
|
Data: common.CopyBytes(tx.Data),
|
||||||
Gas: tx.Gas,
|
Gas: tx.Gas,
|
||||||
// These are initialized below.
|
// 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) data() []byte { return tx.Data }
|
||||||
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
|
func (tx *LegacyTx) gas() uint64 { return tx.Gas }
|
||||||
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
|
func (tx *LegacyTx) gasPrice() *big.Int { return tx.GasPrice }
|
||||||
|
func (tx *LegacyTx) 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) value() *big.Int { return tx.Value }
|
||||||
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
|
func (tx *LegacyTx) nonce() uint64 { return tx.Nonce }
|
||||||
func (tx *LegacyTx) to() *common.Address { return tx.To }
|
func (tx *LegacyTx) to() *common.Address { return tx.To }
|
||||||
|
|
||||||
|
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) {
|
func (tx *LegacyTx) rawSignatureValues() (v, r, s *big.Int) {
|
||||||
return tx.V, tx.R, tx.S
|
return tx.V, tx.R, tx.S
|
||||||
}
|
}
|
||||||
|
|
@ -61,16 +61,14 @@ func (al accessList) equal(other accessList) bool {
|
||||||
if len(al) != len(other) {
|
if len(al) != len(other) {
|
||||||
return false
|
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 {
|
for addr := range al {
|
||||||
if _, ok := other[addr]; !ok {
|
if _, ok := other[addr]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for addr := range other {
|
|
||||||
if _, ok := al[addr]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Accounts match, cross reference the storage slots too
|
// Accounts match, cross reference the storage slots too
|
||||||
for addr, slots := range al {
|
for addr, slots := range al {
|
||||||
otherslots := other[addr]
|
otherslots := other[addr]
|
||||||
|
|
@ -78,16 +76,13 @@ func (al accessList) equal(other accessList) bool {
|
||||||
if len(slots) != len(otherslots) {
|
if len(slots) != len(otherslots) {
|
||||||
return false
|
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 {
|
for hash := range slots {
|
||||||
if _, ok := otherslots[hash]; !ok {
|
if _, ok := otherslots[hash]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for hash := range otherslots {
|
|
||||||
if _, ok := slots[hash]; !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ import (
|
||||||
|
|
||||||
var activators = map[int]func(*JumpTable){
|
var activators = map[int]func(*JumpTable){
|
||||||
3855: enable3855,
|
3855: enable3855,
|
||||||
|
3860: enable3860,
|
||||||
|
3529: enable3529,
|
||||||
3198: enable3198,
|
3198: enable3198,
|
||||||
2929: enable2929,
|
2929: enable2929,
|
||||||
2200: enable2200,
|
2200: enable2200,
|
||||||
|
|
@ -104,28 +106,28 @@ func enable2929(jt *JumpTable) {
|
||||||
jt[SLOAD].constantGas = 0
|
jt[SLOAD].constantGas = 0
|
||||||
jt[SLOAD].dynamicGas = gasSLoadEIP2929
|
jt[SLOAD].dynamicGas = gasSLoadEIP2929
|
||||||
|
|
||||||
jt[EXTCODECOPY].constantGas = WarmStorageReadCostEIP2929
|
jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
|
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
|
||||||
|
|
||||||
jt[EXTCODESIZE].constantGas = WarmStorageReadCostEIP2929
|
jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
|
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
jt[EXTCODEHASH].constantGas = WarmStorageReadCostEIP2929
|
jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
|
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
jt[BALANCE].constantGas = WarmStorageReadCostEIP2929
|
jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
|
||||||
|
|
||||||
jt[CALL].constantGas = WarmStorageReadCostEIP2929
|
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[CALL].dynamicGas = gasCallEIP2929
|
jt[CALL].dynamicGas = gasCallEIP2929
|
||||||
|
|
||||||
jt[CALLCODE].constantGas = WarmStorageReadCostEIP2929
|
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
|
||||||
|
|
||||||
jt[STATICCALL].constantGas = WarmStorageReadCostEIP2929
|
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
|
||||||
|
|
||||||
jt[DELEGATECALL].constantGas = WarmStorageReadCostEIP2929
|
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
|
||||||
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
|
||||||
|
|
||||||
// This was previously part of the dynamic cost, but we're using it as a constantGas
|
// 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
|
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)
|
// enable3198 applies EIP-3198 (BASEFEE Opcode)
|
||||||
// - Adds an opcode that returns the current block's base fee.
|
// - Adds an opcode that returns the current block's base fee.
|
||||||
func enable3198(jt *JumpTable) {
|
func enable3198(jt *JumpTable) {
|
||||||
|
|
@ -148,7 +159,7 @@ func enable3198(jt *JumpTable) {
|
||||||
|
|
||||||
// opBaseFee implements BASEFEE opcode
|
// opBaseFee implements BASEFEE opcode
|
||||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||||
baseFee, _ := uint256.FromBig(common.MinGasPrice50x)
|
baseFee, _ := uint256.FromBig(common.BaseFee)
|
||||||
callContext.Stack.push(baseFee)
|
callContext.Stack.push(baseFee)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -169,3 +180,10 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext)
|
||||||
callContext.Stack.push(new(uint256.Int))
|
callContext.Stack.push(new(uint256.Int))
|
||||||
return nil, nil
|
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")
|
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
||||||
ErrContractAddressCollision = errors.New("contract address collision")
|
ErrContractAddressCollision = errors.New("contract address collision")
|
||||||
ErrExecutionReverted = errors.New("execution reverted")
|
ErrExecutionReverted = errors.New("execution reverted")
|
||||||
|
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||||
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
|
||||||
ErrInvalidJump = errors.New("invalid jump destination")
|
ErrInvalidJump = errors.New("invalid jump destination")
|
||||||
ErrWriteProtection = errors.New("write protection")
|
ErrWriteProtection = errors.New("write protection")
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ type BlockContext struct {
|
||||||
BlockNumber *big.Int // Provides information for NUMBER
|
BlockNumber *big.Int // Provides information for NUMBER
|
||||||
Time *big.Int // Provides information for TIME
|
Time *big.Int // Provides information for TIME
|
||||||
Difficulty *big.Int // Provides information for DIFFICULTY
|
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||||
|
BaseFee *big.Int // Provides information for BASEFEE
|
||||||
Random *common.Hash // Provides information for PREVRANDAO
|
Random *common.Hash // Provides information for PREVRANDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,7 +140,7 @@ type EVM struct {
|
||||||
chainRules params.Rules
|
chainRules params.Rules
|
||||||
// virtual machine configuration options used to initialise the
|
// virtual machine configuration options used to initialise the
|
||||||
// evm.
|
// evm.
|
||||||
vmConfig Config
|
Config Config
|
||||||
// global (to this context) ethereum virtual machine
|
// global (to this context) ethereum virtual machine
|
||||||
// used throughout the execution of the tx.
|
// used throughout the execution of the tx.
|
||||||
interpreters []Interpreter
|
interpreters []Interpreter
|
||||||
|
|
@ -155,13 +156,13 @@ type EVM struct {
|
||||||
|
|
||||||
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
|
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
|
||||||
// only ever be used *once*.
|
// 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{
|
evm := &EVM{
|
||||||
Context: blockCtx,
|
Context: blockCtx,
|
||||||
TxContext: txCtx,
|
TxContext: txCtx,
|
||||||
StateDB: statedb,
|
StateDB: statedb,
|
||||||
tradingStateDB: tradingStateDB,
|
tradingStateDB: tradingStateDB,
|
||||||
vmConfig: vmConfig,
|
Config: config,
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber),
|
chainRules: chainConfig.Rules(blockCtx.BlockNumber),
|
||||||
interpreters: make([]Interpreter, 0, 1),
|
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
|
// 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.
|
// 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]
|
evm.interpreter = evm.interpreters[0]
|
||||||
|
|
||||||
return evm
|
return evm
|
||||||
|
|
@ -217,13 +218,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
||||||
// Calling a non existing account, don't do anything, but ping the tracer
|
// 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 {
|
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)
|
||||||
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
|
evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
|
||||||
} else {
|
} else {
|
||||||
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
||||||
evm.vmConfig.Tracer.CaptureExit(ret, 0, nil)
|
evm.Config.Tracer.CaptureExit(ret, 0, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, gas, 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)
|
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
|
||||||
|
|
||||||
// Capture the tracer start/end events in debug mode
|
// Capture the tracer start/end events in debug mode
|
||||||
if evm.vmConfig.Debug {
|
if evm.Config.Debug {
|
||||||
if evm.depth == 0 {
|
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
|
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())
|
}(gas, time.Now())
|
||||||
} else {
|
} else {
|
||||||
// Handle tracer events for entering and exiting a call frame
|
// 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) {
|
defer func(startGas uint64) {
|
||||||
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
|
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||||
}(gas)
|
}(gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,10 +305,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
var snapshot = evm.StateDB.Snapshot()
|
var snapshot = evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
if evm.vmConfig.Debug {
|
if evm.Config.Debug {
|
||||||
evm.vmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
|
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
|
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||||
}(gas)
|
}(gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,10 +346,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
var snapshot = evm.StateDB.Snapshot()
|
var snapshot = evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
if evm.vmConfig.Debug {
|
if evm.Config.Debug {
|
||||||
evm.vmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
|
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
|
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||||
}(gas)
|
}(gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -395,10 +396,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
evm.StateDB.AddBalance(addr, big0)
|
evm.StateDB.AddBalance(addr, big0)
|
||||||
|
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
if evm.vmConfig.Debug {
|
if evm.Config.Debug {
|
||||||
evm.vmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
|
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
|
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
||||||
}(gas)
|
}(gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -479,11 +480,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
contract := NewContract(caller, AccountRef(address), value, gas)
|
contract := NewContract(caller, AccountRef(address), value, gas)
|
||||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||||
|
|
||||||
if evm.vmConfig.Debug {
|
if evm.Config.Debug {
|
||||||
if evm.depth == 0 {
|
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 {
|
} 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()
|
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 {
|
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 {
|
} 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
|
return ret, address, contract.Gas, err
|
||||||
|
|
|
||||||
|
|
@ -300,6 +300,40 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
|
||||||
return gas, nil
|
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) {
|
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"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[:])
|
interpreter.hasher.Read(interpreter.hasherBuf[:])
|
||||||
|
|
||||||
evm := interpreter.evm
|
evm := interpreter.evm
|
||||||
if evm.vmConfig.EnablePreimageRecording {
|
if evm.Config.EnablePreimageRecording {
|
||||||
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
|
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()))
|
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
gas = scope.Contract.Gas
|
gas = scope.Contract.Gas
|
||||||
)
|
)
|
||||||
|
|
||||||
// Apply EIP150
|
// Apply EIP150
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
scope.Contract.UseGas(gas)
|
scope.Contract.UseGas(gas)
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ func TestAddMod(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
)
|
)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
@ -290,7 +290,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
|
||||||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
scope = &ScopeContext{nil, stack, nil}
|
scope = &ScopeContext{nil, stack, nil}
|
||||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
env.interpreter = evmInterpreter
|
env.interpreter = evmInterpreter
|
||||||
|
|
@ -531,7 +531,7 @@ func TestOpMstore(t *testing.T) {
|
||||||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
env.interpreter = evmInterpreter
|
env.interpreter = evmInterpreter
|
||||||
|
|
@ -557,7 +557,7 @@ func BenchmarkOpMstore(bench *testing.B) {
|
||||||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||||
)
|
)
|
||||||
|
|
||||||
env.interpreter = evmInterpreter
|
env.interpreter = evmInterpreter
|
||||||
|
|
@ -579,7 +579,7 @@ func BenchmarkOpKeccak256(bench *testing.B) {
|
||||||
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
env = NewEVM(BlockContext{}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
mem = NewMemory()
|
mem = NewMemory()
|
||||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||||
)
|
)
|
||||||
env.interpreter = evmInterpreter
|
env.interpreter = evmInterpreter
|
||||||
mem.Resize(32)
|
mem.Resize(32)
|
||||||
|
|
@ -683,7 +683,7 @@ func TestRandom(t *testing.T) {
|
||||||
env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
env = NewEVM(BlockContext{Random: &tt.random}, TxContext{}, nil, nil, params.TestChainConfig, Config{})
|
||||||
stack = newstack()
|
stack = newstack()
|
||||||
pc = uint64(0)
|
pc = uint64(0)
|
||||||
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
|
evmInterpreter = NewEVMInterpreter(env, env.Config)
|
||||||
)
|
)
|
||||||
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
opRandom(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
|
||||||
if len(stack.data) != 1 {
|
if len(stack.data) != 1 {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Debug bool // Enables debugging
|
Debug bool // Enables debugging
|
||||||
Tracer EVMLogger // Opcode logger
|
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
|
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
||||||
|
|
||||||
JumpTable *JumpTable // EVM instruction table, automatically populated if unset
|
JumpTable *JumpTable // EVM instruction table, automatically populated if unset
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ func validate(jt JumpTable) JumpTable {
|
||||||
func newEip1559InstructionSet() JumpTable {
|
func newEip1559InstructionSet() JumpTable {
|
||||||
instructionSet := newShanghaiInstructionSet()
|
instructionSet := newShanghaiInstructionSet()
|
||||||
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
|
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)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +109,6 @@ func newMergeInstructionSet() JumpTable {
|
||||||
// constantinople, istanbul, petersburg, berlin and london instructions.
|
// constantinople, istanbul, petersburg, berlin and london instructions.
|
||||||
func newLondonInstructionSet() JumpTable {
|
func newLondonInstructionSet() JumpTable {
|
||||||
instructionSet := newBerlinInstructionSet()
|
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
|
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
|
||||||
return validate(instructionSet)
|
return validate(instructionSet)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,91 +24,75 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
|
||||||
ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST
|
// If we fail the minimum gas availability invariant, fail (0)
|
||||||
WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST
|
if contract.Gas <= params.SstoreSentryGasEIP2200 {
|
||||||
)
|
return 0, errors.New("not enough gas for reentrancy sentry")
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
}
|
// Gas sentry honoured, do the actual gas calculation based on the stored value
|
||||||
value := common.Hash(y.Bytes32())
|
var (
|
||||||
|
y, x = stack.Back(1), stack.peek()
|
||||||
if current == value { // noop (1)
|
slot = common.Hash(x.Bytes32())
|
||||||
// EIP 2200 original clause:
|
current = evm.StateDB.GetState(contract.Address(), slot)
|
||||||
// return params.SloadGasEIP2200, nil
|
cost = uint64(0)
|
||||||
return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS
|
)
|
||||||
}
|
// Check slot presence in the access list
|
||||||
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
|
if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
|
||||||
if original == current {
|
cost = params.ColdSloadCostEIP2929
|
||||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
return cost + params.SstoreSetGasEIP2200, nil
|
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)
|
value := common.Hash(y.Bytes32())
|
||||||
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
|
|
||||||
|
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:
|
// EIP-2200 original clause:
|
||||||
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
|
//return params.SloadGasEIP2200, nil // dirty update (2.2)
|
||||||
return cost + (params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929), nil // write existing slot (2.1.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
|
// 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 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
|
// If he does afford it, we can skip checking the same thing later on, during execution
|
||||||
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
|
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
|
// 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)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
var overflow bool
|
var overflow bool
|
||||||
// We charge (cold-warm), since 'warm' is already charged as constantGas
|
// 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 0, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
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
|
// If the caller cannot afford the cost, this change will be rolled back
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// The warm storage read cost is already charged as constantGas
|
// The warm storage read cost is already charged as constantGas
|
||||||
return ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929, nil
|
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
|
||||||
}
|
}
|
||||||
return 0, 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) {
|
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
addr := common.Address(stack.Back(1).Bytes20())
|
addr := common.Address(stack.Back(1).Bytes20())
|
||||||
// Check slot presence in the access list
|
// 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)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
if !contract.UseGas(ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929) {
|
// gas for call
|
||||||
|
if !contract.UseGas(coldCost) {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +178,16 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
||||||
// - transfer value
|
// - transfer value
|
||||||
// - memory expansion
|
// - memory expansion
|
||||||
// - 63/64ths rule
|
// - 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)
|
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
|
||||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
||||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
|
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) {
|
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539
|
||||||
var (
|
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
||||||
gas uint64
|
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||||
address = common.Address(stack.peek().Bytes20())
|
var (
|
||||||
)
|
gas uint64
|
||||||
if !evm.StateDB.AddressInAccessList(address) {
|
address = common.Address(stack.peek().Bytes20())
|
||||||
// If the caller cannot afford the cost, this change will be rolled back
|
)
|
||||||
evm.StateDB.AddAddressToAccessList(address)
|
if !evm.StateDB.AddressInAccessList(address) {
|
||||||
gas = ColdAccountAccessCostEIP2929
|
// 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
|
return gasFunc
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ func NewEnv(cfg *Config) *vm.EVM {
|
||||||
Time: cfg.Time,
|
Time: cfg.Time,
|
||||||
Difficulty: cfg.Difficulty,
|
Difficulty: cfg.Difficulty,
|
||||||
GasLimit: cfg.GasLimit,
|
GasLimit: cfg.GasLimit,
|
||||||
|
BaseFee: cfg.BaseFee,
|
||||||
}
|
}
|
||||||
|
|
||||||
return vm.NewEVM(blockContext, txContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig)
|
return vm.NewEVM(blockContext, txContext, cfg.State, nil, cfg.ChainConfig, cfg.EVMConfig)
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ type Config struct {
|
||||||
Value *big.Int
|
Value *big.Int
|
||||||
Debug bool
|
Debug bool
|
||||||
EVMConfig vm.Config
|
EVMConfig vm.Config
|
||||||
|
BaseFee *big.Int
|
||||||
|
|
||||||
State *state.StateDB
|
State *state.StateDB
|
||||||
GetHashFn func(n uint64) common.Hash
|
GetHashFn func(n uint64) common.Hash
|
||||||
|
|
@ -68,6 +69,7 @@ func setDefaults(cfg *Config) {
|
||||||
LondonBlock: new(big.Int),
|
LondonBlock: new(big.Int),
|
||||||
MergeBlock: new(big.Int),
|
MergeBlock: new(big.Int),
|
||||||
ShanghaiBlock: 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())))
|
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.
|
// 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)
|
"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) {
|
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) {
|
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) {
|
func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) {
|
||||||
pending, err := b.eth.txPool.Pending()
|
pending := b.eth.txPool.Pending(false)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var txs types.Transactions
|
var txs types.Transactions
|
||||||
for _, batch := range pending {
|
for _, batch := range pending {
|
||||||
txs = append(txs, batch...)
|
txs = append(txs, batch...)
|
||||||
|
|
@ -325,6 +322,10 @@ func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions,
|
||||||
return b.eth.TxPool().Content()
|
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) {
|
func (b *EthApiBackend) OrderTxPoolContent() (map[common.Address]types.OrderTransactions, map[common.Address]types.OrderTransactions) {
|
||||||
return b.eth.OrderPool().Content()
|
return b.eth.OrderPool().Content()
|
||||||
}
|
}
|
||||||
|
|
@ -344,8 +345,12 @@ func (b *EthApiBackend) ProtocolVersion() int {
|
||||||
return b.eth.EthVersion()
|
return b.eth.EthVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
|
func (b *EthApiBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
return b.gpo.SuggestPrice(ctx)
|
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 {
|
func (b *EthApiBackend) ChainDb() ethdb.Database {
|
||||||
|
|
@ -393,6 +398,10 @@ func (b *EthApiBackend) GetEngine() consensus.Engine {
|
||||||
return b.eth.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) {
|
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)
|
return b.eth.stateAtBlock(block, reexec, base, checkLive)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"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)
|
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
|
||||||
// Trace all the transactions contained within
|
// Trace all the transactions contained within
|
||||||
for i, tx := range task.block.Transactions() {
|
for i, tx := range task.block.Transactions() {
|
||||||
var balacne *big.Int
|
var balance *big.Int
|
||||||
if tx.To() != nil {
|
if tx.To() != nil {
|
||||||
if value, ok := feeCapacity[*tx.To()]; ok {
|
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{
|
txctx := &tracers.Context{
|
||||||
BlockHash: task.block.Hash(),
|
BlockHash: task.block.Hash(),
|
||||||
TxIndex: i,
|
TxIndex: i,
|
||||||
|
|
@ -480,13 +480,14 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
||||||
// Fetch and execute the next transaction trace tasks
|
// Fetch and execute the next transaction trace tasks
|
||||||
for task := range jobs {
|
for task := range jobs {
|
||||||
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
|
feeCapacity := state.GetTRC21FeeCapacityFromState(task.statedb)
|
||||||
var balacne *big.Int
|
var balance *big.Int
|
||||||
if txs[task.index].To() != nil {
|
if txs[task.index].To() != nil {
|
||||||
if value, ok := feeCapacity[*txs[task.index].To()]; ok {
|
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{
|
txctx := &tracers.Context{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
TxIndex: task.index,
|
TxIndex: task.index,
|
||||||
|
|
@ -507,18 +508,19 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
// Send the trace task over for execution
|
// Send the trace task over for execution
|
||||||
jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
|
jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
|
||||||
var balacne *big.Int
|
var balance *big.Int
|
||||||
if tx.To() != nil {
|
if tx.To() != nil {
|
||||||
// Bypass the validation for trading and lending transactions as their nonce are not incremented
|
// Bypass the validation for trading and lending transactions as their nonce are not incremented
|
||||||
if tx.IsSkipNonceTransaction() {
|
if tx.IsSkipNonceTransaction() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if value, ok := feeCapacity[*tx.To()]; ok {
|
if value, ok := feeCapacity[*tx.To()]; ok {
|
||||||
balacne = value
|
balance = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate the next state snapshot fast without tracing
|
// 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)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
statedb.Prepare(tx.Hash(), i)
|
statedb.Prepare(tx.Hash(), i)
|
||||||
|
|
||||||
|
|
@ -689,7 +691,12 @@ func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.Transacti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Execute the trace
|
// 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)
|
vmctx := core.NewEVMBlockContext(block.Header(), api.eth.blockchain, nil)
|
||||||
var traceConfig *TraceConfig
|
var traceConfig *TraceConfig
|
||||||
if config != nil {
|
if config != nil {
|
||||||
|
|
@ -737,7 +744,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t
|
||||||
tracer = vm.NewStructLogger(config.LogConfig)
|
tracer = vm.NewStructLogger(config.LogConfig)
|
||||||
}
|
}
|
||||||
// Run the transaction with tracing enabled.
|
// 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
|
// Call Prepare to clear out the statedb access list
|
||||||
statedb.Prepare(txctx.TxHash, txctx.TxIndex)
|
statedb.Prepare(txctx.TxHash, txctx.TxIndex)
|
||||||
|
|
@ -800,7 +807,8 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
|
||||||
balanceFee = value
|
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 {
|
if err != nil {
|
||||||
return nil, vm.BlockContext{}, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
|
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/contracts"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||||
|
|
@ -72,9 +73,9 @@ type Ethereum struct {
|
||||||
shutdownChan chan bool // Channel for shutting down the ethereum
|
shutdownChan chan bool // Channel for shutting down the ethereum
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
txPool *core.TxPool
|
txPool *txpool.TxPool
|
||||||
orderPool *core.OrderPool
|
orderPool *txpool.OrderPool
|
||||||
lendingPool *core.LendingPool
|
lendingPool *txpool.LendingPool
|
||||||
blockchain *core.BlockChain
|
blockchain *core.BlockChain
|
||||||
protocolManager *ProtocolManager
|
protocolManager *ProtocolManager
|
||||||
lesServer LesServer
|
lesServer LesServer
|
||||||
|
|
@ -186,9 +187,9 @@ func New(ctx *node.ServiceContext, config *ethconfig.Config, XDCXServ *XDCx.XDCX
|
||||||
if config.TxPool.Journal != "" {
|
if config.TxPool.Journal != "" {
|
||||||
config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
|
config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
|
||||||
}
|
}
|
||||||
eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
|
eth.txPool = txpool.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
|
||||||
eth.orderPool = core.NewOrderPool(eth.chainConfig, eth.blockchain)
|
eth.orderPool = txpool.NewOrderPool(eth.chainConfig, eth.blockchain)
|
||||||
eth.lendingPool = core.NewLendingPool(eth.chainConfig, eth.blockchain)
|
eth.lendingPool = txpool.NewLendingPool(eth.chainConfig, eth.blockchain)
|
||||||
if common.RollbackHash != (common.Hash{}) {
|
if common.RollbackHash != (common.Hash{}) {
|
||||||
curBlock := eth.blockchain.CurrentBlock()
|
curBlock := eth.blockchain.CurrentBlock()
|
||||||
if curBlock == nil {
|
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) AccountManager() *accounts.Manager { return e.accountManager }
|
||||||
func (e *Ethereum) BlockChain() *core.BlockChain { return e.blockchain }
|
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) EventMux() *event.TypeMux { return e.eventMux }
|
||||||
func (e *Ethereum) Engine() consensus.Engine { return e.engine }
|
func (e *Ethereum) Engine() consensus.Engine { return e.engine }
|
||||||
func (e *Ethereum) ChainDb() ethdb.Database { return e.chainDb }
|
func (e *Ethereum) ChainDb() ethdb.Database { return e.chainDb }
|
||||||
|
|
@ -591,7 +592,7 @@ func (e *Ethereum) GetXDCX() *XDCx.XDCX {
|
||||||
return e.XDCX
|
return e.XDCX
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Ethereum) OrderPool() *core.OrderPool {
|
func (e *Ethereum) OrderPool() *txpool.OrderPool {
|
||||||
return e.orderPool
|
return e.orderPool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -600,6 +601,6 @@ func (e *Ethereum) GetXDCXLending() *XDCxlending.Lending {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LendingPool geth eth lending pool
|
// LendingPool geth eth lending pool
|
||||||
func (e *Ethereum) LendingPool() *core.LendingPool {
|
func (e *Ethereum) LendingPool() *txpool.LendingPool {
|
||||||
return e.lendingPool
|
return e.lendingPool
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package downloader
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -78,7 +79,7 @@ type downloadTester struct {
|
||||||
// newTester creates a new downloader test mocker.
|
// newTester creates a new downloader test mocker.
|
||||||
func newTester() *downloadTester {
|
func newTester() *downloadTester {
|
||||||
testdb := rawdb.NewMemoryDatabase()
|
testdb := rawdb.NewMemoryDatabase()
|
||||||
genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000))
|
genesis := core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(math.MaxInt64))
|
||||||
|
|
||||||
tester := &downloadTester{
|
tester := &downloadTester{
|
||||||
genesis: genesis,
|
genesis: genesis,
|
||||||
|
|
@ -109,7 +110,8 @@ func newTester() *downloadTester {
|
||||||
// reassembly.
|
// 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) {
|
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
|
// 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})
|
block.SetCoinbase(common.Address{seed})
|
||||||
|
|
||||||
// If a heavy chain is requested, delay blocks to raise difficulty
|
// 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 the block number is multiple of 3, send a bonus transaction to the miner
|
||||||
if parent == dl.genesis && i%3 == 0 {
|
if parent == dl.genesis && i%3 == 0 {
|
||||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
signer := types.MakeSigner(config, block.Number())
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -464,7 +466,11 @@ func (dl *downloadTester) handleProposedBlock(header *types.Header) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config retrieves the blockchain's chain configuration.
|
// 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 {
|
type downloadTesterPeer struct {
|
||||||
dl *downloadTester
|
dl *downloadTester
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
|
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
|
@ -36,18 +37,22 @@ import (
|
||||||
|
|
||||||
// FullNodeGPO contains default gasprice oracle settings for full node.
|
// FullNodeGPO contains default gasprice oracle settings for full node.
|
||||||
var FullNodeGPO = gasprice.Config{
|
var FullNodeGPO = gasprice.Config{
|
||||||
Blocks: 20,
|
Blocks: 20,
|
||||||
Percentile: 60,
|
Percentile: 60,
|
||||||
MaxPrice: gasprice.DefaultMaxPrice,
|
MaxHeaderHistory: 1024,
|
||||||
IgnorePrice: gasprice.DefaultIgnorePrice,
|
MaxBlockHistory: 1024,
|
||||||
|
MaxPrice: gasprice.DefaultMaxPrice,
|
||||||
|
IgnorePrice: gasprice.DefaultIgnorePrice,
|
||||||
}
|
}
|
||||||
|
|
||||||
// LightClientGPO contains default gasprice oracle settings for light client.
|
// LightClientGPO contains default gasprice oracle settings for light client.
|
||||||
var LightClientGPO = gasprice.Config{
|
var LightClientGPO = gasprice.Config{
|
||||||
Blocks: 2,
|
Blocks: 2,
|
||||||
Percentile: 60,
|
Percentile: 60,
|
||||||
MaxPrice: gasprice.DefaultMaxPrice,
|
MaxHeaderHistory: 300,
|
||||||
IgnorePrice: gasprice.DefaultIgnorePrice,
|
MaxBlockHistory: 5,
|
||||||
|
MaxPrice: gasprice.DefaultMaxPrice,
|
||||||
|
IgnorePrice: gasprice.DefaultIgnorePrice,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defaults contains default settings for use on the Ethereum main net.
|
// Defaults contains default settings for use on the Ethereum main net.
|
||||||
|
|
@ -68,7 +73,7 @@ var Defaults = Config{
|
||||||
FilterLogCacheSize: 32,
|
FilterLogCacheSize: 32,
|
||||||
GasPrice: big.NewInt(0.25 * params.Shannon),
|
GasPrice: big.NewInt(0.25 * params.Shannon),
|
||||||
|
|
||||||
TxPool: core.DefaultTxPoolConfig,
|
TxPool: txpool.DefaultConfig,
|
||||||
RPCGasCap: 50000000,
|
RPCGasCap: 50000000,
|
||||||
GPO: FullNodeGPO,
|
GPO: FullNodeGPO,
|
||||||
RPCTxFeeCap: 1, // 1 ether
|
RPCTxFeeCap: 1, // 1 ether
|
||||||
|
|
@ -125,7 +130,7 @@ type Config struct {
|
||||||
Ethash ethash.Config
|
Ethash ethash.Config
|
||||||
|
|
||||||
// Transaction pool options
|
// Transaction pool options
|
||||||
TxPool core.TxPoolConfig
|
TxPool txpool.Config
|
||||||
|
|
||||||
// Gas Price Oracle options
|
// Gas Price Oracle options
|
||||||
GPO gasprice.Config
|
GPO gasprice.Config
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
|
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
|
||||||
)
|
)
|
||||||
|
|
@ -33,7 +34,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
GasPrice *big.Int
|
GasPrice *big.Int
|
||||||
FilterLogCacheSize int
|
FilterLogCacheSize int
|
||||||
Ethash ethash.Config
|
Ethash ethash.Config
|
||||||
TxPool core.TxPoolConfig
|
TxPool txpool.Config
|
||||||
GPO gasprice.Config
|
GPO gasprice.Config
|
||||||
EnablePreimageRecording bool
|
EnablePreimageRecording bool
|
||||||
DocRoot string `toml:"-"`
|
DocRoot string `toml:"-"`
|
||||||
|
|
@ -87,7 +88,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
GasPrice *big.Int
|
GasPrice *big.Int
|
||||||
FilterLogCacheSize *int
|
FilterLogCacheSize *int
|
||||||
Ethash *ethash.Config
|
Ethash *ethash.Config
|
||||||
TxPool *core.TxPoolConfig
|
TxPool *txpool.Config
|
||||||
GPO *gasprice.Config
|
GPO *gasprice.Config
|
||||||
EnablePreimageRecording *bool
|
EnablePreimageRecording *bool
|
||||||
DocRoot *string `toml:"-"`
|
DocRoot *string `toml:"-"`
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ func TestBlockSubscription(t *testing.T) {
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
backend, sys = newTestFilterSystem(t, db, Config{})
|
backend, sys = newTestFilterSystem(t, db, Config{})
|
||||||
api = NewFilterAPI(sys, false)
|
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) {})
|
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {})
|
||||||
chainEvents = []core.ChainEvent{}
|
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"
|
"sync"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/event"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rpc"
|
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
const sampleNumber = 3 // Number of transactions sampled in a block
|
const sampleNumber = 3 // Number of transactions sampled in a block
|
||||||
|
|
||||||
var DefaultMaxPrice = big.NewInt(500 * params.GWei)
|
var (
|
||||||
var DefaultIgnorePrice = big.NewInt(2 * params.Wei)
|
DefaultMaxPrice = big.NewInt(500 * params.GWei)
|
||||||
|
DefaultIgnorePrice = big.NewInt(2 * params.Wei)
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Blocks int
|
Blocks int
|
||||||
Percentile int
|
Percentile int
|
||||||
Default *big.Int `toml:",omitempty"`
|
MaxHeaderHistory uint64
|
||||||
MaxPrice *big.Int `toml:",omitempty"`
|
MaxBlockHistory uint64
|
||||||
IgnorePrice *big.Int `toml:",omitempty"`
|
Default *big.Int `toml:",omitempty"`
|
||||||
|
MaxPrice *big.Int `toml:",omitempty"`
|
||||||
|
IgnorePrice *big.Int `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OracleBackend includes all necessary background APIs for oracle.
|
// OracleBackend includes all necessary background APIs for oracle.
|
||||||
type OracleBackend interface {
|
type OracleBackend interface {
|
||||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||||
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, 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
|
ChainConfig() *params.ChainConfig
|
||||||
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oracle recommends gas prices based on the content of recent
|
// Oracle recommends gas prices based on the content of recent
|
||||||
|
|
@ -60,8 +70,9 @@ type Oracle struct {
|
||||||
cacheLock sync.RWMutex
|
cacheLock sync.RWMutex
|
||||||
fetchLock sync.Mutex
|
fetchLock sync.Mutex
|
||||||
|
|
||||||
checkBlocks int
|
checkBlocks, percentile int
|
||||||
percentile int
|
maxHeaderHistory, maxBlockHistory uint64
|
||||||
|
historyCache *lru.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOracle returns a new gasprice oracle which can recommend suitable
|
// 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 {
|
} else if ignorePrice.Int64() > 0 {
|
||||||
log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
|
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{
|
return &Oracle{
|
||||||
backend: backend,
|
backend: backend,
|
||||||
lastPrice: params.Default,
|
lastPrice: params.Default,
|
||||||
maxPrice: maxPrice,
|
maxPrice: maxPrice,
|
||||||
ignorePrice: ignorePrice,
|
ignorePrice: ignorePrice,
|
||||||
checkBlocks: blocks,
|
checkBlocks: blocks,
|
||||||
percentile: percent,
|
percentile: percent,
|
||||||
|
maxHeaderHistory: params.MaxHeaderHistory,
|
||||||
|
maxBlockHistory: params.MaxBlockHistory,
|
||||||
|
historyCache: cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuggestPrice returns the recommended gas price.
|
// SuggestTipCap returns a tip cap so that newly created transaction can have a
|
||||||
func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
|
// very high chance to be included in the following blocks.
|
||||||
head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
|
//
|
||||||
|
// 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()
|
headHash := head.Hash()
|
||||||
|
|
||||||
// If the latest gasprice is still available, return it.
|
// If the latest gasprice is still available, return it.
|
||||||
gpo.cacheLock.RLock()
|
oracle.cacheLock.RLock()
|
||||||
lastHead, lastPrice := gpo.lastHead, gpo.lastPrice
|
lastHead, lastPrice := oracle.lastHead, oracle.lastPrice
|
||||||
gpo.cacheLock.RUnlock()
|
oracle.cacheLock.RUnlock()
|
||||||
if headHash == lastHead {
|
if headHash == lastHead {
|
||||||
return lastPrice, nil
|
return new(big.Int).Set(lastPrice), nil
|
||||||
}
|
}
|
||||||
gpo.fetchLock.Lock()
|
oracle.fetchLock.Lock()
|
||||||
defer gpo.fetchLock.Unlock()
|
defer oracle.fetchLock.Unlock()
|
||||||
|
|
||||||
// Try checking the cache again, maybe the last fetch fetched what we need
|
// Try checking the cache again, maybe the last fetch fetched what we need
|
||||||
gpo.cacheLock.RLock()
|
oracle.cacheLock.RLock()
|
||||||
lastHead, lastPrice = gpo.lastHead, gpo.lastPrice
|
lastHead, lastPrice = oracle.lastHead, oracle.lastPrice
|
||||||
gpo.cacheLock.RUnlock()
|
oracle.cacheLock.RUnlock()
|
||||||
if headHash == lastHead {
|
if headHash == lastHead {
|
||||||
return lastPrice, nil
|
return new(big.Int).Set(lastPrice), nil
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
sent, exp int
|
sent, exp int
|
||||||
number = head.Number.Uint64()
|
number = head.Number.Uint64()
|
||||||
result = make(chan getBlockPricesResult, gpo.checkBlocks)
|
result = make(chan results, oracle.checkBlocks)
|
||||||
quit = make(chan struct{})
|
quit = make(chan struct{})
|
||||||
txPrices []*big.Int
|
results []*big.Int
|
||||||
)
|
)
|
||||||
for sent < gpo.checkBlocks && number > 0 {
|
for sent < oracle.checkBlocks && number > 0 {
|
||||||
go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit)
|
go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit)
|
||||||
sent++
|
sent++
|
||||||
exp++
|
exp++
|
||||||
number--
|
number--
|
||||||
|
|
@ -142,93 +175,116 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
|
||||||
res := <-result
|
res := <-result
|
||||||
if res.err != nil {
|
if res.err != nil {
|
||||||
close(quit)
|
close(quit)
|
||||||
return lastPrice, res.err
|
return new(big.Int).Set(lastPrice), res.err
|
||||||
}
|
}
|
||||||
exp--
|
exp--
|
||||||
// Nothing returned. There are two special cases here:
|
// Nothing returned. There are two special cases here:
|
||||||
// - The block is empty
|
// - The block is empty
|
||||||
// - All the transactions included are sent by the miner itself.
|
// - All the transactions included are sent by the miner itself.
|
||||||
// In these cases, use the latest calculated price for samping.
|
// In these cases, use the latest calculated price for samping.
|
||||||
if len(res.prices) == 0 {
|
if len(res.values) == 0 {
|
||||||
res.prices = []*big.Int{lastPrice}
|
res.values = []*big.Int{lastPrice}
|
||||||
}
|
}
|
||||||
// Besides, in order to collect enough data for sampling, if nothing
|
// Besides, in order to collect enough data for sampling, if nothing
|
||||||
// meaningful returned, try to query more blocks. But the maximum
|
// meaningful returned, try to query more blocks. But the maximum
|
||||||
// is 2*checkBlocks.
|
// is 2*checkBlocks.
|
||||||
if len(res.prices) == 1 && len(txPrices)+1+exp < gpo.checkBlocks*2 && number > 0 {
|
if len(res.values) == 1 && len(results)+1+exp < oracle.checkBlocks*2 && number > 0 {
|
||||||
go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, gpo.ignorePrice, result, quit)
|
go oracle.getBlockValues(ctx, types.MakeSigner(oracle.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, oracle.ignorePrice, result, quit)
|
||||||
sent++
|
sent++
|
||||||
exp++
|
exp++
|
||||||
number--
|
number--
|
||||||
}
|
}
|
||||||
txPrices = append(txPrices, res.prices...)
|
results = append(results, res.values...)
|
||||||
}
|
}
|
||||||
price := lastPrice
|
price := lastPrice
|
||||||
if len(txPrices) > 0 {
|
if len(results) > 0 {
|
||||||
sort.Sort(bigIntArray(txPrices))
|
sort.Sort(bigIntArray(results))
|
||||||
price = txPrices[(len(txPrices)-1)*gpo.percentile/100]
|
price = results[(len(results)-1)*oracle.percentile/100]
|
||||||
}
|
}
|
||||||
if price.Cmp(gpo.maxPrice) > 0 {
|
if price.Cmp(oracle.maxPrice) > 0 {
|
||||||
price = new(big.Int).Set(gpo.maxPrice)
|
price = new(big.Int).Set(oracle.maxPrice)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check gas price min.
|
// Check min gas price for non-eip1559 block
|
||||||
minGasPrice := common.GetMinGasPrice(head.Number)
|
if head.BaseFee == nil {
|
||||||
if price.Cmp(minGasPrice) < 0 {
|
minGasPrice := common.GetMinGasPrice(head.Number)
|
||||||
price = new(big.Int).Set(minGasPrice)
|
if price.Cmp(minGasPrice) < 0 {
|
||||||
|
price = new(big.Int).Set(minGasPrice)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gpo.cacheLock.Lock()
|
oracle.cacheLock.Lock()
|
||||||
gpo.lastHead = headHash
|
oracle.lastHead = headHash
|
||||||
gpo.lastPrice = price
|
oracle.lastPrice = price
|
||||||
gpo.cacheLock.Unlock()
|
oracle.cacheLock.Unlock()
|
||||||
return price, nil
|
|
||||||
|
return new(big.Int).Set(price), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type getBlockPricesResult struct {
|
type results struct {
|
||||||
prices []*big.Int
|
values []*big.Int
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
type transactionsByGasPrice []*types.Transaction
|
type txSorter struct {
|
||||||
|
txs []*types.Transaction
|
||||||
|
baseFee *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
func (t transactionsByGasPrice) Len() int { return len(t) }
|
func newSorter(txs []*types.Transaction, baseFee *big.Int) *txSorter {
|
||||||
func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
|
return &txSorter{
|
||||||
func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPriceCmp(t[j]) < 0 }
|
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
|
// 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
|
// 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
|
// 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.
|
// 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{}) {
|
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 := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
|
block, err := oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
|
||||||
if block == nil {
|
if block == nil {
|
||||||
select {
|
select {
|
||||||
case result <- getBlockPricesResult{nil, err}:
|
case result <- results{nil, err}:
|
||||||
case <-quit:
|
case <-quit:
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
blockTxs := block.Transactions()
|
// Sort the transaction by effective tip in ascending sort.
|
||||||
txs := make([]*types.Transaction, len(blockTxs))
|
txs := make([]*types.Transaction, len(block.Transactions()))
|
||||||
copy(txs, blockTxs)
|
copy(txs, block.Transactions())
|
||||||
sort.Sort(transactionsByGasPrice(txs))
|
sorter := newSorter(txs, block.BaseFee())
|
||||||
|
sort.Sort(sorter)
|
||||||
|
|
||||||
var prices []*big.Int
|
var prices []*big.Int
|
||||||
for _, tx := range txs {
|
for _, tx := range sorter.txs {
|
||||||
if ignoreUnder != nil && tx.GasPrice().Cmp(ignoreUnder) == -1 {
|
tip, _ := tx.EffectiveGasTip(block.BaseFee())
|
||||||
|
if ignoreUnder != nil && tip.Cmp(ignoreUnder) == -1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sender, err := types.Sender(signer, tx)
|
sender, err := types.Sender(signer, tx)
|
||||||
if err == nil && sender != block.Coinbase() {
|
if err == nil && sender != block.Coinbase() {
|
||||||
prices = append(prices, tx.GasPrice())
|
prices = append(prices, tip)
|
||||||
if len(prices) >= limit {
|
if len(prices) >= limit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case result <- getBlockPricesResult{prices, nil}:
|
case result <- results{prices, nil}:
|
||||||
case <-quit:
|
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
|
// 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()
|
p.lock.RLock()
|
||||||
defer p.lock.RUnlock()
|
defer p.lock.RUnlock()
|
||||||
|
|
||||||
|
|
@ -123,7 +123,7 @@ func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
|
||||||
for _, batch := range batches {
|
for _, batch := range batches {
|
||||||
sort.Sort(types.TxByNonce(batch))
|
sort.Sort(types.TxByNonce(batch))
|
||||||
}
|
}
|
||||||
return batches, nil
|
return batches
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ type txPool interface {
|
||||||
|
|
||||||
// Pending should return pending transactions.
|
// Pending should return pending transactions.
|
||||||
// The slice should be modifiable by the caller.
|
// The slice should be modifiable by the caller.
|
||||||
Pending() (map[common.Address]types.Transactions, error)
|
Pending(enforceTips bool) map[common.Address]types.Transactions
|
||||||
|
|
||||||
// SubscribeNewTxsEvent should return an event subscription of
|
// SubscribeNewTxsEvent should return an event subscription of
|
||||||
// NewTxsEvent and send events to the given channel.
|
// NewTxsEvent and send events to the given channel.
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ type txsync struct {
|
||||||
// syncTransactions starts sending all currently pending transactions to the given peer.
|
// syncTransactions starts sending all currently pending transactions to the given peer.
|
||||||
func (pm *ProtocolManager) syncTransactions(p *peer) {
|
func (pm *ProtocolManager) syncTransactions(p *peer) {
|
||||||
var txs types.Transactions
|
var txs types.Transactions
|
||||||
pending, _ := pm.txpool.Pending()
|
pending := pm.txpool.Pending(false)
|
||||||
for _, batch := range pending {
|
for _, batch := range pending {
|
||||||
txs = append(txs, batch...)
|
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)
|
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})
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
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)
|
b.Fatalf("failed to parse testcase input: %v", err)
|
||||||
}
|
}
|
||||||
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)))
|
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 {
|
if err != nil {
|
||||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
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
|
// Compute intrinsic gas
|
||||||
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
|
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
|
||||||
|
isEIP1559 := env.ChainConfig().IsEIP1559(env.Context.BlockNumber)
|
||||||
// after update core.IntrinsicGas, use isIstanbul in it
|
// after update core.IntrinsicGas, use isIstanbul in it
|
||||||
// isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
|
// 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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
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})
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +336,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||||
//EnableReturnData: false,
|
//EnableReturnData: false,
|
||||||
})
|
})
|
||||||
evm := vm.NewEVM(context, txContext, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
|
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 {
|
if err != nil {
|
||||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
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
|
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 {
|
type rpcProgress struct {
|
||||||
StartingBlock hexutil.Uint64
|
StartingBlock hexutil.Uint64
|
||||||
CurrentBlock hexutil.Uint64
|
CurrentBlock hexutil.Uint64
|
||||||
|
|
@ -436,8 +425,6 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
|
||||||
return uint(num), err
|
return uint(num), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: SubscribePendingTransactions (needs server side)
|
|
||||||
|
|
||||||
// Contract Calling
|
// Contract Calling
|
||||||
|
|
||||||
// CallContract executes a message call transaction, which is directly executed in the VM
|
// 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
|
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
|
// 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 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,
|
// 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))
|
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.
|
// 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
|
// 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 {
|
if msg.GasPrice != nil {
|
||||||
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
|
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
|
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()
|
sync := s.eth.Downloader().Progress()
|
||||||
syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
|
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())
|
gasprice = int(price.Uint64())
|
||||||
|
if basefee := s.eth.ApiBackend.CurrentHeader().BaseFee; basefee != nil {
|
||||||
|
gasprice += int(basefee.Uint64())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sync := s.les.Downloader().Progress()
|
sync := s.les.Downloader().Progress()
|
||||||
syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
|
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/google/uuid v1.3.0 // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // 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/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect
|
||||||
github.com/maruel/ut v1.0.2 // indirect
|
github.com/maruel/ut v1.0.2 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // 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)
|
To *common.Address // the destination contract (nil for contract creation)
|
||||||
Gas uint64 // if 0, the call executes with near-infinite gas
|
Gas uint64 // if 0, the call executes with near-infinite gas
|
||||||
GasPrice *big.Int // wei <-> gas exchange ratio
|
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
|
Value *big.Int // amount of wei sent along with the call
|
||||||
Data []byte // input data, usually an ABI-encoded contract method invocation
|
Data []byte // input data, usually an ABI-encoded contract method invocation
|
||||||
BalanceTokenFee *big.Int
|
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
|
// 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)
|
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
|
// 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
|
// 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
|
// 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"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||||
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
|
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
|
@ -80,10 +81,60 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI {
|
||||||
return &PublicEthereumAPI{b}
|
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) {
|
func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) {
|
||||||
price, err := s.b.SuggestPrice(ctx)
|
tipcap, err := s.b.SuggestGasTipCap(ctx)
|
||||||
return (*hexutil.Big)(price), err
|
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
|
// 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),
|
"queued": make(map[string]map[string]*RPCTransaction),
|
||||||
}
|
}
|
||||||
pending, queue := s.b.TxPoolContent()
|
pending, queue := s.b.TxPoolContent()
|
||||||
|
curHeader := s.b.CurrentHeader()
|
||||||
// Flatten the pending transactions
|
// Flatten the pending transactions
|
||||||
for account, txs := range pending {
|
for account, txs := range pending {
|
||||||
dump := make(map[string]*RPCTransaction)
|
dump := make(map[string]*RPCTransaction)
|
||||||
for _, tx := range txs {
|
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
|
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 {
|
for account, txs := range queue {
|
||||||
dump := make(map[string]*RPCTransaction)
|
dump := make(map[string]*RPCTransaction)
|
||||||
for _, tx := range txs {
|
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
|
content["queued"][account.Hex()] = dump
|
||||||
}
|
}
|
||||||
return content
|
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.
|
// Status returns the number of pending and queued transaction in the pool.
|
||||||
func (s *PublicTxPoolAPI) Status() map[string]hexutil.Uint {
|
func (s *PublicTxPoolAPI) Status() map[string]hexutil.Uint {
|
||||||
pending, queue := s.b.Stats()
|
pending, queue := s.b.Stats()
|
||||||
|
|
@ -366,7 +440,7 @@ func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args *Transacti
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Set some sanity defaults and terminate on failure
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
// Assemble the transaction and sign with the wallet
|
// 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 {
|
if args.Gas == nil {
|
||||||
return nil, errors.New("gas not specified")
|
return nil, errors.New("gas not specified")
|
||||||
}
|
}
|
||||||
if args.GasPrice == nil {
|
if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) {
|
||||||
return nil, errors.New("gasPrice not specified")
|
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
|
||||||
}
|
}
|
||||||
if args.Nonce == nil {
|
if args.Nonce == nil {
|
||||||
return nil, errors.New("nonce not specified")
|
return nil, errors.New("nonce not specified")
|
||||||
}
|
}
|
||||||
// Before actually sign the transaction, ensure the transaction fee is reasonable.
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
signed, err := s.signTransaction(ctx, &args, passwd)
|
signed, err := s.signTransaction(ctx, &args, passwd)
|
||||||
|
|
@ -1247,7 +1322,7 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno
|
||||||
return candidatesWithStakeInfo, nil
|
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())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
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
|
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
|
// Setup context so it may be cancelled the call has completed
|
||||||
// or, in case of unmetered gas, setup a context with a timeout.
|
// or, in case of unmetered gas, setup a context with a timeout.
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
|
|
@ -1292,8 +1364,16 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
||||||
return nil, 0, false, err, nil
|
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.
|
// 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 {
|
if err != nil {
|
||||||
return nil, 0, false, 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 {
|
if args.To != nil && *args.To == common.MasternodeVotingSMCBinary {
|
||||||
timeout = 0
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
executable := func(gas uint64) (bool, []byte, error, error) {
|
||||||
args.Gas = (*hexutil.Uint64)(&gas)
|
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 err != nil {
|
||||||
if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) {
|
if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) {
|
||||||
return false, nil, nil, nil // Special case, raise gas limit
|
return false, nil, nil, nil // Special case, raise gas limit
|
||||||
|
|
@ -1558,14 +1638,11 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
|
||||||
return formatted
|
return formatted
|
||||||
}
|
}
|
||||||
|
|
||||||
// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
|
// RPCMarshalHeader converts the given header to the RPC output .
|
||||||
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
|
func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
||||||
// transaction hashes.
|
result := map[string]interface{}{
|
||||||
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{}{
|
|
||||||
"number": (*hexutil.Big)(head.Number),
|
"number": (*hexutil.Big)(head.Number),
|
||||||
"hash": b.Hash(),
|
"hash": head.Hash(),
|
||||||
"parentHash": head.ParentHash,
|
"parentHash": head.ParentHash,
|
||||||
"nonce": head.Nonce,
|
"nonce": head.Nonce,
|
||||||
"mixHash": head.MixDigest,
|
"mixHash": head.MixDigest,
|
||||||
|
|
@ -1574,9 +1651,8 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
|
||||||
"stateRoot": head.Root,
|
"stateRoot": head.Root,
|
||||||
"miner": head.Coinbase,
|
"miner": head.Coinbase,
|
||||||
"difficulty": (*hexutil.Big)(head.Difficulty),
|
"difficulty": (*hexutil.Big)(head.Difficulty),
|
||||||
"totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())),
|
|
||||||
"extraData": hexutil.Bytes(head.Extra),
|
"extraData": hexutil.Bytes(head.Extra),
|
||||||
"size": hexutil.Uint64(b.Size()),
|
"size": hexutil.Uint64(head.Size()),
|
||||||
"gasLimit": hexutil.Uint64(head.GasLimit),
|
"gasLimit": hexutil.Uint64(head.GasLimit),
|
||||||
"gasUsed": hexutil.Uint64(head.GasUsed),
|
"gasUsed": hexutil.Uint64(head.GasUsed),
|
||||||
"timestamp": (*hexutil.Big)(head.Time),
|
"timestamp": (*hexutil.Big)(head.Time),
|
||||||
|
|
@ -1587,6 +1663,21 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
|
||||||
"penalties": hexutil.Bytes(head.Penalties),
|
"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 {
|
if inclTx {
|
||||||
formatTx := func(tx *types.Transaction) (interface{}, error) {
|
formatTx := func(tx *types.Transaction) (interface{}, error) {
|
||||||
return tx.Hash(), nil
|
return tx.Hash(), nil
|
||||||
|
|
@ -1773,6 +1864,8 @@ type RPCTransaction struct {
|
||||||
From common.Address `json:"from"`
|
From common.Address `json:"from"`
|
||||||
Gas hexutil.Uint64 `json:"gas"`
|
Gas hexutil.Uint64 `json:"gas"`
|
||||||
GasPrice *hexutil.Big `json:"gasPrice"`
|
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||||
|
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
|
||||||
|
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
Input hexutil.Bytes `json:"input"`
|
Input hexutil.Bytes `json:"input"`
|
||||||
Nonce hexutil.Uint64 `json:"nonce"`
|
Nonce hexutil.Uint64 `json:"nonce"`
|
||||||
|
|
@ -1789,7 +1882,7 @@ type RPCTransaction struct {
|
||||||
|
|
||||||
// newRPCTransaction returns a transaction that will serialize to the RPC
|
// newRPCTransaction returns a transaction that will serialize to the RPC
|
||||||
// representation, with the given location metadata set (if available).
|
// 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
|
// Determine the signer. For replay-protected transactions, use the most permissive
|
||||||
// signer, because we assume that signers are backwards-compatible with old
|
// signer, because we assume that signers are backwards-compatible with old
|
||||||
// transactions. For non-protected transactions, the homestead signer signer is used
|
// 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 {
|
} else {
|
||||||
signer = types.HomesteadSigner{}
|
signer = types.HomesteadSigner{}
|
||||||
}
|
}
|
||||||
|
|
||||||
from, _ := types.Sender(signer, tx)
|
from, _ := types.Sender(signer, tx)
|
||||||
v, r, s := tx.RawSignatureValues()
|
v, r, s := tx.RawSignatureValues()
|
||||||
result := &RPCTransaction{
|
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.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
|
||||||
result.TransactionIndex = (*hexutil.Uint64)(&index)
|
result.TransactionIndex = (*hexutil.Uint64)(&index)
|
||||||
}
|
}
|
||||||
if tx.Type() == types.AccessListTxType {
|
switch tx.Type() {
|
||||||
|
case types.AccessListTxType:
|
||||||
al := tx.AccessList()
|
al := tx.AccessList()
|
||||||
result.Accesses = &al
|
result.Accesses = &al
|
||||||
result.ChainID = (*hexutil.Big)(tx.ChainId())
|
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
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
|
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
|
||||||
func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
|
func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction {
|
||||||
return newRPCTransaction(tx, common.Hash{}, 0, 0)
|
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.
|
// 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)) {
|
if index >= uint64(len(txs)) {
|
||||||
return nil
|
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.
|
// 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{}
|
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
|
// 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
|
return nil, 0, nil, err
|
||||||
}
|
}
|
||||||
var to common.Address
|
var to common.Address
|
||||||
|
|
@ -1944,27 +2051,25 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
accessList := prevTracer.AccessList()
|
accessList := prevTracer.AccessList()
|
||||||
log.Trace("Creating access list", "input", 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
|
// Copy the original db so we don't modify it
|
||||||
statedb := db.Copy()
|
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)
|
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
|
||||||
var balanceTokenFee *big.Int
|
var balanceTokenFee *big.Int
|
||||||
if value, ok := feeCapacity[to]; ok {
|
if value, ok := feeCapacity[to]; ok {
|
||||||
balanceTokenFee = value
|
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
|
// Apply the transaction with the access list tracer
|
||||||
tracer := vm.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
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)
|
vmenv, _, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, err
|
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
|
// 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
|
// Try to return an already finalized transaction
|
||||||
if tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash); tx != nil {
|
tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
|
||||||
return newRPCTransaction(tx, blockHash, blockNumber, index)
|
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
|
// No finalized transaction, try to retrieve it from the pool
|
||||||
if tx := s.b.GetPoolTransaction(hash); tx != nil {
|
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
|
// Transaction unknown, return as such
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
// 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) {
|
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
||||||
tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
|
tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
|
// When the transaction doesn't exist, the RPC method should return JSON null
|
||||||
|
// as per specification.
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
receipts, err := s.b.GetReceipts(ctx, blockHash)
|
receipts, err := s.b.GetReceipts(ctx, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(receipts) <= int(index) {
|
if uint64(len(receipts)) <= index {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
receipt := receipts[index]
|
receipt := receipts[index]
|
||||||
|
|
@ -2122,37 +2235,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha
|
||||||
// Derive the sender.
|
// Derive the sender.
|
||||||
bigblock := new(big.Int).SetUint64(blockNumber)
|
bigblock := new(big.Int).SetUint64(blockNumber)
|
||||||
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
|
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
|
||||||
from, _ := types.Sender(signer, tx)
|
return marshalReceipt(receipt, blockHash, blockNumber, signer, tx, int(index)), nil
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshalReceipt marshals a transaction receipt into a JSON object.
|
// 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,
|
"logs": receipt.Logs,
|
||||||
"logsBloom": receipt.Bloom,
|
"logsBloom": receipt.Bloom,
|
||||||
"type": hexutil.Uint(tx.Type()),
|
"type": hexutil.Uint(tx.Type()),
|
||||||
// uncomment below line after EIP-1559
|
"effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
|
||||||
// TODO: "effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign receipt status or post state.
|
// 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
|
// 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
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
// Assemble the transaction and sign with the wallet
|
// 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)
|
return SubmitTransaction(ctx, s.b, signed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FillTransaction fills the defaults (nonce, gas, gasPrice) on a given unsigned transaction,
|
// FillTransaction fills the defaults (nonce, gas, gasPrice or 1559 fields)
|
||||||
// and returns it to the caller for further processing (signing + broadcast)
|
// 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) {
|
func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
|
||||||
// Set some sanity defaults and terminate on failure
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
// Assemble the transaction and obtain rlp
|
// Assemble the transaction and obtain rlp
|
||||||
|
|
@ -3199,24 +3282,25 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Tra
|
||||||
if args.Gas == nil {
|
if args.Gas == nil {
|
||||||
return nil, errors.New("not specify Gas")
|
return nil, errors.New("not specify Gas")
|
||||||
}
|
}
|
||||||
if args.GasPrice == nil {
|
if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
|
||||||
return nil, errors.New("not specify GasPrice")
|
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
|
||||||
}
|
}
|
||||||
if args.Nonce == nil {
|
if args.Nonce == nil {
|
||||||
return nil, errors.New("not specify Nonce")
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
// Before actually sign the transaction, ensure the transaction fee is reasonable.
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
tx, err := s.sign(args.from(), args.toTransaction())
|
signed, err := s.sign(args.from(), tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data, err := tx.MarshalBinary()
|
data, err := signed.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -3236,11 +3320,12 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err
|
||||||
accounts[account.Address] = struct{}{}
|
accounts[account.Address] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
curHeader := s.b.CurrentHeader()
|
||||||
transactions := make([]*RPCTransaction, 0, len(pending))
|
transactions := make([]*RPCTransaction, 0, len(pending))
|
||||||
for _, tx := range pending {
|
for _, tx := range pending {
|
||||||
from, _ := types.Sender(s.signer, tx)
|
from, _ := types.Sender(s.signer, tx)
|
||||||
if _, exists := accounts[from]; exists {
|
if _, exists := accounts[from]; exists {
|
||||||
transactions = append(transactions, newRPCPendingTransaction(tx))
|
transactions = append(transactions, newRPCPendingTransaction(tx, curHeader, s.b.ChainConfig()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return transactions, nil
|
return transactions, nil
|
||||||
|
|
@ -3252,7 +3337,7 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs Transact
|
||||||
if sendArgs.Nonce == nil {
|
if sendArgs.Nonce == nil {
|
||||||
return common.Hash{}, errors.New("missing transaction nonce in transaction spec")
|
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
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
matchTx := sendArgs.toTransaction()
|
matchTx := sendArgs.toTransaction()
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,8 @@ type Backend interface {
|
||||||
// General Ethereum API
|
// General Ethereum API
|
||||||
Downloader() *downloader.Downloader
|
Downloader() *downloader.Downloader
|
||||||
ProtocolVersion() int
|
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
|
ChainDb() ethdb.Database
|
||||||
AccountManager() *accounts.Manager
|
AccountManager() *accounts.Manager
|
||||||
RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
|
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)
|
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
|
||||||
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
||||||
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*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)
|
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
|
||||||
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
||||||
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*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)
|
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
||||||
Stats() (pending int, queued int)
|
Stats() (pending int, queued int)
|
||||||
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
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
|
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||||
|
|
||||||
// Order Pool Transaction
|
// 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