// 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 (
"crypto/ecdsa"
"math"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
func u64 ( val uint64 ) * uint64 { return & val }
// 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 = & params . 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 ) ,
MuirGlacierBlock : big . NewInt ( 0 ) ,
BerlinBlock : big . NewInt ( 0 ) ,
LondonBlock : big . NewInt ( 0 ) ,
Ethash : new ( params . EthashConfig ) ,
TerminalTotalDifficulty : big . NewInt ( 0 ) ,
ShanghaiTime : new ( uint64 ) ,
CancunTime : new ( uint64 ) ,
}
signer = types . LatestSigner ( config )
key1 , _ = crypto . HexToECDSA ( "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" )
key2 , _ = crypto . HexToECDSA ( "0202020202020202020202020202020202020202020202020202002020202020" )
)
var makeTx = func ( key * ecdsa . PrivateKey , nonce uint64 , to common . Address , amount * big . Int , gasLimit uint64 , gasPrice * big . Int , data [ ] byte ) * types . Transaction {
tx , _ := types . SignTx ( types . NewTransaction ( nonce , to , amount , gasLimit , gasPrice , data ) , signer , key )
return tx
}
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 , key1 )
return tx
}
var mkDynamicCreationTx = func ( nonce uint64 , gasLimit uint64 , gasTipCap , gasFeeCap * big . Int , data [ ] byte ) * types . Transaction {
tx , _ := types . SignTx ( types . NewTx ( & types . DynamicFeeTx {
Nonce : nonce ,
GasTipCap : gasTipCap ,
GasFeeCap : gasFeeCap ,
Gas : gasLimit ,
Value : big . NewInt ( 0 ) ,
Data : data ,
} ) , signer , key1 )
return tx
}
var mkBlobTx = func ( nonce uint64 , to common . Address , gasLimit uint64 , gasTipCap , gasFeeCap , blobGasFeeCap * big . Int , hashes [ ] common . Hash ) * types . Transaction {
tx , err := types . SignTx ( types . NewTx ( & types . BlobTx {
Nonce : nonce ,
GasTipCap : uint256 . MustFromBig ( gasTipCap ) ,
GasFeeCap : uint256 . MustFromBig ( gasFeeCap ) ,
Gas : gasLimit ,
To : to ,
BlobHashes : hashes ,
BlobFeeCap : uint256 . MustFromBig ( blobGasFeeCap ) ,
Value : new ( uint256 . Int ) ,
} ) , signer , key1 )
if err != nil {
t . Fatal ( err )
}
return tx
}
{ // Tests against a 'recent' chain definition
var (
db = rawdb . NewMemoryDatabase ( )
gspec = & Genesis {
Config : config ,
Alloc : types . GenesisAlloc {
common . HexToAddress ( "0x71562b71999873DB5b286dF957af199Ec94617F7" ) : types . Account {
Balance : big . NewInt ( 1000000000000000000 ) , // 1 ether
Nonce : 0 ,
} ,
common . HexToAddress ( "0xfd0810DD14796680f72adf1a371963d0745BCc64" ) : types . Account {
Balance : big . NewInt ( 1000000000000000000 ) , // 1 ether
Nonce : math . MaxUint64 ,
} ,
} ,
}
blockchain , _ = NewBlockChain ( db , nil , gspec , nil , beacon . New ( ethash . NewFaker ( ) ) , vm . Config { } , nil )
tooBigInitCode = [ params . MaxInitCodeSize + 1 ] byte { }
)
defer blockchain . Stop ( )
eth/protocols/snap: fix snap sync failure on empty storage range (#28306)
This change addresses an issue in snap sync, specifically when the entire sync process can be halted due to an encountered empty storage range.
Currently, on the snap sync client side, the response to an empty (partial) storage range is discarded as a non-delivery. However, this response can be a valid response, when the particular range requested does not contain any slots.
For instance, consider a large contract where the entire key space is divided into 16 chunks, and there are no available slots in the last chunk [0xf] -> [end]. When the node receives a request for this particular range, the response includes:
The proof with origin [0xf]
A nil storage slot set
If we simply discard this response, the finalization of the last range will be skipped, halting the entire sync process indefinitely. The test case TestSyncWithUnevenStorage can reproduce the scenario described above.
In addition, this change also defines the common variables MaxAddress and MaxHash.
1 year ago
bigNumber := new ( big . Int ) . SetBytes ( common . MaxHash . Bytes ( ) )
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 ( key1 , 0 , common . Address { } , big . NewInt ( 0 ) , params . TxGas , big . NewInt ( 875000000 ) , nil ) ,
makeTx ( key1 , 0 , common . Address { } , big . NewInt ( 0 ) , params . TxGas , big . NewInt ( 875000000 ) , nil ) ,
} ,
want : "could not apply tx 1 [0x0026256b3939ed97e2c4a6f3fce8ecf83bdcfa6d507c47838c308a1fb0436f62]: nonce too low: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 0 state: 1" ,
} ,
{ // ErrNonceTooHigh
txs : [ ] * types . Transaction {
makeTx ( key1 , 100 , common . Address { } , big . NewInt ( 0 ) , params . TxGas , big . NewInt ( 875000000 ) , nil ) ,
} ,
want : "could not apply tx 0 [0xdebad714ca7f363bd0d8121c4518ad48fa469ca81b0a081be3d10c17460f751b]: nonce too high: address 0x71562b71999873DB5b286dF957af199Ec94617F7, tx: 100 state: 0" ,
} ,
{ // ErrNonceMax
txs : [ ] * types . Transaction {
makeTx ( key2 , math . MaxUint64 , common . Address { } , big . NewInt ( 0 ) , params . TxGas , big . NewInt ( 875000000 ) , nil ) ,
} ,
want : "could not apply tx 0 [0x84ea18d60eb2bb3b040e3add0eb72f757727122cc257dd858c67cb6591a85986]: nonce has max value: address 0xfd0810DD14796680f72adf1a371963d0745BCc64, nonce: 18446744073709551615" ,
} ,
{ // ErrGasLimitReached
txs : [ ] * types . Transaction {
makeTx ( key1 , 0 , common . Address { } , big . NewInt ( 0 ) , 21000000 , big . NewInt ( 875000000 ) , nil ) ,
} ,
want : "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached" ,
} ,
{ // ErrInsufficientFundsForTransfer
txs : [ ] * types . Transaction {
makeTx ( key1 , 0 , common . Address { } , big . NewInt ( 1000000000000000000 ) , params . TxGas , big . NewInt ( 875000000 ) , nil ) ,
} ,
want : "could not apply tx 0 [0x98c796b470f7fcab40aaef5c965a602b0238e1034cce6fb73823042dd0638d74]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000018375000000000" ,
} ,
{ // ErrInsufficientFunds
txs : [ ] * types . Transaction {
makeTx ( key1 , 0 , common . Address { } , big . NewInt ( 0 ) , params . TxGas , big . NewInt ( 900000000000000000 ) , nil ) ,
} ,
want : "could not apply tx 0 [0x4a69690c4b0cd85e64d0d9ea06302455b01e10a83db964d60281739752003440]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 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 ( key1 , 0 , common . Address { } , big . NewInt ( 0 ) , params . TxGas - 1000 , big . NewInt ( 875000000 ) , nil ) ,
} ,
want : "could not apply tx 0 [0xcf3b049a0b516cb4f9274b3e2a264359e2ba53b2fb64b7bda2c634d5c9d01fca]: intrinsic gas too low: have 20000, want 21000" ,
} ,
{ // ErrGasLimitReached
txs : [ ] * types . Transaction {
makeTx ( key1 , 0 , common . Address { } , big . NewInt ( 0 ) , params . TxGas * 1000 , big . NewInt ( 875000000 ) , nil ) ,
} ,
want : "could not apply tx 0 [0xbd49d8dadfd47fb846986695f7d4da3f7b2c48c8da82dbc211a26eb124883de9]: gas limit reached" ,
} ,
{ // ErrFeeCapTooLow
txs : [ ] * types . Transaction {
mkDynamicTx ( 0 , common . Address { } , params . TxGas , big . NewInt ( 0 ) , big . NewInt ( 0 ) ) ,
} ,
want : "could not apply tx 0 [0xc4ab868fef0c82ae0387b742aee87907f2d0fc528fc6ea0a021459fb0fc4a4a8]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 0, baseFee: 875000000" ,
} ,
{ // ErrTipVeryHigh
txs : [ ] * types . Transaction {
mkDynamicTx ( 0 , common . Address { } , params . TxGas , tooBigNumber , big . NewInt ( 1 ) ) ,
} ,
want : "could not apply tx 0 [0x15b8391b9981f266b32f3ab7da564bbeb3d6c21628364ea9b32a21139f89f712]: max priority fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxPriorityFeePerGas bit length: 257" ,
} ,
{ // ErrFeeCapVeryHigh
txs : [ ] * types . Transaction {
mkDynamicTx ( 0 , common . Address { } , params . TxGas , big . NewInt ( 1 ) , tooBigNumber ) ,
} ,
want : "could not apply tx 0 [0x48bc299b83fdb345c57478f239e89814bb3063eb4e4b49f3b6057a69255c16bd]: max fee per gas higher than 2^256-1: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas bit length: 257" ,
} ,
{ // ErrTipAboveFeeCap
txs : [ ] * types . Transaction {
mkDynamicTx ( 0 , common . Address { } , params . TxGas , big . NewInt ( 2 ) , big . NewInt ( 1 ) ) ,
} ,
want : "could not apply tx 0 [0xf987a31ff0c71895780a7612f965a0c8b056deb54e020bb44fa478092f14c9b4]: max priority fee per gas higher than max fee per gas: address 0x71562b71999873DB5b286dF957af199Ec94617F7, 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 : "could not apply tx 0 [0x413603cd096a87f41b1660d3ed3e27d62e1da78eac138961c0a1314ed43bd129]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 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 : "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 required balance exceeds 256 bits" ,
} ,
{ // ErrMaxInitCodeSizeExceeded
txs : [ ] * types . Transaction {
mkDynamicCreationTx ( 0 , 500000 , common . Big0 , big . NewInt ( params . InitialBaseFee ) , tooBigInitCode [ : ] ) ,
} ,
want : "could not apply tx 0 [0xd491405f06c92d118dd3208376fcee18a57c54bc52063ee4a26b1cf296857c25]: max initcode size exceeded: code size 49153 limit 49152" ,
} ,
{ // ErrIntrinsicGas: Not enough gas to cover init code
txs : [ ] * types . Transaction {
mkDynamicCreationTx ( 0 , 54299 , common . Big0 , big . NewInt ( params . InitialBaseFee ) , make ( [ ] byte , 320 ) ) ,
} ,
want : "could not apply tx 0 [0xfd49536a9b323769d8472fcb3ebb3689b707a349379baee3e2ee3fe7baae06a1]: intrinsic gas too low: have 54299, want 54300" ,
} ,
{ // ErrBlobFeeCapTooLow
txs : [ ] * types . Transaction {
mkBlobTx ( 0 , common . Address { } , params . TxGas , big . NewInt ( 1 ) , big . NewInt ( 1 ) , big . NewInt ( 0 ) , [ ] common . Hash { ( common . Hash { 1 } ) } ) ,
} ,
want : "could not apply tx 0 [0x6c11015985ce82db691d7b2d017acda296db88b811c3c60dc71449c76256c716]: max fee per gas less than block base fee: address 0x71562b71999873DB5b286dF957af199Ec94617F7, maxFeePerGas: 1, baseFee: 875000000" ,
} ,
} {
block := GenerateBadBlock ( gspec . ToBlock ( ) , beacon . New ( ethash . NewFaker ( ) ) , tt . txs , gspec . Config , false )
_ , 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 )
}
}
}
// ErrTxTypeNotSupported, For this, we need an older chain
{
var (
db = rawdb . NewMemoryDatabase ( )
gspec = & Genesis {
Config : & params . 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 ) ,
MuirGlacierBlock : big . NewInt ( 0 ) ,
} ,
Alloc : types . GenesisAlloc {
common . HexToAddress ( "0x71562b71999873DB5b286dF957af199Ec94617F7" ) : types . Account {
Balance : big . NewInt ( 1000000000000000000 ) , // 1 ether
Nonce : 0 ,
} ,
} ,
}
blockchain , _ = NewBlockChain ( db , nil , gspec , nil , ethash . NewFaker ( ) , vm . Config { } , nil )
)
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 : "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported" ,
} ,
} {
block := GenerateBadBlock ( gspec . ToBlock ( ) , ethash . NewFaker ( ) , tt . txs , gspec . Config , true )
_ , 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 )
}
}
}
// ErrSenderNoEOA, for this we need the sender to have contract code
{
var (
db = rawdb . NewMemoryDatabase ( )
gspec = & Genesis {
Config : config ,
Alloc : types . GenesisAlloc {
common . HexToAddress ( "0x71562b71999873DB5b286dF957af199Ec94617F7" ) : types . Account {
Balance : big . NewInt ( 1000000000000000000 ) , // 1 ether
Nonce : 0 ,
Code : common . FromHex ( "0xB0B0FACE" ) ,
} ,
} ,
}
blockchain , _ = NewBlockChain ( db , nil , gspec , nil , beacon . New ( ethash . NewFaker ( ) ) , vm . Config { } , nil )
)
defer blockchain . Stop ( )
for i , tt := range [ ] struct {
txs [ ] * types . Transaction
want string
} {
{ // ErrSenderNoEOA
txs : [ ] * types . Transaction {
mkDynamicTx ( 0 , common . Address { } , params . TxGas - 1000 , big . NewInt ( 0 ) , big . NewInt ( 0 ) ) ,
} ,
want : "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1" ,
} ,
} {
block := GenerateBadBlock ( gspec . ToBlock ( ) , beacon . New ( ethash . NewFaker ( ) ) , tt . txs , gspec . Config , false )
_ , 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 ( parent * types . Block , engine consensus . Engine , txs types . Transactions , config * params . ChainConfig , isPOW bool ) * types . Block {
difficulty := big . NewInt ( 0 )
if isPOW {
fakeChainReader := newChainMaker ( nil , config , engine )
difficulty = engine . CalcDifficulty ( fakeChainReader , parent . Time ( ) + 10 , & types . Header {
Number : parent . Number ( ) ,
Time : parent . Time ( ) ,
Difficulty : parent . Difficulty ( ) ,
UncleHash : parent . UncleHash ( ) ,
} )
}
header := & types . Header {
ParentHash : parent . Hash ( ) ,
Coinbase : parent . Coinbase ( ) ,
Difficulty : difficulty ,
GasLimit : parent . GasLimit ( ) ,
Number : new ( big . Int ) . Add ( parent . Number ( ) , common . Big1 ) ,
Time : parent . Time ( ) + 10 ,
UncleHash : types . EmptyUncleHash ,
}
if config . IsLondon ( header . Number ) {
header . BaseFee = eip1559 . CalcBaseFee ( config , parent . Header ( ) )
}
if config . IsShanghai ( header . Number , header . Time ) {
header . WithdrawalsHash = & types . EmptyWithdrawalsHash
}
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
var nBlobs int
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 ( )
nBlobs += len ( tx . BlobHashes ( ) )
}
header . Root = common . BytesToHash ( hasher . Sum ( nil ) )
if config . IsCancun ( header . Number , header . Time ) {
var pExcess , pUsed = uint64 ( 0 ) , uint64 ( 0 )
if parent . ExcessBlobGas ( ) != nil {
pExcess = * parent . ExcessBlobGas ( )
pUsed = * parent . BlobGasUsed ( )
}
excess := eip4844 . CalcExcessBlobGas ( pExcess , pUsed )
used := uint64 ( nBlobs * params . BlobTxBlobGasPerBlob )
header . ExcessBlobGas = & excess
header . BlobGasUsed = & used
beaconRoot := common . HexToHash ( "0xbeac00" )
header . ParentBeaconRoot = & beaconRoot
}
// Assemble and return the final block for sealing
body := & types . Body { Transactions : txs }
if config . IsShanghai ( header . Number , header . Time ) {
body . Withdrawals = [ ] * types . Withdrawal { }
}
return types . NewBlock ( header , body , receipts , trie . NewStackTrie ( nil ) )
}