mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 11:41:19 +00:00
Fix for Istanbul sync (EIP-2200) (#271)
* Started adding tx tracing, try to fix GetCommittedState * Fix lint * Fix lint * Fix lint * Fix the collision tests * Switch to boltDB with Yield Yield boltdb transaction regularly to not block memory map resizing * Added test * gofmt -w * Fix lint * Revert "Switch to boltDB with Yield" This reverts commit b42650d6e477ecd0f19fd017a29744384c249cac. * Fix lint
This commit is contained in:
parent
8ac2bef75b
commit
6d98798700
@ -484,3 +484,93 @@ func TestReproduceCrash(t *testing.T) {
|
||||
t.Errorf("Expected empty list of prunables, got:\n %s", prunables)
|
||||
}
|
||||
}
|
||||
func TestEip2200Gas(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
db = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &core.Genesis{
|
||||
Config: ¶ms.ChainConfig{
|
||||
ChainID: big.NewInt(1),
|
||||
HomesteadBlock: new(big.Int),
|
||||
EIP150Block: new(big.Int),
|
||||
EIP155Block: new(big.Int),
|
||||
EIP158Block: big.NewInt(1),
|
||||
ByzantiumBlock: big.NewInt(1),
|
||||
PetersburgBlock: big.NewInt(1),
|
||||
ConstantinopleBlock: big.NewInt(1),
|
||||
IstanbulBlock: big.NewInt(1),
|
||||
},
|
||||
Alloc: core.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
)
|
||||
|
||||
engine := ethash.NewFaker()
|
||||
blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
blockchain.EnableReceipts(true)
|
||||
|
||||
contractBackend := backends.NewSimulatedBackendWithConfig(gspec.Alloc, gspec.Config, gspec.GasLimit)
|
||||
transactOpts := bind.NewKeyedTransactor(key)
|
||||
transactOpts.GasLimit = 1000000
|
||||
|
||||
var contractAddress common.Address
|
||||
var selfDestruct *contracts.Selfdestruct
|
||||
|
||||
ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
|
||||
// Here we generate 1 block with 2 transactions, first creates a contract with some initial values in the
|
||||
// It activates the SSTORE pricing rules specific to EIP-2200 (istanbul)
|
||||
blocks, _ := core.GenerateChain(ctx, gspec.Config, genesis, engine, db.MemCopy(), 3, func(i int, block *core.BlockGen) {
|
||||
var tx *types.Transaction
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
contractAddress, tx, selfDestruct, err = contracts.DeploySelfdestruct(transactOpts, contractBackend)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
transactOpts.GasPrice = big.NewInt(1)
|
||||
tx, err = selfDestruct.Change(transactOpts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
}
|
||||
contractBackend.Commit()
|
||||
})
|
||||
|
||||
st, _, _ := blockchain.State()
|
||||
if !st.Exist(address) {
|
||||
t.Error("expected account to exist")
|
||||
}
|
||||
if st.Exist(contractAddress) {
|
||||
t.Error("expected contractAddress to not exist before block 0", contractAddress.String())
|
||||
}
|
||||
balanceBefore := st.GetBalance(address)
|
||||
|
||||
// BLOCK 1
|
||||
if _, err = blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
st, _, _ = blockchain.State()
|
||||
if !st.Exist(contractAddress) {
|
||||
t.Error("expected contractAddress to exist at the block 1", contractAddress.String())
|
||||
}
|
||||
balanceAfter := st.GetBalance(address)
|
||||
gasSpent := big.NewInt(0).Sub(balanceBefore, balanceAfter)
|
||||
expectedGasSpent := big.NewInt(192245) // In the incorrect version, it is 179645
|
||||
if gasSpent.Cmp(expectedGasSpent) != 0 {
|
||||
t.Errorf("Expected gas spent: %d, got %d", expectedGasSpent, gasSpent)
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,6 @@ type stateObject struct {
|
||||
code Code // contract bytecode, which gets set when code is loaded
|
||||
|
||||
originStorage Storage // Storage cache of original entries to dedup rewrites
|
||||
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
|
||||
blockOriginStorage Storage
|
||||
dirtyStorage Storage // Storage entries that need to be flushed to disk
|
||||
fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
|
||||
@ -109,7 +108,6 @@ func newObject(db *IntraBlockState, address common.Address, data, original *acco
|
||||
db: db,
|
||||
address: address,
|
||||
originStorage: make(Storage),
|
||||
pendingStorage: make(Storage),
|
||||
blockOriginStorage: make(Storage),
|
||||
dirtyStorage: make(Storage),
|
||||
}
|
||||
@ -168,9 +166,6 @@ func (so *stateObject) GetState(key common.Hash) common.Hash {
|
||||
|
||||
// GetCommittedState retrieves a value from the committed account storage trie.
|
||||
func (so *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
||||
if so.created {
|
||||
return common.Hash{}
|
||||
}
|
||||
// If we have the original value cached, return that
|
||||
{
|
||||
value, cached := so.originStorage[key]
|
||||
@ -178,6 +173,9 @@ func (so *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
||||
return value
|
||||
}
|
||||
}
|
||||
if so.created {
|
||||
return common.Hash{}
|
||||
}
|
||||
// Load from DB in case it is missing.
|
||||
enc, err := so.db.stateReader.ReadAccountStorage(so.address, so.data.GetIncarnation(), &key)
|
||||
if err != nil {
|
||||
@ -231,17 +229,6 @@ func (so *stateObject) setState(key, value common.Hash) {
|
||||
so.dirtyStorage[key] = value
|
||||
}
|
||||
|
||||
// finalise moves all dirty storage slots into the pending area to be hashed or
|
||||
// committed later. It is invoked at the end of every transaction.
|
||||
func (so *stateObject) finalise() {
|
||||
for key, value := range so.dirtyStorage {
|
||||
so.pendingStorage[key] = value
|
||||
}
|
||||
if len(so.dirtyStorage) > 0 {
|
||||
so.dirtyStorage = make(Storage)
|
||||
}
|
||||
}
|
||||
|
||||
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||
func (so *stateObject) updateTrie(ctx context.Context, stateWriter StateWriter) error {
|
||||
for key, value := range so.dirtyStorage {
|
||||
|
@ -17,9 +17,9 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
//"os"
|
||||
//"encoding/json"
|
||||
//"bytes"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"fmt"
|
||||
|
||||
@ -41,9 +41,10 @@ import (
|
||||
//
|
||||
// StateProcessor implements Processor.
|
||||
type StateProcessor struct {
|
||||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
engine consensus.Engine // Consensus engine used for block rewards
|
||||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
engine consensus.Engine // Consensus engine used for block rewards
|
||||
txTraceHash []byte // Hash of the transaction to trace (or nil if there nothing to trace)
|
||||
}
|
||||
|
||||
// NewStateProcessor initialises a new StateProcessor.
|
||||
@ -55,6 +56,11 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
|
||||
}
|
||||
}
|
||||
|
||||
// SetTxTraceHash allows setting the hash of the transaction to trace
|
||||
func (p *StateProcessor) SetTxTraceHash(txTraceHash common.Hash) {
|
||||
p.txTraceHash = txTraceHash[:]
|
||||
}
|
||||
|
||||
// StructLogRes stores a structured log emitted by the EVM while replaying a
|
||||
// transaction in debug mode
|
||||
type StructLogRes struct {
|
||||
@ -69,7 +75,7 @@ type StructLogRes struct {
|
||||
Storage *map[string]string `json:"storage,omitempty"`
|
||||
}
|
||||
|
||||
// formatLogs formats EVM returned structured logs for json output
|
||||
// FormatLogs formats EVM returned structured logs for json output
|
||||
func FormatLogs(logs []vm.StructLog) []StructLogRes {
|
||||
formatted := make([]StructLogRes, len(logs))
|
||||
for index, trace := range logs {
|
||||
@ -128,8 +134,39 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.IntraBlockSt
|
||||
// Iterate over and process the individual transactions
|
||||
tds.StartNewBuffer()
|
||||
for i, tx := range block.Transactions() {
|
||||
statedb.Prepare(tx.Hash(), block.Hash(), i)
|
||||
txHash := tx.Hash()
|
||||
statedb.Prepare(txHash, block.Hash(), i)
|
||||
writeTrace := false
|
||||
if !cfg.Debug && p.txTraceHash != nil && bytes.Equal(p.txTraceHash, txHash[:]) {
|
||||
// This code is useful when debugging a certain transaction. If uncommented, together with the code
|
||||
// at the end of this function, after the execution of transaction with given hash, the file
|
||||
// structlogs.txt will contain full trace of the transactin in JSON format. This can be compared
|
||||
// to another trace, obtained from the correct version of the turbo-geth or go-ethereum
|
||||
cfg.Tracer = vm.NewStructLogger(&vm.LogConfig{})
|
||||
cfg.Debug = true
|
||||
writeTrace = true
|
||||
}
|
||||
receipt, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, tds.TrieStateWriter(), header, tx, usedGas, cfg)
|
||||
// This code is useful when debugging a certain transaction. If uncommented, together with the code
|
||||
// at the end of this function, after the execution of transaction with given hash, the file
|
||||
// structlogs.txt will contain full trace of the transactin in JSON format. This can be compared
|
||||
// to another trace, obtained from the correct version of the turbo-geth or go-ethereum
|
||||
if writeTrace {
|
||||
w, err1 := os.Create(fmt.Sprintf("txtrace_%x.txt", p.txTraceHash))
|
||||
if err1 != nil {
|
||||
panic(err1)
|
||||
}
|
||||
encoder := json.NewEncoder(w)
|
||||
logs := FormatLogs(cfg.Tracer.(*vm.StructLogger).StructLogs())
|
||||
if err2 := encoder.Encode(logs); err2 != nil {
|
||||
panic(err2)
|
||||
}
|
||||
if err2 := w.Close(); err2 != nil {
|
||||
panic(err2)
|
||||
}
|
||||
cfg.Debug = false
|
||||
cfg.Tracer = nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
@ -163,17 +200,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.IntraBlockSt
|
||||
// for the transaction, gas used and an error if the transaction failed,
|
||||
// indicating the block was invalid.
|
||||
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
|
||||
/*
|
||||
// This code is useful when debugging a certain transaction. If uncommented, together with the code
|
||||
// at the end of this function, after the execution of transaction with given hash, the file
|
||||
// structlogs.txt will contain full trace of the transactin in JSON format. This can be compared
|
||||
// to another trace, obtained from the correct version of the turbo-geth or go-ethereum
|
||||
var h common.Hash = tx.Hash()
|
||||
if bytes.Equal(h[:], common.FromHex("0x340acfd967a744646ebdcfa2cab9b457a1d42224598d33051047ededdd24caa1")) {
|
||||
cfg.Tracer = vm.NewStructLogger(&vm.LogConfig{})
|
||||
cfg.Debug = true
|
||||
}
|
||||
*/
|
||||
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -186,28 +212,6 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
||||
// Apply the transaction to the current state (included in the env)
|
||||
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
|
||||
/*
|
||||
// This code is useful when debugging a certain transaction. If uncommented, together with the code
|
||||
// at the end of this function, after the execution of transaction with given hash, the file
|
||||
// structlogs.txt will contain full trace of the transactin in JSON format. This can be compared
|
||||
// to another trace, obtained from the correct version of the turbo-geth or go-ethereum
|
||||
if cfg.Tracer != nil {
|
||||
w, err := os.Create("structlogs.txt")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
encoder := json.NewEncoder(w)
|
||||
logs := FormatLogs(cfg.Tracer.(*vm.StructLogger).StructLogs())
|
||||
if err := encoder.Encode(logs); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cfg.Debug = false
|
||||
cfg.Tracer = nil
|
||||
}
|
||||
*/
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user