erigon-pulse/core/state/change_set_writer.go
Andrew Ashikhmin aeed1657c7
Issue #340: Re-execute all historical transaction in read-only mode and check ChangeSets (#388)
* Clean up code duplication between IntraBlockState's FinalizeTx & CommitBlock

* checkChangeSets command

* linter

* First attempt at checking account changes

* Reuse runBlock in CheckChangeSets

* linter

* linter

* Optionally include no-changes in the ChangeSets

* linter

* Detect storage changes for account change sets

* Fix post-merge compilation errors

* Use database format compatible with !debug.IsThinHistory()

* PrintChangedAccounts in ChangeSetWriter

* Avoid out-of-bounds access

* Storage changes

* hack FirstContractIncarnation

* Call ChangeSetWriter only once per block
2020-03-11 16:54:09 +01:00

120 lines
3.2 KiB
Go

package state
import (
"bytes"
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/changeset"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
"github.com/ledgerwatch/turbo-geth/core/types/accounts"
"github.com/ledgerwatch/turbo-geth/crypto"
)
// ChangeSetWriter is a mock StateWriter that accumulates changes in-memory into ChangeSets.
type ChangeSetWriter struct {
accountChanges map[common.Address][]byte
storageChanged map[common.Address]bool
storageChanges map[string][]byte
}
func NewChangeSetWriter() *ChangeSetWriter {
return &ChangeSetWriter{
accountChanges: make(map[common.Address][]byte),
storageChanged: make(map[common.Address]bool),
storageChanges: make(map[string][]byte),
}
}
func (w *ChangeSetWriter) GetAccountChanges() *changeset.ChangeSet {
cs := changeset.NewAccountChangeSet()
for key, val := range w.accountChanges {
if err := cs.Add(crypto.Keccak256(key.Bytes()), val); err != nil {
panic(err)
}
}
return cs
}
func (w *ChangeSetWriter) GetStorageChanges() *changeset.ChangeSet {
cs := changeset.NewStorageChangeSet()
for key, val := range w.storageChanges {
if err := cs.Add([]byte(key), val); err != nil {
panic(err)
}
}
return cs
}
func accountData(original *accounts.Account) []byte {
var originalData []byte
if !original.Initialised {
originalData = []byte{}
} else {
originalDataLen := original.EncodingLengthForStorage()
originalData = make([]byte, originalDataLen)
original.EncodeForStorage(originalData)
}
return originalData
}
func (w *ChangeSetWriter) UpdateAccountData(ctx context.Context, address common.Address, original, account *accounts.Account) error {
if !accountsEqual(original, account) || w.storageChanged[address] {
w.accountChanges[address] = accountData(original)
}
return nil
}
func (w *ChangeSetWriter) UpdateAccountCode(addrHash common.Hash, incarnation uint64, codeHash common.Hash, code []byte) error {
return nil
}
func (w *ChangeSetWriter) DeleteAccount(ctx context.Context, address common.Address, original *accounts.Account) error {
w.accountChanges[address] = accountData(original)
return nil
}
func (w *ChangeSetWriter) WriteAccountStorage(ctx context.Context, address common.Address, incarnation uint64, key, original, value *common.Hash) error {
if *original == *value {
return nil
}
secKey, err := common.HashData(key[:])
if err != nil {
return err
}
addrHash, err := common.HashData(address[:])
if err != nil {
return err
}
if incarnation == 0 {
incarnation = FirstContractIncarnation
}
compositeKey := dbutils.GenerateCompositeStorageKey(addrHash, incarnation, secKey)
o := bytes.TrimLeft(original[:], "\x00")
originalValue := make([]byte, len(o))
copy(originalValue, o)
w.storageChanges[string(compositeKey)] = originalValue
w.storageChanged[address] = true
return nil
}
func (w *ChangeSetWriter) CreateContract(address common.Address) error {
return nil
}
func (w *ChangeSetWriter) PrintChangedAccounts() {
fmt.Println("Account Changes")
for k := range w.accountChanges {
fmt.Println(hexutil.Encode(k.Bytes()))
}
fmt.Println("------------------------------------------")
}