core, tests: EIP-4844 transaction processing logic (#27721)
This updates the reference tests to the latest version and also adds logic to process EIP-4844 blob transactions into the state transition. We are now passing most Cancun fork tests. Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
parent
99e000cb13
commit
b058cf454b
@ -117,6 +117,7 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
|
|||||||
// Test failed, mark as so and dump any state to aid debugging
|
// Test failed, mark as so and dump any state to aid debugging
|
||||||
result.Pass, result.Error = false, err.Error()
|
result.Pass, result.Error = false, err.Error()
|
||||||
if dump && s != nil {
|
if dump && s != nil {
|
||||||
|
s, _ = state.New(*result.Root, s.Database(), nil)
|
||||||
dump := s.RawDump(nil)
|
dump := s.RawDump(nil)
|
||||||
result.State = &dump
|
result.State = &dump
|
||||||
}
|
}
|
||||||
|
@ -86,18 +86,8 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
|||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.Transactions() {
|
||||||
// Count the number of blobs to validate against the header's dataGasUsed
|
// Count the number of blobs to validate against the header's dataGasUsed
|
||||||
blobs += len(tx.BlobHashes())
|
blobs += len(tx.BlobHashes())
|
||||||
|
// The individual checks for blob validity (version-check + not empty)
|
||||||
// Validate the data blobs individually too
|
// happens in the state_transition check.
|
||||||
if tx.Type() == types.BlobTxType {
|
|
||||||
if len(tx.BlobHashes()) == 0 {
|
|
||||||
return errors.New("no-blob blob transaction present in block body")
|
|
||||||
}
|
|
||||||
for _, hash := range tx.BlobHashes() {
|
|
||||||
if hash[0] != params.BlobTxHashVersion {
|
|
||||||
return fmt.Errorf("blob hash version mismatch (have %d, supported %d)", hash[0], params.BlobTxHashVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if header.DataGasUsed != nil {
|
if header.DataGasUsed != nil {
|
||||||
if want := *header.DataGasUsed / params.BlobTxDataGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated
|
if want := *header.DataGasUsed / params.BlobTxDataGasPerBlob; uint64(blobs) != want { // div because the header is surely good vs the body might be bloated
|
||||||
|
@ -100,4 +100,8 @@ var (
|
|||||||
|
|
||||||
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
|
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
|
||||||
ErrSenderNoEOA = errors.New("sender not an eoa")
|
ErrSenderNoEOA = errors.New("sender not an eoa")
|
||||||
|
|
||||||
|
// ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the
|
||||||
|
// data gas fee of the block.
|
||||||
|
ErrBlobFeeCapTooLow = errors.New("max fee per data gas less than block data gas fee")
|
||||||
)
|
)
|
||||||
|
@ -66,6 +66,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||||||
BaseFee: baseFee,
|
BaseFee: baseFee,
|
||||||
GasLimit: header.GasLimit,
|
GasLimit: header.GasLimit,
|
||||||
Random: random,
|
Random: random,
|
||||||
|
ExcessDataGas: header.ExcessDataGas,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
|||||||
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"`
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||||
|
ExcessDataGas *math.HexOrDecimal64 `json:"excessDataGas"`
|
||||||
|
DataGasUsed *math.HexOrDecimal64 `json:"dataGasUsed"`
|
||||||
}
|
}
|
||||||
var enc Genesis
|
var enc Genesis
|
||||||
enc.Config = g.Config
|
enc.Config = g.Config
|
||||||
@ -51,6 +53,8 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
|||||||
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)
|
enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee)
|
||||||
|
enc.ExcessDataGas = (*math.HexOrDecimal64)(g.ExcessDataGas)
|
||||||
|
enc.DataGasUsed = (*math.HexOrDecimal64)(g.DataGasUsed)
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +74,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
|||||||
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"`
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||||
|
ExcessDataGas *math.HexOrDecimal64 `json:"excessDataGas"`
|
||||||
|
DataGasUsed *math.HexOrDecimal64 `json:"dataGasUsed"`
|
||||||
}
|
}
|
||||||
var dec Genesis
|
var dec Genesis
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
@ -120,5 +126,11 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
|||||||
if dec.BaseFee != nil {
|
if dec.BaseFee != nil {
|
||||||
g.BaseFee = (*big.Int)(dec.BaseFee)
|
g.BaseFee = (*big.Int)(dec.BaseFee)
|
||||||
}
|
}
|
||||||
|
if dec.ExcessDataGas != nil {
|
||||||
|
g.ExcessDataGas = (*uint64)(dec.ExcessDataGas)
|
||||||
|
}
|
||||||
|
if dec.DataGasUsed != nil {
|
||||||
|
g.DataGasUsed = (*uint64)(dec.DataGasUsed)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,9 @@ 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"`
|
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
|
||||||
|
ExcessDataGas *uint64 `json:"excessDataGas"` // EIP-4844
|
||||||
|
DataGasUsed *uint64 `json:"dataGasUsed"` // EIP-4844
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
||||||
@ -96,6 +98,9 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
|||||||
genesis.Difficulty = genesisHeader.Difficulty
|
genesis.Difficulty = genesisHeader.Difficulty
|
||||||
genesis.Mixhash = genesisHeader.MixDigest
|
genesis.Mixhash = genesisHeader.MixDigest
|
||||||
genesis.Coinbase = genesisHeader.Coinbase
|
genesis.Coinbase = genesisHeader.Coinbase
|
||||||
|
genesis.BaseFee = genesisHeader.BaseFee
|
||||||
|
genesis.ExcessDataGas = genesisHeader.ExcessDataGas
|
||||||
|
genesis.DataGasUsed = genesisHeader.DataGasUsed
|
||||||
|
|
||||||
return &genesis, nil
|
return &genesis, nil
|
||||||
}
|
}
|
||||||
@ -221,8 +226,10 @@ 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
|
||||||
|
BaseFee *math.HexOrDecimal256
|
||||||
|
ExcessDataGas *math.HexOrDecimal64
|
||||||
|
DataGasUsed *math.HexOrDecimal64
|
||||||
}
|
}
|
||||||
|
|
||||||
type genesisAccountMarshaling struct {
|
type genesisAccountMarshaling struct {
|
||||||
@ -463,10 +470,23 @@ func (g *Genesis) ToBlock() *types.Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var withdrawals []*types.Withdrawal
|
var withdrawals []*types.Withdrawal
|
||||||
if g.Config != nil && g.Config.IsShanghai(big.NewInt(int64(g.Number)), g.Timestamp) {
|
if conf := g.Config; conf != nil {
|
||||||
|
num := big.NewInt(int64(g.Number))
|
||||||
|
if conf.IsShanghai(num, g.Timestamp) {
|
||||||
head.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
head.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||||
withdrawals = make([]*types.Withdrawal, 0)
|
withdrawals = make([]*types.Withdrawal, 0)
|
||||||
}
|
}
|
||||||
|
if conf.IsCancun(num, g.Timestamp) {
|
||||||
|
head.ExcessDataGas = g.ExcessDataGas
|
||||||
|
head.DataGasUsed = g.DataGasUsed
|
||||||
|
if head.ExcessDataGas == nil {
|
||||||
|
head.ExcessDataGas = new(uint64)
|
||||||
|
}
|
||||||
|
if head.DataGasUsed == nil {
|
||||||
|
head.DataGasUsed = new(uint64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)).WithWithdrawals(withdrawals)
|
return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)).WithWithdrawals(withdrawals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +157,6 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
|||||||
}
|
}
|
||||||
// Create a new context to be used in the EVM environment
|
// Create a new context to be used in the EVM environment
|
||||||
blockContext := NewEVMBlockContext(header, bc, author)
|
blockContext := NewEVMBlockContext(header, bc, author)
|
||||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
|
vmenv := vm.NewEVM(blockContext, vm.TxContext{BlobHashes: tx.BlobHashes()}, statedb, config, cfg)
|
||||||
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
|
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,6 +59,10 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
BerlinBlock: big.NewInt(0),
|
BerlinBlock: big.NewInt(0),
|
||||||
LondonBlock: big.NewInt(0),
|
LondonBlock: big.NewInt(0),
|
||||||
Ethash: new(params.EthashConfig),
|
Ethash: new(params.EthashConfig),
|
||||||
|
TerminalTotalDifficulty: big.NewInt(0),
|
||||||
|
TerminalTotalDifficultyPassed: true,
|
||||||
|
ShanghaiTime: new(uint64),
|
||||||
|
CancunTime: new(uint64),
|
||||||
}
|
}
|
||||||
signer = types.LatestSigner(config)
|
signer = types.LatestSigner(config)
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
@ -89,6 +94,22 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
}), signer, key1)
|
}), signer, key1)
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
var mkBlobTx = func(nonce uint64, to common.Address, gasLimit uint64, gasTipCap, gasFeeCap *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,
|
||||||
|
Value: new(uint256.Int),
|
||||||
|
}), signer, key1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
{ // Tests against a 'recent' chain definition
|
{ // Tests against a 'recent' chain definition
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
@ -105,8 +126,10 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
||||||
|
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
||||||
)
|
)
|
||||||
|
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
bigNumber := new(big.Int).SetBytes(common.FromHex("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||||
tooBigNumber := new(big.Int).Set(bigNumber)
|
tooBigNumber := new(big.Int).Set(bigNumber)
|
||||||
@ -209,8 +232,26 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
|
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
|
||||||
},
|
},
|
||||||
|
{ // 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), []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(), ethash.NewFaker(), tt.txs, gspec.Config)
|
block := GenerateBadBlock(gspec.ToBlock(), beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
|
||||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("block imported without errors")
|
t.Fatal("block imported without errors")
|
||||||
@ -284,7 +325,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
||||||
)
|
)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
for i, tt := range []struct {
|
for i, tt := range []struct {
|
||||||
@ -298,73 +339,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
|||||||
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
|
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
block := GenerateBadBlock(gspec.ToBlock(), ethash.NewFaker(), tt.txs, gspec.Config)
|
block := GenerateBadBlock(gspec.ToBlock(), beacon.New(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrMaxInitCodeSizeExceeded, for this we need extra Shanghai (EIP-3860) enabled.
|
|
||||||
{
|
|
||||||
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),
|
|
||||||
MuirGlacierBlock: big.NewInt(0),
|
|
||||||
BerlinBlock: big.NewInt(0),
|
|
||||||
LondonBlock: big.NewInt(0),
|
|
||||||
ArrowGlacierBlock: big.NewInt(0),
|
|
||||||
GrayGlacierBlock: big.NewInt(0),
|
|
||||||
MergeNetsplitBlock: big.NewInt(0),
|
|
||||||
TerminalTotalDifficulty: big.NewInt(0),
|
|
||||||
TerminalTotalDifficultyPassed: true,
|
|
||||||
ShanghaiTime: u64(0),
|
|
||||||
},
|
|
||||||
Alloc: GenesisAlloc{
|
|
||||||
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
|
|
||||||
Balance: big.NewInt(1000000000000000000), // 1 ether
|
|
||||||
Nonce: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
genesis = gspec.MustCommit(db)
|
|
||||||
blockchain, _ = NewBlockChain(db, nil, gspec, nil, beacon.New(ethash.NewFaker()), vm.Config{}, nil, nil)
|
|
||||||
tooBigInitCode = [params.MaxInitCodeSize + 1]byte{}
|
|
||||||
smallInitCode = [320]byte{}
|
|
||||||
)
|
|
||||||
defer blockchain.Stop()
|
|
||||||
for i, tt := range []struct {
|
|
||||||
txs []*types.Transaction
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{ // ErrMaxInitCodeSizeExceeded
|
|
||||||
txs: []*types.Transaction{
|
|
||||||
mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header()), tooBigInitCode[:]),
|
|
||||||
},
|
|
||||||
want: "could not apply tx 0 [0x832b54a6c3359474a9f504b1003b2cc1b6fcaa18e4ef369eb45b5d40dad6378f]: 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, misc.CalcBaseFee(config, genesis.Header()), smallInitCode[:]),
|
|
||||||
},
|
|
||||||
want: "could not apply tx 0 [0x39b7436cb432d3662a25626474282c5c4c1a213326fd87e4e18a91477bae98b2]: intrinsic gas too low: have 54299, want 54300",
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
block := GenerateBadBlock(genesis, beacon.New(ethash.NewFaker()), tt.txs, gspec.Config)
|
|
||||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("block imported without errors")
|
t.Fatal("block imported without errors")
|
||||||
@ -412,6 +387,7 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||||||
hasher := sha3.NewLegacyKeccak256()
|
hasher := sha3.NewLegacyKeccak256()
|
||||||
hasher.Write(header.Number.Bytes())
|
hasher.Write(header.Number.Bytes())
|
||||||
var cumulativeGas uint64
|
var cumulativeGas uint64
|
||||||
|
var nBlobs int
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
txh := tx.Hash()
|
txh := tx.Hash()
|
||||||
hasher.Write(txh[:])
|
hasher.Write(txh[:])
|
||||||
@ -420,8 +396,20 @@ func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Tr
|
|||||||
receipt.GasUsed = tx.Gas()
|
receipt.GasUsed = tx.Gas()
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
cumulativeGas += tx.Gas()
|
cumulativeGas += tx.Gas()
|
||||||
|
nBlobs += len(tx.BlobHashes())
|
||||||
}
|
}
|
||||||
header.Root = common.BytesToHash(hasher.Sum(nil))
|
header.Root = common.BytesToHash(hasher.Sum(nil))
|
||||||
|
if config.IsCancun(header.Number, header.Time) {
|
||||||
|
var pExcess, pUsed = uint64(0), uint64(0)
|
||||||
|
if parent.ExcessDataGas() != nil {
|
||||||
|
pExcess = *parent.ExcessDataGas()
|
||||||
|
pUsed = *parent.DataGasUsed()
|
||||||
|
}
|
||||||
|
excess := misc.CalcExcessDataGas(pExcess, pUsed)
|
||||||
|
used := uint64(nBlobs * params.BlobTxDataGasPerBlob)
|
||||||
|
header.ExcessDataGas = &excess
|
||||||
|
header.DataGasUsed = &used
|
||||||
|
}
|
||||||
// Assemble and return the final block for sealing
|
// Assemble and return the final block for sealing
|
||||||
if config.IsShanghai(header.Number, header.Time) {
|
if config.IsShanghai(header.Number, header.Time) {
|
||||||
return types.NewBlockWithWithdrawals(header, txs, nil, receipts, []*types.Withdrawal{}, trie.NewStackTrie(nil))
|
return types.NewBlockWithWithdrawals(header, txs, nil, receipts, []*types.Withdrawal{}, trie.NewStackTrie(nil))
|
||||||
|
@ -17,12 +17,14 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
cmath "github.com/ethereum/go-ethereum/common/math"
|
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
@ -135,6 +137,7 @@ type Message struct {
|
|||||||
GasTipCap *big.Int
|
GasTipCap *big.Int
|
||||||
Data []byte
|
Data []byte
|
||||||
AccessList types.AccessList
|
AccessList types.AccessList
|
||||||
|
BlobGasFeeCap *big.Int
|
||||||
BlobHashes []common.Hash
|
BlobHashes []common.Hash
|
||||||
|
|
||||||
// When SkipAccountChecks is true, the message nonce is not checked against the
|
// When SkipAccountChecks is true, the message nonce is not checked against the
|
||||||
@ -157,6 +160,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
|||||||
AccessList: tx.AccessList(),
|
AccessList: tx.AccessList(),
|
||||||
SkipAccountChecks: false,
|
SkipAccountChecks: false,
|
||||||
BlobHashes: tx.BlobHashes(),
|
BlobHashes: tx.BlobHashes(),
|
||||||
|
BlobGasFeeCap: tx.BlobGasFeeCap(),
|
||||||
}
|
}
|
||||||
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
||||||
if baseFee != nil {
|
if baseFee != nil {
|
||||||
@ -230,12 +234,28 @@ func (st *StateTransition) to() common.Address {
|
|||||||
func (st *StateTransition) buyGas() error {
|
func (st *StateTransition) buyGas() error {
|
||||||
mgval := new(big.Int).SetUint64(st.msg.GasLimit)
|
mgval := new(big.Int).SetUint64(st.msg.GasLimit)
|
||||||
mgval = mgval.Mul(mgval, st.msg.GasPrice)
|
mgval = mgval.Mul(mgval, st.msg.GasPrice)
|
||||||
balanceCheck := mgval
|
balanceCheck := new(big.Int).Set(mgval)
|
||||||
if st.msg.GasFeeCap != nil {
|
if st.msg.GasFeeCap != nil {
|
||||||
balanceCheck = new(big.Int).SetUint64(st.msg.GasLimit)
|
balanceCheck.SetUint64(st.msg.GasLimit)
|
||||||
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
|
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
|
||||||
balanceCheck.Add(balanceCheck, st.msg.Value)
|
balanceCheck.Add(balanceCheck, st.msg.Value)
|
||||||
}
|
}
|
||||||
|
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
||||||
|
if dataGas := st.dataGasUsed(); dataGas > 0 {
|
||||||
|
if st.evm.Context.ExcessDataGas == nil {
|
||||||
|
// programming error
|
||||||
|
panic("missing field excess data gas")
|
||||||
|
}
|
||||||
|
// Check that the user has enough funds to cover dataGasUsed * tx.BlobGasFeeCap
|
||||||
|
blobBalanceCheck := new(big.Int).SetUint64(dataGas)
|
||||||
|
blobBalanceCheck.Mul(blobBalanceCheck, st.msg.BlobGasFeeCap)
|
||||||
|
balanceCheck.Add(balanceCheck, blobBalanceCheck)
|
||||||
|
// Pay for dataGasUsed * actual blob fee
|
||||||
|
blobFee := new(big.Int).SetUint64(dataGas)
|
||||||
|
blobFee.Mul(blobFee, misc.CalcBlobFee(*st.evm.Context.ExcessDataGas))
|
||||||
|
mgval.Add(mgval, blobFee)
|
||||||
|
}
|
||||||
|
}
|
||||||
if have, want := st.state.GetBalance(st.msg.From), balanceCheck; have.Cmp(want) < 0 {
|
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)
|
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
|
||||||
}
|
}
|
||||||
@ -297,6 +317,28 @@ func (st *StateTransition) preCheck() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check the blob version validity
|
||||||
|
if msg.BlobHashes != nil {
|
||||||
|
if len(msg.BlobHashes) == 0 {
|
||||||
|
return errors.New("blob transaction missing blob hashes")
|
||||||
|
}
|
||||||
|
for i, hash := range msg.BlobHashes {
|
||||||
|
if hash[0] != params.BlobTxHashVersion {
|
||||||
|
return fmt.Errorf("blob %d hash version mismatch (have %d, supported %d)",
|
||||||
|
i, hash[0], params.BlobTxHashVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
|
||||||
|
if st.dataGasUsed() > 0 {
|
||||||
|
// Check that the user is paying at least the current blob fee
|
||||||
|
if have, want := st.msg.BlobGasFeeCap, misc.CalcBlobFee(*st.evm.Context.ExcessDataGas); have.Cmp(want) < 0 {
|
||||||
|
return fmt.Errorf("%w: address %v have %v want %v", ErrBlobFeeCapTooLow, st.msg.From.Hex(), have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return st.buyGas()
|
return st.buyGas()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,3 +469,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
|
|||||||
func (st *StateTransition) gasUsed() uint64 {
|
func (st *StateTransition) gasUsed() uint64 {
|
||||||
return st.initialGas - st.gasRemaining
|
return st.initialGas - st.gasRemaining
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dataGasUsed returns the amount of data gas used by the message.
|
||||||
|
func (st *StateTransition) dataGasUsed() uint64 {
|
||||||
|
return uint64(len(st.msg.BlobHashes) * params.BlobTxDataGasPerBlob)
|
||||||
|
}
|
||||||
|
@ -74,6 +74,7 @@ type BlockContext struct {
|
|||||||
Difficulty *big.Int // Provides information for DIFFICULTY
|
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||||
BaseFee *big.Int // Provides information for BASEFEE
|
BaseFee *big.Int // Provides information for BASEFEE
|
||||||
Random *common.Hash // Provides information for PREVRANDAO
|
Random *common.Hash // Provides information for PREVRANDAO
|
||||||
|
ExcessDataGas *uint64 // ExcessDataGas field in the header, needed to compute the data
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxContext provides the EVM with information about a transaction.
|
// TxContext provides the EVM with information about a transaction.
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -26,6 +27,8 @@ func (s stTransaction) MarshalJSON() ([]byte, error) {
|
|||||||
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
||||||
Value []string `json:"value"`
|
Value []string `json:"value"`
|
||||||
PrivateKey hexutil.Bytes `json:"secretKey"`
|
PrivateKey hexutil.Bytes `json:"secretKey"`
|
||||||
|
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||||
|
BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerDataGas,omitempty"`
|
||||||
}
|
}
|
||||||
var enc stTransaction
|
var enc stTransaction
|
||||||
enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice)
|
enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice)
|
||||||
@ -43,6 +46,8 @@ func (s stTransaction) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
enc.Value = s.Value
|
enc.Value = s.Value
|
||||||
enc.PrivateKey = s.PrivateKey
|
enc.PrivateKey = s.PrivateKey
|
||||||
|
enc.BlobVersionedHashes = s.BlobVersionedHashes
|
||||||
|
enc.BlobGasFeeCap = (*math.HexOrDecimal256)(s.BlobGasFeeCap)
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +64,8 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error {
|
|||||||
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
|
||||||
Value []string `json:"value"`
|
Value []string `json:"value"`
|
||||||
PrivateKey *hexutil.Bytes `json:"secretKey"`
|
PrivateKey *hexutil.Bytes `json:"secretKey"`
|
||||||
|
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||||
|
BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerDataGas,omitempty"`
|
||||||
}
|
}
|
||||||
var dec stTransaction
|
var dec stTransaction
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
@ -97,5 +104,11 @@ func (s *stTransaction) UnmarshalJSON(input []byte) error {
|
|||||||
if dec.PrivateKey != nil {
|
if dec.PrivateKey != nil {
|
||||||
s.PrivateKey = *dec.PrivateKey
|
s.PrivateKey = *dec.PrivateKey
|
||||||
}
|
}
|
||||||
|
if dec.BlobVersionedHashes != nil {
|
||||||
|
s.BlobVersionedHashes = dec.BlobVersionedHashes
|
||||||
|
}
|
||||||
|
if dec.BlobGasFeeCap != nil {
|
||||||
|
s.BlobGasFeeCap = (*big.Int)(dec.BlobGasFeeCap)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -48,23 +48,24 @@ func TestState(t *testing.T) {
|
|||||||
st.slow(`^stStaticCall/static_Return50000`)
|
st.slow(`^stStaticCall/static_Return50000`)
|
||||||
st.slow(`^stSystemOperationsTest/CallRecursiveBomb`)
|
st.slow(`^stSystemOperationsTest/CallRecursiveBomb`)
|
||||||
st.slow(`^stTransactionTest/Opcodes_TransactionInit`)
|
st.slow(`^stTransactionTest/Opcodes_TransactionInit`)
|
||||||
|
|
||||||
// Very time consuming
|
// Very time consuming
|
||||||
st.skipLoad(`^stTimeConsuming/`)
|
st.skipLoad(`^stTimeConsuming/`)
|
||||||
st.skipLoad(`.*vmPerformance/loop.*`)
|
st.skipLoad(`.*vmPerformance/loop.*`)
|
||||||
|
|
||||||
// Uses 1GB RAM per tested fork
|
// Uses 1GB RAM per tested fork
|
||||||
st.skipLoad(`^stStaticCall/static_Call1MB`)
|
st.skipLoad(`^stStaticCall/static_Call1MB`)
|
||||||
|
|
||||||
// Broken tests:
|
// Broken tests:
|
||||||
//
|
// EOF is not part of cancun
|
||||||
// The stEOF tests are generated with EOF as part of Shanghai, which
|
st.skipLoad(`^stEOF/`)
|
||||||
// is erroneous. Therefore, these tests are skipped.
|
|
||||||
st.skipLoad(`^EIPTests/stEOF/`)
|
|
||||||
// Expected failures:
|
// Expected failures:
|
||||||
|
// These EIP-4844 tests need to be regenerated.
|
||||||
|
st.fails(`stEIP4844-blobtransactions/opcodeBlobhashOutOfRange.json`, "test has incorrect state root")
|
||||||
|
st.fails(`stEIP4844-blobtransactions/opcodeBlobhBounds.json`, "test has incorrect state root")
|
||||||
|
|
||||||
// For Istanbul, older tests were moved into LegacyTests
|
// For Istanbul, older tests were moved into LegacyTests
|
||||||
for _, dir := range []string{
|
for _, dir := range []string{
|
||||||
|
filepath.Join(baseDir, "EIPTests", "StateTests"),
|
||||||
stateTestDir,
|
stateTestDir,
|
||||||
legacyStateTestDir,
|
legacyStateTestDir,
|
||||||
benchmarksDir,
|
benchmarksDir,
|
||||||
|
@ -113,6 +113,8 @@ type stTransaction struct {
|
|||||||
GasLimit []uint64 `json:"gasLimit"`
|
GasLimit []uint64 `json:"gasLimit"`
|
||||||
Value []string `json:"value"`
|
Value []string `json:"value"`
|
||||||
PrivateKey []byte `json:"secretKey"`
|
PrivateKey []byte `json:"secretKey"`
|
||||||
|
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
|
||||||
|
BlobGasFeeCap *big.Int `json:"maxFeePerDataGas,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type stTransactionMarshaling struct {
|
type stTransactionMarshaling struct {
|
||||||
@ -122,6 +124,7 @@ type stTransactionMarshaling struct {
|
|||||||
Nonce math.HexOrDecimal64
|
Nonce math.HexOrDecimal64
|
||||||
GasLimit []math.HexOrDecimal64
|
GasLimit []math.HexOrDecimal64
|
||||||
PrivateKey hexutil.Bytes
|
PrivateKey hexutil.Bytes
|
||||||
|
BlobGasFeeCap *math.HexOrDecimal256
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChainConfig takes a fork definition and returns a chain config.
|
// GetChainConfig takes a fork definition and returns a chain config.
|
||||||
@ -413,6 +416,8 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess
|
|||||||
GasTipCap: tx.MaxPriorityFeePerGas,
|
GasTipCap: tx.MaxPriorityFeePerGas,
|
||||||
Data: data,
|
Data: data,
|
||||||
AccessList: accessList,
|
AccessList: accessList,
|
||||||
|
BlobHashes: tx.BlobVersionedHashes,
|
||||||
|
BlobGasFeeCap: tx.BlobGasFeeCap,
|
||||||
}
|
}
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit bac70c50a579197af68af5fc6d8c7b6163b92c52
|
Subproject commit ee3fa4c86d05f99f2717f83a6ad08008490ddf07
|
Loading…
Reference in New Issue
Block a user