Skip to content

Commit

Permalink
[evm] EIP-1153 enable transient storage feature (#4214)
Browse files Browse the repository at this point in the history
  • Loading branch information
millken authored Apr 12, 2024
1 parent 9496ed5 commit afbbbd2
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 59 deletions.
6 changes: 6 additions & 0 deletions action/protocol/execution/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,12 @@ func getChainConfig(g genesis.Blockchain, height uint64, id uint32, getBlockTime
}
sumatraTimestamp := (uint64)(sumatraTime.Unix())
chainConfig.ShanghaiTime = &sumatraTimestamp
upernavikTime, err := getBlockTime(g.UpernavikBlockHeight)
if err != nil {
return nil, err
}
upernavikTimestamp := (uint64)(upernavikTime.Unix())
chainConfig.CancunTime = &upernavikTimestamp
return &chainConfig, nil
}

Expand Down
17 changes: 14 additions & 3 deletions action/protocol/execution/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,20 @@ func TestConstantinople(t *testing.T) {
"io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y",
29275560,
},
// after Tsunami
// after Tsunami - Upernavik
{
action.EmptyAddress,
29275561,
},
{
"io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y",
39275560,
},
// after Upernavik
{
action.EmptyAddress,
39275561,
},
{
"io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y",
1261440000, // = 200*365*24*3600/5, around 200 years later
Expand Down Expand Up @@ -355,8 +364,10 @@ func TestConstantinople(t *testing.T) {
require.Equal(isSumatra, chainRules.IsMerge)
require.Equal(isSumatra, chainRules.IsShanghai)

// Cancun, Prague not yet enabled
require.False(evmChainConfig.IsCancun(big.NewInt(int64(e.height)), evm.Context.Time))
// Upernavik = enable Cancun
isUpernavik := g.IsUpernavik(e.height)
require.Equal(isUpernavik, chainRules.IsCancun)
//Prague not yet enabled
require.False(evmChainConfig.IsPrague(big.NewInt(int64(e.height)), evm.Context.Time))

// test basefee
Expand Down
100 changes: 62 additions & 38 deletions action/protocol/execution/evm/evmstatedbadapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,27 @@ type (

// StateDBAdapter represents the state db adapter for evm to access iotx blockchain
StateDBAdapter struct {
sm protocol.StateManager
logs []*action.Log
transactionLogs []*action.TransactionLog
err error
blockHeight uint64
executionHash hash.Hash256
lastAddBalanceAddr string
lastAddBalanceAmount *big.Int
refund uint64
refundSnapshot map[int]uint64
cachedContract contractMap
contractSnapshot map[int]contractMap // snapshots of contracts
selfDestructed deleteAccount // account/contract calling SelfDestruct
selfDestructedSnapshot map[int]deleteAccount // snapshots of SelfDestruct accounts
preimages preimageMap
preimageSnapshot map[int]preimageMap
accessList *accessList // per-transaction access list
accessListSnapshot map[int]*accessList
sm protocol.StateManager
logs []*action.Log
transactionLogs []*action.TransactionLog
err error
blockHeight uint64
executionHash hash.Hash256
lastAddBalanceAddr string
lastAddBalanceAmount *big.Int
refund uint64
refundSnapshot map[int]uint64
cachedContract contractMap
contractSnapshot map[int]contractMap // snapshots of contracts
selfDestructed deleteAccount // account/contract calling SelfDestruct
selfDestructedSnapshot map[int]deleteAccount // snapshots of SelfDestruct accounts
preimages preimageMap
preimageSnapshot map[int]preimageMap
accessList *accessList // per-transaction access list
accessListSnapshot map[int]*accessList
// Transient storage
transientStorage transientStorage
transientStorageSnapshot map[int]transientStorage
logsSnapshot map[int]int // logs is an array, save len(logs) at time of snapshot suffices
txLogsSnapshot map[int]int
notFixTopicCopyBug bool
Expand Down Expand Up @@ -179,23 +182,25 @@ func NewStateDBAdapter(
opts ...StateDBAdapterOption,
) (*StateDBAdapter, error) {
s := &StateDBAdapter{
sm: sm,
logs: []*action.Log{},
err: nil,
blockHeight: blockHeight,
executionHash: executionHash,
lastAddBalanceAmount: new(big.Int),
refundSnapshot: make(map[int]uint64),
cachedContract: make(contractMap),
contractSnapshot: make(map[int]contractMap),
selfDestructed: make(deleteAccount),
selfDestructedSnapshot: make(map[int]deleteAccount),
preimages: make(preimageMap),
preimageSnapshot: make(map[int]preimageMap),
accessList: newAccessList(),
accessListSnapshot: make(map[int]*accessList),
logsSnapshot: make(map[int]int),
txLogsSnapshot: make(map[int]int),
sm: sm,
logs: []*action.Log{},
err: nil,
blockHeight: blockHeight,
executionHash: executionHash,
lastAddBalanceAmount: new(big.Int),
refundSnapshot: make(map[int]uint64),
cachedContract: make(contractMap),
contractSnapshot: make(map[int]contractMap),
selfDestructed: make(deleteAccount),
selfDestructedSnapshot: make(map[int]deleteAccount),
preimages: make(preimageMap),
preimageSnapshot: make(map[int]preimageMap),
accessList: newAccessList(),
accessListSnapshot: make(map[int]*accessList),
transientStorage: newTransientStorage(),
transientStorageSnapshot: make(map[int]transientStorage),
logsSnapshot: make(map[int]int),
txLogsSnapshot: make(map[int]int),
}
for _, opt := range opts {
if err := opt(s); err != nil {
Expand Down Expand Up @@ -483,13 +488,16 @@ func (stateDB *StateDBAdapter) HasSelfDestructed(evmAddr common.Address) bool {

// SetTransientState sets transient storage for a given account
func (stateDB *StateDBAdapter) SetTransientState(addr common.Address, key, value common.Hash) {
log.S().Panic("SetTransientState not implemented")
prev := stateDB.transientStorage.Get(addr, key)
if prev == value {
return
}
stateDB.transientStorage.Set(addr, key, value)
}

// GetTransientState gets transient storage for a given account.
func (stateDB *StateDBAdapter) GetTransientState(addr common.Address, key common.Hash) common.Hash {
log.S().Panic("GetTransientState not implemented")
return common.Hash{}
return stateDB.transientStorage.Get(addr, key)
}

// Selfdestruct6780 implements EIP-6780
Expand Down Expand Up @@ -631,6 +639,18 @@ func (stateDB *StateDBAdapter) RevertToSnapshot(snapshot int) {
}
}
}
//restore transientStorage
stateDB.transientStorage = stateDB.transientStorageSnapshot[snapshot]
{
delete(stateDB.transientStorageSnapshot, snapshot)
for i := snapshot + 1; ; i++ {
if _, ok := stateDB.transientStorageSnapshot[i]; ok {
delete(stateDB.transientStorageSnapshot, i)
} else {
break
}
}
}
// restore logs and txLogs
if stateDB.revertLog {
stateDB.logs = stateDB.logs[:stateDB.logsSnapshot[snapshot]]
Expand Down Expand Up @@ -758,6 +778,8 @@ func (stateDB *StateDBAdapter) Snapshot() int {
stateDB.preimageSnapshot[sn] = p
// save a copy of access list
stateDB.accessListSnapshot[sn] = stateDB.accessList.Copy()
// save a copy of transient storage
stateDB.transientStorageSnapshot[sn] = stateDB.transientStorage.Copy()
return sn
}

Expand Down Expand Up @@ -1084,6 +1106,8 @@ func (stateDB *StateDBAdapter) clear() {
stateDB.preimageSnapshot = make(map[int]preimageMap)
stateDB.accessList = newAccessList()
stateDB.accessListSnapshot = make(map[int]*accessList)
stateDB.transientStorage = newTransientStorage()
stateDB.transientStorageSnapshot = make(map[int]transientStorage)
stateDB.logsSnapshot = make(map[int]int)
stateDB.txLogsSnapshot = make(map[int]int)
stateDB.logs = []*action.Log{}
Expand Down
94 changes: 86 additions & 8 deletions action/protocol/execution/evm/evmstatedbadapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,10 +342,13 @@ var tests = []stateDBTest{
[]access{
{_c1, []common.Hash{_k1, _k2}, []common.Hash{_k3, _k4}, false},
},
[]transient{
{_c1, _k1, _v1},
},
[]*types.Log{
newTestLog(_c3), newTestLog(_c2), newTestLog(_c1),
},
3, 0,
3, 0, 1,
"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r", "io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r", "",
},
{
Expand Down Expand Up @@ -373,10 +376,13 @@ var tests = []stateDBTest{
{_c1, []common.Hash{_k3, _k4}, nil, true},
{_c2, []common.Hash{_k1, _k3}, []common.Hash{_k2, _k4}, false},
},
[]transient{
{_c2, _k2, _v2},
},
[]*types.Log{
newTestLog(_c4),
},
4, 1,
4, 1, 2,
"io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q",
"io1j4kjr6x5s8p6dyqlcfrxxdrsea32u2hpvpl5us",
"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe",
Expand All @@ -398,10 +404,13 @@ var tests = []stateDBTest{
[]access{
{_c2, []common.Hash{_k2, _k4}, nil, true},
},
[]transient{
{_c3, _k3, _v3},
},
[]*types.Log{
newTestLog(_c1), newTestLog(_c2),
},
6, 2,
6, 2, 3,
"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe",
"io1q2hz49tdy85dfqwr560pge3ngux0vf0vmhanad",
"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
Expand Down Expand Up @@ -481,13 +490,17 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
require.False(sOk)
}
}
//set transient storage
for _, e := range test.transient {
stateDB.SetTransientState(e.addr, e.k, e.v)
}
// set logs and txLogs
for _, l := range test.logs {
stateDB.AddLog(l)
}

require.Equal(test.logSize, len(stateDB.logs))
require.Equal(test.txLogSize, len(stateDB.transactionLogs))
require.Equal(test.transientSize, len(stateDB.transientStorage))
require.Equal(test.logAddr, stateDB.logs[test.logSize-1].Address)
if test.txLogSize > 0 {
require.Equal(test.txSender, stateDB.transactionLogs[test.txLogSize-1].Sender)
Expand Down Expand Up @@ -526,8 +539,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
{_c1, []common.Hash{_k1, _k2, _k3, _k4}, nil, true},
{_c2, []common.Hash{_k1, _k2, _k3, _k4}, nil, true},
},
[]transient{
{_c3, _k3, _v3},
},
nil,
6, 2,
6, 2, 3,
"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe",
"io1q2hz49tdy85dfqwr560pge3ngux0vf0vmhanad",
"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
Expand Down Expand Up @@ -556,8 +572,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
{_c1, []common.Hash{_k1, _k2, _k3, _k4}, nil, true},
{_c2, []common.Hash{_k1, _k3}, []common.Hash{_k2, _k4}, true},
},
[]transient{
{_c2, _k2, _v2},
},
nil,
4, 1,
4, 1, 2,
"io1zg0qrlpyvc68pnmz4c4f2mfc6jqu8f57jjy09q",
"io1j4kjr6x5s8p6dyqlcfrxxdrsea32u2hpvpl5us",
"io1x3cv7c4w922k6wx5s8p6d8sjrcqlcfrxhkn5xe",
Expand Down Expand Up @@ -590,8 +609,11 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
{_c1, []common.Hash{_k1, _k2}, []common.Hash{_k3, _k4}, true},
{_c2, nil, []common.Hash{_k1, _k2, _k3, _k4}, false},
},
[]transient{
{_c1, _k1, _v1},
},
nil,
3, 0,
3, 0, 1,
"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
"io1q87zge3ngux0v2hz49tdy85dfqwr560pj9mk7r",
"",
Expand Down Expand Up @@ -638,6 +660,10 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
require.False(sOk)
}
}
//test transient storage
for _, e := range test.transient {
require.Equal(e.v, stateDB.GetTransientState(e.addr, e.k))
}
}
// test SelfDestruct/exist
for _, e := range test.selfDestruct {
Expand Down Expand Up @@ -669,14 +695,16 @@ func TestSnapshotRevertAndCommit(t *testing.T) {
require.Equal(1, len(stateDB.selfDestructedSnapshot))
require.Equal(1, len(stateDB.preimageSnapshot))
require.Equal(1, len(stateDB.accessListSnapshot))
require.Equal(1, len(stateDB.transientStorageSnapshot))
require.Equal(1, len(stateDB.refundSnapshot))
} else {
require.Equal(3, len(stateDB.contractSnapshot))
require.Equal(3, len(stateDB.selfDestructedSnapshot))
require.Equal(3, len(stateDB.preimageSnapshot))
// refund fix and accessList are introduced after fixSnapshot
// refund fix, accessList, and transient storage are introduced after fixSnapshot
// so their snapshot are always properly cleared
require.Zero(len(stateDB.accessListSnapshot))
require.Zero(len(stateDB.transientStorageSnapshot))
require.Zero(len(stateDB.refundSnapshot))
}
// commit snapshot 0's state
Expand Down Expand Up @@ -944,3 +972,53 @@ func TestSortMap(t *testing.T) {
require.True(testFunc(t, sm))
})
}

func TestStateDBTransientStorage(t *testing.T) {
require := require.New(t)
ctrl := gomock.NewController(t)
sm, err := initMockStateManager(ctrl)
require.NoError(err)
var opts []StateDBAdapterOption
opts = append(opts,
NotFixTopicCopyBugOption(),
FixSnapshotOrderOption(),
)
state, err := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opts...)
if err != nil {
t.Fatal(err)
}
var (
addr0 = common.Address{}
addr1 = common.HexToAddress("1234567890")
k1 = common.Hash{}
k2 = common.HexToHash("34567890ab")
v1 = common.HexToHash("567890abcd")
v2 = common.HexToHash("7890abcdef")
)
tests := []struct {
addr common.Address
key, val common.Hash
}{
{addr0, k1, v1},
{addr0, k2, v2},
{addr1, k1, v2},
{addr1, k2, v1},
}
for _, test := range tests {
addr := test.addr
key := test.key
value := test.val
sn := state.Snapshot()
state.SetTransientState(addr, key, value)
require.Equal(value, state.GetTransientState(addr, key))

// revert the transient state being set and then check that the
// value is now the empty hash
state.RevertToSnapshot(sn)
require.Equal(common.Hash{}, state.GetTransientState(addr, key))

state.SetTransientState(addr, key, value)
require.Equal(value, state.GetTransientState(addr, key))
}

}
Loading

0 comments on commit afbbbd2

Please sign in to comment.