Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft] contract historical storage by Erigon #4536

Closed
wants to merge 15 commits into from
Prev Previous commit
Next Next commit
erigon for contract
envestcc committed Jan 13, 2025
commit ab4cf014a11a6a64edc88c4391cce0fd8e98b7b4
11 changes: 11 additions & 0 deletions action/protocol/context.go
Original file line number Diff line number Diff line change
@@ -33,6 +33,8 @@ type (

vmConfigContextKey struct{}

erigonContextKey struct{}

// TipInfo contains the tip block information
TipInfo struct {
Height uint64
@@ -398,3 +400,12 @@ func GetVMConfigCtx(ctx context.Context) (vm.Config, bool) {
cfg, ok := ctx.Value(vmConfigContextKey{}).(vm.Config)
return cfg, ok
}

func WithErigonCtx(ctx context.Context) context.Context {
return context.WithValue(ctx, erigonContextKey{}, struct{}{})
}

func GetErigonCtx(ctx context.Context) (any, bool) {
v := ctx.Value(erigonContextKey{})
return v, v != nil
}
117 changes: 117 additions & 0 deletions action/protocol/execution/evm/contractv2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package evm

import (
"math/big"

"github.com/holiman/uint256"
"github.com/iotexproject/go-pkgs/hash"
libcommon "github.com/ledgerwatch/erigon-lib/common"
erigonstate "github.com/ledgerwatch/erigon/core/state"
"github.com/pkg/errors"

"github.com/iotexproject/iotex-core/v2/action/protocol"
"github.com/iotexproject/iotex-core/v2/db/trie"
"github.com/iotexproject/iotex-core/v2/state"
)

type contractV2 struct {
*state.Account
dirtyCode bool // contract's code has been set
dirtyState bool // contract's account state has changed
code protocol.SerializableBytes // contract byte-code
committed map[hash.Hash256][]byte
sm protocol.StateReader
intra *erigonstate.IntraBlockState
addr hash.Hash160
}

func newContractV2(addr hash.Hash160, account *state.Account, sm protocol.StateReader, intra *erigonstate.IntraBlockState) (Contract, error) {
c := &contractV2{
Account: account,
committed: make(map[hash.Hash256][]byte),
sm: sm,
intra: intra,
addr: addr,
}
return c, nil
}

func (c *contractV2) GetCommittedState(key hash.Hash256) ([]byte, error) {
if v, ok := c.committed[key]; ok {
return v, nil
}
return c.GetState(key)

}
func (c *contractV2) GetState(key hash.Hash256) ([]byte, error) {
k := libcommon.Hash(key)
v := uint256.NewInt(0)
c.intra.GetState(libcommon.Address(c.addr), &k, v)
// Fix(erigon): return err if not exist
if _, ok := c.committed[key]; !ok {
c.committed[key] = v.Bytes()
}
return v.Bytes(), nil
}

func (c *contractV2) SetState(key hash.Hash256, value []byte) error {
if _, ok := c.committed[key]; !ok {
_, _ = c.GetState(key)
}
c.dirtyState = true
k := libcommon.Hash(key)
c.intra.SetState(libcommon.Address(c.addr), &k, *uint256.MustFromBig(big.NewInt(0).SetBytes(value)))
return nil
}
func (c *contractV2) GetCode() ([]byte, error) {
if c.code != nil {
return c.code[:], nil
}
_, err := c.sm.State(&c.code, protocol.NamespaceOption(CodeKVNameSpace), protocol.KeyOption(c.Account.CodeHash))
if err != nil {
return nil, err
}
return c.code[:], nil
}
func (c *contractV2) SetCode(hash hash.Hash256, code []byte) {
c.Account.CodeHash = hash[:]
c.code = code
c.dirtyCode = true
}

func (c *contractV2) SelfState() *state.Account {
return c.Account
}

func (c *contractV2) Commit() error {
if c.dirtyState {
c.dirtyState = false
// purge the committed value cache
c.committed = nil
c.committed = make(map[hash.Hash256][]byte)
}
if c.dirtyCode {
c.dirtyCode = false
}
return nil
}
func (c *contractV2) LoadRoot() error {
return nil
}

// Iterator is only for debug
func (c *contractV2) Iterator() (trie.Iterator, error) {
return nil, errors.New("not supported")
}

func (c *contractV2) Snapshot() Contract {
return &contractV2{
Account: c.Account.Clone(),
dirtyCode: c.dirtyCode,
dirtyState: c.dirtyState,
code: c.code,
committed: c.committed,
sm: c.sm,
intra: c.intra,
}
}
128 changes: 128 additions & 0 deletions action/protocol/execution/evm/erigonadapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package evm

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/holiman/uint256"
erigonchain "github.com/ledgerwatch/erigon-lib/chain"
libcommon "github.com/ledgerwatch/erigon-lib/common"
erigonstate "github.com/ledgerwatch/erigon/core/state"
"github.com/pkg/errors"
"go.uber.org/zap"

"github.com/iotexproject/go-pkgs/hash"

"github.com/iotexproject/iotex-core/v2/action"
"github.com/iotexproject/iotex-core/v2/pkg/log"
"github.com/iotexproject/iotex-core/v2/state"
)

type StateDB interface {
vm.StateDB

CommitContracts() error
Logs() []*action.Log
TransactionLogs() []*action.TransactionLog
clear()
Error() error
}

type ErigonStateDBAdapter struct {
*StateDBAdapter
rw erigonstate.StateWriter
intra *erigonstate.IntraBlockState
chainRules *erigonchain.Rules
snDiff int
}

func NewErigonStateDBAdapter(adapter *StateDBAdapter,
rw erigonstate.StateWriter,
intra *erigonstate.IntraBlockState,
chainRules *erigonchain.Rules,
) *ErigonStateDBAdapter {
adapter.newContract = func(addr hash.Hash160, account *state.Account) (Contract, error) {
return newContractV2(addr, account, adapter.sm, intra)
}
return &ErigonStateDBAdapter{
StateDBAdapter: adapter,
rw: rw,
intra: intra,
chainRules: chainRules,
}
}

func (s *ErigonStateDBAdapter) CreateAccount(evmAddr common.Address) {
s.StateDBAdapter.CreateAccount(evmAddr)
s.intra.CreateAccount(libcommon.Address(evmAddr), true)
}

func (s *ErigonStateDBAdapter) SubBalance(evmAddr common.Address, v *uint256.Int) {
s.StateDBAdapter.SubBalance(evmAddr, v)
s.intra.SubBalance(libcommon.Address(evmAddr), v)
}

func (s *ErigonStateDBAdapter) AddBalance(evmAddr common.Address, v *uint256.Int) {
s.StateDBAdapter.AddBalance(evmAddr, v)
s.intra.AddBalance(libcommon.Address(evmAddr), v)
}

func (s *ErigonStateDBAdapter) SetNonce(evmAddr common.Address, n uint64) {
s.StateDBAdapter.SetNonce(evmAddr, n)
s.intra.SetNonce(libcommon.Address(evmAddr), n)
}

func (s *ErigonStateDBAdapter) SetCode(evmAddr common.Address, c []byte) {
s.StateDBAdapter.SetCode(evmAddr, c)
s.intra.SetCode(libcommon.Address(evmAddr), c)
}

func (s *ErigonStateDBAdapter) AddRefund(r uint64) {
s.StateDBAdapter.AddRefund(r)
s.intra.AddRefund(r)
}
func (s *ErigonStateDBAdapter) SubRefund(r uint64) {
s.StateDBAdapter.SubRefund(r)
s.intra.SubRefund(r)
}
func (s *ErigonStateDBAdapter) SetState(evmAddr common.Address, k common.Hash, v common.Hash) {
s.StateDBAdapter.SetState(evmAddr, k, v)
key := libcommon.Hash(k)
s.intra.SetState(libcommon.Address(evmAddr), &key, *uint256.MustFromBig(big.NewInt(0).SetBytes(v[:])))
}

func (s *ErigonStateDBAdapter) SelfDestruct(evmAddr common.Address) {
s.StateDBAdapter.SelfDestruct(evmAddr)
s.intra.Selfdestruct(libcommon.Address(evmAddr))
}

func (s *ErigonStateDBAdapter) Selfdestruct6780(evmAddr common.Address) {
s.StateDBAdapter.Selfdestruct6780(evmAddr)
s.intra.Selfdestruct6780(libcommon.Address(evmAddr))
}

func (s *ErigonStateDBAdapter) CommitContracts() error {
log.L().Debug("intraBlockState Committing contracts", zap.Uint64("height", s.StateDBAdapter.blockHeight))
err := s.intra.FinalizeTx(s.chainRules, s.rw)
if err != nil {
return errors.Wrap(err, "failed to finalize tx")
}
return nil
}

func (s *ErigonStateDBAdapter) RevertToSnapshot(sn int) {
s.StateDBAdapter.RevertToSnapshot(sn)
s.intra.RevertToSnapshot(sn + s.snDiff)
}

func (s *ErigonStateDBAdapter) Snapshot() int {
sn := s.StateDBAdapter.Snapshot()
isn := s.intra.Snapshot()
diff := isn - sn
if s.snDiff != 0 && diff != s.snDiff {
log.L().Panic("snapshot diff changed", zap.Int("old", s.snDiff), zap.Int("new", diff))
}
s.snDiff = diff
return sn
}
Loading