From 4ab40f0538523709d15b1bdfe292c49553bcb0ed Mon Sep 17 00:00:00 2001 From: millken Date: Thu, 28 Mar 2024 16:43:35 +0800 Subject: [PATCH 01/13] [evm] enable transient storage feature --- action/protocol/execution/evm/evm.go | 7 ++ .../execution/evm/evmstatedbadapter.go | 114 ++++++++++++------ .../execution/evm/evmstatedbadapter_test.go | 42 +++++++ action/protocol/execution/protocol_test.go | 10 ++ 4 files changed, 135 insertions(+), 38 deletions(-) diff --git a/action/protocol/execution/evm/evm.go b/action/protocol/execution/evm/evm.go index fca42d0254..bc28b85d18 100644 --- a/action/protocol/execution/evm/evm.go +++ b/action/protocol/execution/evm/evm.go @@ -447,6 +447,13 @@ func getChainConfig(g genesis.Blockchain, height uint64, id uint32, getBlockTime } sumatraTimestamp := (uint64)(sumatraTime.Unix()) chainConfig.ShanghaiTime = &sumatraTimestamp + //TODO: enable Cancun at TBA + tobeEnableTime, err := getBlockTime(g.ToBeEnabledBlockHeight) + if err != nil { + return nil, err + } + tobeEnableTimestamp := (uint64)(tobeEnableTime.Unix()) + chainConfig.CancunTime = &tobeEnableTimestamp return &chainConfig, nil } diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go index c7a7f964c4..33e8e44dd6 100644 --- a/action/protocol/execution/evm/evmstatedbadapter.go +++ b/action/protocol/execution/evm/evmstatedbadapter.go @@ -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 - suicided deleteAccount // account/contract calling Suicide - suicideSnapshot map[int]deleteAccount // snapshots of suicide 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 + suicided deleteAccount // account/contract calling Suicide + suicideSnapshot map[int]deleteAccount // snapshots of suicide 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 @@ -170,23 +173,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), - suicided: make(deleteAccount), - suicideSnapshot: 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), + suicided: make(deleteAccount), + suicideSnapshot: 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 { @@ -478,13 +483,22 @@ 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.GetTransientState(addr, key) + if prev == value { + return + } + stateDB.setTransientState(addr, key, value) +} + +// setTransientState is a lower level setter for transient storage. It +// is called during a revert to prevent modifications to the journal. +func (stateDB *StateDBAdapter) setTransientState(addr common.Address, key, value common.Hash) { + 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 @@ -530,6 +544,8 @@ func (stateDB *StateDBAdapter) Prepare(rules params.Rules, sender, coinbase comm if !rules.IsBerlin { return } + // Clear out any leftover from previous executions + stateDB.accessList = newAccessList() stateDB.AddAddressToAccessList(sender) if dst != nil { stateDB.AddAddressToAccessList(*dst) @@ -547,6 +563,8 @@ func (stateDB *StateDBAdapter) Prepare(rules params.Rules, sender, coinbase comm if rules.IsShanghai { // EIP-3651: warm coinbase stateDB.AddAddressToAccessList(coinbase) } + // Reset transient storage at the beginning of transaction execution + stateDB.transientStorage = newTransientStorage() } // AddressInAccessList returns true if the given address is in the access list @@ -592,6 +610,23 @@ func (stateDB *StateDBAdapter) RevertToSnapshot(snapshot int) { stateDB.logError(err) return } + // restore transientStorage + tss, ok := stateDB.transientStorageSnapshot[snapshot] + if !ok { + log.L().Error("unexpected error: missing transient storage snapshot") + return + } + stateDB.transientStorage = tss + { + delete(stateDB.transientStorageSnapshot, snapshot) + for i := snapshot + 1; ; i++ { + if _, ok := stateDB.transientStorageSnapshot[i]; ok { + delete(stateDB.transientStorageSnapshot, i) + } else { + break + } + } + } ds, ok := stateDB.suicideSnapshot[snapshot] if !ok { // this should not happen, b/c we save the suicide accounts on a successful return of Snapshot(), but check anyway @@ -750,6 +785,7 @@ func (stateDB *StateDBAdapter) Snapshot() int { stateDB.preimageSnapshot[sn] = p // save a copy of access list stateDB.accessListSnapshot[sn] = stateDB.accessList.Copy() + stateDB.transientStorageSnapshot[sn] = stateDB.transientStorage.Copy() return sn } @@ -1089,6 +1125,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{} diff --git a/action/protocol/execution/evm/evmstatedbadapter_test.go b/action/protocol/execution/evm/evmstatedbadapter_test.go index 8eb07d8b51..3dcda5af45 100644 --- a/action/protocol/execution/evm/evmstatedbadapter_test.go +++ b/action/protocol/execution/evm/evmstatedbadapter_test.go @@ -934,3 +934,45 @@ 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) + } + key := common.Hash{0x01} + value := common.Hash{0x02} + addr := common.Address{} + + sn := state.Snapshot() + state.SetTransientState(addr, key, value) + if exp, got := 0, sn; exp != got { + t.Fatalf("journal length mismatch: have %d, want %d", got, exp) + } + // the retrieved value should equal what was set + if got := state.GetTransientState(addr, key); got != value { + t.Fatalf("transient storage mismatch: have %x, want %x", got, value) + } + + // revert the transient state being set and then check that the + // value is now the empty hash + state.RevertToSnapshot(sn) + if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got { + t.Fatalf("transient storage mismatch: have %x, want %x", got, exp) + } + + // reset transient state + state.SetTransientState(addr, key, value) + if got := state.GetTransientState(addr, key); got != value { + t.Fatalf("transient storage mismatch: have %x, want %x", got, value) + } +} diff --git a/action/protocol/execution/protocol_test.go b/action/protocol/execution/protocol_test.go index 21edfc01dc..8cf00fd384 100644 --- a/action/protocol/execution/protocol_test.go +++ b/action/protocol/execution/protocol_test.go @@ -71,6 +71,7 @@ type ( IsIceland bool `json:"isIceland"` IsLondon bool `json:"isLondon"` IsShanghai bool `json:"isShanghai"` + IsCancun bool `json:"isCancun"` } Log struct { @@ -437,6 +438,9 @@ func (sct *SmartContractTest) prepareBlockchain( cfg.Genesis.Blockchain.SumatraBlockHeight = 0 cfg.Genesis.ActionGasLimit = 10000000 } + if sct.InitGenesis.IsCancun { + cfg.Genesis.Blockchain.ToBeEnabledBlockHeight = 0 + } for _, expectedBalance := range sct.InitBalances { cfg.Genesis.InitBalanceMap[expectedBalance.Account] = expectedBalance.Balance().String() } @@ -1321,6 +1325,12 @@ func TestShanghaiEVM(t *testing.T) { }) } +func TestCancunEVM(t *testing.T) { + t.Run("eip1153-transientstorage", func(t *testing.T) { + NewSmartContractTest(t, "testdata-cancun/transientstorage.json") + }) +} + func benchmarkHotContractWithFactory(b *testing.B, async bool) { sct := SmartContractTest{ InitBalances: []ExpectedBalance{ From 16f63a0bfa45e995471c6550328487eb81359b26 Mon Sep 17 00:00:00 2001 From: millken Date: Thu, 28 Mar 2024 16:43:41 +0800 Subject: [PATCH 02/13] [evm] enable transient storage feature --- .../testdata-cancun/transientstorage.json | 49 +++++++++++++++++++ .../testdata-cancun/transientstorage.sol | 30 ++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 action/protocol/execution/testdata-cancun/transientstorage.json create mode 100644 action/protocol/execution/testdata-cancun/transientstorage.sol diff --git a/action/protocol/execution/testdata-cancun/transientstorage.json b/action/protocol/execution/testdata-cancun/transientstorage.json new file mode 100644 index 0000000000..ce5942881b --- /dev/null +++ b/action/protocol/execution/testdata-cancun/transientstorage.json @@ -0,0 +1,49 @@ +{ + "initGenesis": { + "isBering" : true, + "isIceland" : true, + "isLondon" : true, + "isShanghai" : true, + "isCancun": true + }, + "initBalances": [{ + "account": "io1mflp9m6hcgm2qcghchsdqj3z3eccrnekx9p0ms", + "rawBalance": "1000000000000000000000000000" + }], + "deployments": [{ + "rawByteCode": "608060405234801561000f575f80fd5b506102898061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c80630198214f1461004e57806338cc48311461006a5780639c6f010c14610088578063f2c9ecd8146100b8575b5f80fd5b6100686004803603810190610063919061016a565b6100d6565b005b6100726100dd565b60405161007f91906101e7565b60405180910390f35b6100a2600480360381019061009d9190610200565b61010f565b6040516100af919061023a565b60405180910390f35b6100c0610119565b6040516100cd919061023a565b60405180910390f35b80825d5050565b5f6101006001600373ffffffffffffffffffffffffffffffffffffffff166100d6565b61010a600161010f565b905090565b5f815c9050919050565b5f6101255f60216100d6565b61012e5f61010f565b905090565b5f80fd5b5f819050919050565b61014981610137565b8114610153575f80fd5b50565b5f8135905061016481610140565b92915050565b5f80604083850312156101805761017f610133565b5b5f61018d85828601610156565b925050602061019e85828601610156565b9150509250929050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6101d1826101a8565b9050919050565b6101e1816101c7565b82525050565b5f6020820190506101fa5f8301846101d8565b92915050565b5f6020828403121561021557610214610133565b5b5f61022284828501610156565b91505092915050565b61023481610137565b82525050565b5f60208201905061024d5f83018461022b565b9291505056fea2646970667358221220482553b58e8f517e223d2d9d4345c179916b57ace9585c2ff1965520d3a2221064736f6c63430008180033", + "rawPrivateKey": "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1", + "rawAmount": "0", + "rawGasLimit": 5000000, + "rawGasPrice": "0", + "rawExpectedGasConsumed": 207775, + "expectedStatus": 1, + "expectedBalances": [], + "comment": "deploy transientstore contract" + }], + "executions": [{ + "rawPrivateKey": "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1", + "rawByteCode": "f2c9ecd8", + "rawAmount": "0", + "rawGasLimit": 1000000, + "rawGasPrice": "0", + "rawAccessList": [], + "rawExpectedGasConsumed": 11056, + "expectedStatus": 1, + "readOnly": true, + "rawReturnValue": "0000000000000000000000000000000000000000000000000000000000000021", + "comment": "call getNumber" + },{ + "rawPrivateKey": "cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1", + "rawByteCode": "38cc4831", + "rawAmount": "0", + "rawGasLimit": 1000000, + "rawGasPrice": "0", + "rawAccessList": [], + "rawExpectedGasConsumed": 11068, + "expectedStatus": 1, + "readOnly": true, + "rawReturnValue": "0000000000000000000000000000000000000000000000000000000000000003", + "comment": "call getAddress" + }] +} \ No newline at end of file diff --git a/action/protocol/execution/testdata-cancun/transientstorage.sol b/action/protocol/execution/testdata-cancun/transientstorage.sol new file mode 100644 index 0000000000..809d774e36 --- /dev/null +++ b/action/protocol/execution/testdata-cancun/transientstorage.sol @@ -0,0 +1,30 @@ +// This support was introduced with Solidity 0.8.24 +pragma solidity 0.8.24; +// SPDX-License-Identifier: Unlicensed + +contract TransientStorage { + + // Sets a number in the transient storage + function getNumber() public returns (uint) { + tstore(0, uint(33)); + return uint(tload(0)); + } + + // Sets an address in the transient storage + function getAddress() public returns (address) { + tstore(1, uint(uint160(address(3)))); + return address(uint160(tload(1))); + } + + function tstore(uint location, uint value) public { + assembly { + tstore(location, value) + } + } + + function tload(uint location) public view returns (uint value) { + assembly { + value := tload(location) + } + } +} \ No newline at end of file From 47bf31c1bd9dc6f8e4da819b622c369377eb0fdb Mon Sep 17 00:00:00 2001 From: millken Date: Fri, 29 Mar 2024 11:07:55 +0800 Subject: [PATCH 03/13] [evm] update cancuntime --- action/protocol/execution/evm/evm.go | 7 +++---- action/protocol/execution/protocol_test.go | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/action/protocol/execution/evm/evm.go b/action/protocol/execution/evm/evm.go index bc28b85d18..5a20d7dc01 100644 --- a/action/protocol/execution/evm/evm.go +++ b/action/protocol/execution/evm/evm.go @@ -447,13 +447,12 @@ func getChainConfig(g genesis.Blockchain, height uint64, id uint32, getBlockTime } sumatraTimestamp := (uint64)(sumatraTime.Unix()) chainConfig.ShanghaiTime = &sumatraTimestamp - //TODO: enable Cancun at TBA - tobeEnableTime, err := getBlockTime(g.ToBeEnabledBlockHeight) + upernavikTime, err := getBlockTime(g.UpernavikBlockHeight) if err != nil { return nil, err } - tobeEnableTimestamp := (uint64)(tobeEnableTime.Unix()) - chainConfig.CancunTime = &tobeEnableTimestamp + upernavikTimestamp := (uint64)(upernavikTime.Unix()) + chainConfig.CancunTime = &upernavikTimestamp return &chainConfig, nil } diff --git a/action/protocol/execution/protocol_test.go b/action/protocol/execution/protocol_test.go index 8cf00fd384..44d425abae 100644 --- a/action/protocol/execution/protocol_test.go +++ b/action/protocol/execution/protocol_test.go @@ -439,7 +439,7 @@ func (sct *SmartContractTest) prepareBlockchain( cfg.Genesis.ActionGasLimit = 10000000 } if sct.InitGenesis.IsCancun { - cfg.Genesis.Blockchain.ToBeEnabledBlockHeight = 0 + cfg.Genesis.Blockchain.UpernavikBlockHeight = 0 } for _, expectedBalance := range sct.InitBalances { cfg.Genesis.InitBalanceMap[expectedBalance.Account] = expectedBalance.Balance().String() From 9bf2c28ab38b67656aaf4b721f154b87846494f5 Mon Sep 17 00:00:00 2001 From: millken Date: Fri, 29 Mar 2024 11:19:26 +0800 Subject: [PATCH 04/13] fix tests --- action/protocol/execution/evm/evm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index 2d5df1c204..a22ad7d9e9 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -259,7 +259,7 @@ func TestConstantinople(t *testing.T) { }, { "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", - 1261440000, // = 200*365*24*3600/5, around 200 years later + 39275560, }, } now := time.Now() From 64d1b5d44d71261d7186832ee8ee11a8a6812120 Mon Sep 17 00:00:00 2001 From: millken Date: Mon, 1 Apr 2024 13:53:22 +0800 Subject: [PATCH 05/13] fix comments issue --- .../execution/evm/evmstatedbadapter.go | 112 ++++++++---------- .../execution/evm/evmstatedbadapter_test.go | 102 +++++++++++----- .../protocol/execution/evm/testdata_test.go | 26 ++-- .../testdata-cancun/transientstorage.sol | 2 +- 4 files changed, 141 insertions(+), 101 deletions(-) diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go index 43957aa190..23b9ee3c05 100644 --- a/action/protocol/execution/evm/evmstatedbadapter.go +++ b/action/protocol/execution/evm/evmstatedbadapter.go @@ -43,25 +43,25 @@ 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 - // Transient storage + 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 @@ -173,25 +173,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), - transientStorage: newTransientStorage(), + 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), + logsSnapshot: make(map[int]int), + txLogsSnapshot: make(map[int]int), } for _, opt := range opts { if err := opt(s); err != nil { @@ -483,16 +483,10 @@ 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) { - prev := stateDB.GetTransientState(addr, key) + prev := stateDB.transientStorage.Get(addr, key) if prev == value { return } - stateDB.setTransientState(addr, key, value) -} - -// setTransientState is a lower level setter for transient storage. It -// is called during a revert to prevent modifications to the journal. -func (stateDB *StateDBAdapter) setTransientState(addr common.Address, key, value common.Hash) { stateDB.transientStorage.Set(addr, key, value) } @@ -610,23 +604,6 @@ func (stateDB *StateDBAdapter) RevertToSnapshot(snapshot int) { stateDB.logError(err) return } - // restore transientStorage - tss, ok := stateDB.transientStorageSnapshot[snapshot] - if !ok { - log.L().Error("unexpected error: missing transient storage snapshot") - return - } - stateDB.transientStorage = tss - { - delete(stateDB.transientStorageSnapshot, snapshot) - for i := snapshot + 1; ; i++ { - if _, ok := stateDB.transientStorageSnapshot[i]; ok { - delete(stateDB.transientStorageSnapshot, i) - } else { - break - } - } - } ds, ok := stateDB.selfDestructedSnapshot[snapshot] if !ok { // this should not happen, b/c we save the SelfDestruct accounts on a successful return of Snapshot(), but check anyway @@ -658,6 +635,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]] @@ -785,6 +774,7 @@ 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 } diff --git a/action/protocol/execution/evm/evmstatedbadapter_test.go b/action/protocol/execution/evm/evmstatedbadapter_test.go index 60c5f94502..1eb3422e5a 100644 --- a/action/protocol/execution/evm/evmstatedbadapter_test.go +++ b/action/protocol/execution/evm/evmstatedbadapter_test.go @@ -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", "", }, { @@ -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", @@ -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", @@ -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) @@ -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", @@ -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", @@ -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", "", @@ -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 { @@ -669,6 +695,7 @@ 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)) @@ -677,6 +704,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) { // refund fix and accessList 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 @@ -949,30 +977,46 @@ func TestStateDBTransientStorage(t *testing.T) { if err != nil { t.Fatal(err) } - key := common.Hash{0x01} - value := common.Hash{0x02} - addr := common.Address{} - - sn := state.Snapshot() - state.SetTransientState(addr, key, value) - if exp, got := 0, sn; exp != got { - t.Fatalf("journal length mismatch: have %d, want %d", got, exp) - } - // the retrieved value should equal what was set - if got := state.GetTransientState(addr, key); got != value { - t.Fatalf("transient storage mismatch: have %x, want %x", got, value) + 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) + // the retrieved value should equal what was set + if got := state.GetTransientState(addr, key); got != value { + t.Fatalf("transient storage mismatch: have %x, want %x", got, value) + } - // revert the transient state being set and then check that the - // value is now the empty hash - state.RevertToSnapshot(sn) - if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got { - t.Fatalf("transient storage mismatch: have %x, want %x", got, exp) - } + // revert the transient state being set and then check that the + // value is now the empty hash + state.RevertToSnapshot(sn) + if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got { + t.Fatalf("transient storage mismatch: have %x, want %x", got, exp) + } - // reset transient state - state.SetTransientState(addr, key, value) - if got := state.GetTransientState(addr, key); got != value { - t.Fatalf("transient storage mismatch: have %x, want %x", got, value) + // reset transient state + state.SetTransientState(addr, key, value) + if got := state.GetTransientState(addr, key); got != value { + t.Fatalf("transient storage mismatch: have %x, want %x", got, value) + } } + } diff --git a/action/protocol/execution/evm/testdata_test.go b/action/protocol/execution/evm/testdata_test.go index 8d3830a70e..88c982a43f 100644 --- a/action/protocol/execution/evm/testdata_test.go +++ b/action/protocol/execution/evm/testdata_test.go @@ -56,17 +56,23 @@ type ( nx []common.Hash exist bool } + transient struct { + addr common.Address + k common.Hash + v common.Hash + } stateDBTest struct { - balance []bal - codes []code - states []evmSet - refund uint64 - selfDestruct []sui - preimage []image - accessList []access - logs []*types.Log - logSize, txLogSize int - logAddr, txSender, txReceiver string + balance []bal + codes []code + states []evmSet + refund uint64 + selfDestruct []sui + preimage []image + accessList []access + transient []transient + logs []*types.Log + logSize, txLogSize, transientSize int + logAddr, txSender, txReceiver string } ) diff --git a/action/protocol/execution/testdata-cancun/transientstorage.sol b/action/protocol/execution/testdata-cancun/transientstorage.sol index 809d774e36..46d648ffa1 100644 --- a/action/protocol/execution/testdata-cancun/transientstorage.sol +++ b/action/protocol/execution/testdata-cancun/transientstorage.sol @@ -27,4 +27,4 @@ contract TransientStorage { value := tload(location) } } -} \ No newline at end of file +} From 4f12e2023964785078bec1ceed57a6cbd00c0bd2 Mon Sep 17 00:00:00 2001 From: millken Date: Mon, 1 Apr 2024 14:12:01 +0800 Subject: [PATCH 06/13] fix comments issue --- action/protocol/execution/evm/evm_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index a22ad7d9e9..0018709114 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -259,7 +259,7 @@ func TestConstantinople(t *testing.T) { }, { "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", - 39275560, + 1261440000, // = 200*365*24*3600/5, around 200 years later }, } now := time.Now() @@ -356,7 +356,8 @@ func TestConstantinople(t *testing.T) { require.Equal(isSumatra, chainRules.IsShanghai) // Cancun, Prague not yet enabled - require.False(evmChainConfig.IsCancun(big.NewInt(int64(e.height)), evm.Context.Time)) + isUpernavik := g.IsUpernavik(e.height) + require.Equal(isUpernavik, chainRules.IsCancun) require.False(evmChainConfig.IsPrague(big.NewInt(int64(e.height)), evm.Context.Time)) // test basefee From 5d00100434bb8f40eff2f6d9f98e733e04c71546 Mon Sep 17 00:00:00 2001 From: millken Date: Wed, 3 Apr 2024 16:15:05 +0800 Subject: [PATCH 07/13] fix comments --- action/protocol/execution/evm/evm_test.go | 3 ++- .../execution/evm/evmstatedbadapter_test.go | 16 ++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index 0018709114..9d68282c92 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -355,9 +355,10 @@ func TestConstantinople(t *testing.T) { require.Equal(isSumatra, chainRules.IsMerge) require.Equal(isSumatra, chainRules.IsShanghai) - // Cancun, Prague not yet enabled + // 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 diff --git a/action/protocol/execution/evm/evmstatedbadapter_test.go b/action/protocol/execution/evm/evmstatedbadapter_test.go index 1eb3422e5a..1f58c8b5e1 100644 --- a/action/protocol/execution/evm/evmstatedbadapter_test.go +++ b/action/protocol/execution/evm/evmstatedbadapter_test.go @@ -701,7 +701,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) { 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 and accessList、transient storage are introduced after fixSnapshot // so their snapshot are always properly cleared require.Zero(len(stateDB.accessListSnapshot)) require.Zero(len(stateDB.transientStorageSnapshot)) @@ -1000,23 +1000,15 @@ func TestStateDBTransientStorage(t *testing.T) { value := test.val sn := state.Snapshot() state.SetTransientState(addr, key, value) - // the retrieved value should equal what was set - if got := state.GetTransientState(addr, key); got != value { - t.Fatalf("transient storage mismatch: have %x, want %x", got, 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) - if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got { - t.Fatalf("transient storage mismatch: have %x, want %x", got, exp) - } + require.Equal(common.Hash{}, state.GetTransientState(addr, key)) - // reset transient state state.SetTransientState(addr, key, value) - if got := state.GetTransientState(addr, key); got != value { - t.Fatalf("transient storage mismatch: have %x, want %x", got, value) - } + require.Equal(value, state.GetTransientState(addr, key)) } } From 214787c9708370344adea6390ecf59ddf34f4209 Mon Sep 17 00:00:00 2001 From: millken Date: Tue, 9 Apr 2024 08:37:35 +0800 Subject: [PATCH 08/13] fix comments --- action/protocol/execution/evm/evmstatedbadapter_test.go | 2 +- action/protocol/execution/testdata-cancun/transientstorage.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/action/protocol/execution/evm/evmstatedbadapter_test.go b/action/protocol/execution/evm/evmstatedbadapter_test.go index 1f58c8b5e1..e0846f9aaf 100644 --- a/action/protocol/execution/evm/evmstatedbadapter_test.go +++ b/action/protocol/execution/evm/evmstatedbadapter_test.go @@ -701,7 +701,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) { require.Equal(3, len(stateDB.contractSnapshot)) require.Equal(3, len(stateDB.selfDestructedSnapshot)) require.Equal(3, len(stateDB.preimageSnapshot)) - // refund fix and accessList、transient storage are introduced after fixSnapshot + // refund fix and accessList,transient storage are introduced after fixSnapshot // so their snapshot are always properly cleared require.Zero(len(stateDB.accessListSnapshot)) require.Zero(len(stateDB.transientStorageSnapshot)) diff --git a/action/protocol/execution/testdata-cancun/transientstorage.json b/action/protocol/execution/testdata-cancun/transientstorage.json index ce5942881b..5cb939d9e4 100644 --- a/action/protocol/execution/testdata-cancun/transientstorage.json +++ b/action/protocol/execution/testdata-cancun/transientstorage.json @@ -46,4 +46,4 @@ "rawReturnValue": "0000000000000000000000000000000000000000000000000000000000000003", "comment": "call getAddress" }] -} \ No newline at end of file +} From 3a19630397b0e200193b476367f5646855ef147f Mon Sep 17 00:00:00 2001 From: millken Date: Tue, 9 Apr 2024 14:26:30 +0800 Subject: [PATCH 09/13] fix comments --- action/protocol/execution/evm/evm_test.go | 10 ++++++++++ action/protocol/execution/evm/evmstatedbadapter.go | 2 -- .../protocol/execution/evm/evmstatedbadapter_test.go | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index 9d68282c92..a421a0682b 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -257,6 +257,16 @@ func TestConstantinople(t *testing.T) { action.EmptyAddress, 29275561, }, + // after Tsunami - Upernavik + { + action.EmptyAddress, + 39275360, + }, + // after Upernavik + { + action.EmptyAddress, + 39275562, + }, { "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", 1261440000, // = 200*365*24*3600/5, around 200 years later diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go index 23b9ee3c05..3c7495648d 100644 --- a/action/protocol/execution/evm/evmstatedbadapter.go +++ b/action/protocol/execution/evm/evmstatedbadapter.go @@ -538,8 +538,6 @@ func (stateDB *StateDBAdapter) Prepare(rules params.Rules, sender, coinbase comm if !rules.IsBerlin { return } - // Clear out any leftover from previous executions - stateDB.accessList = newAccessList() stateDB.AddAddressToAccessList(sender) if dst != nil { stateDB.AddAddressToAccessList(*dst) diff --git a/action/protocol/execution/evm/evmstatedbadapter_test.go b/action/protocol/execution/evm/evmstatedbadapter_test.go index e0846f9aaf..370a2525d9 100644 --- a/action/protocol/execution/evm/evmstatedbadapter_test.go +++ b/action/protocol/execution/evm/evmstatedbadapter_test.go @@ -701,7 +701,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) { require.Equal(3, len(stateDB.contractSnapshot)) require.Equal(3, len(stateDB.selfDestructedSnapshot)) require.Equal(3, len(stateDB.preimageSnapshot)) - // refund fix and accessList,transient storage 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)) From 77d80fddf900e5b64e4c41775bdf6f08ce79f750 Mon Sep 17 00:00:00 2001 From: millken Date: Tue, 9 Apr 2024 14:29:38 +0800 Subject: [PATCH 10/13] fix comments --- action/protocol/execution/evm/evmstatedbadapter_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action/protocol/execution/evm/evmstatedbadapter_test.go b/action/protocol/execution/evm/evmstatedbadapter_test.go index 370a2525d9..46d6c8592c 100644 --- a/action/protocol/execution/evm/evmstatedbadapter_test.go +++ b/action/protocol/execution/evm/evmstatedbadapter_test.go @@ -701,7 +701,7 @@ func TestSnapshotRevertAndCommit(t *testing.T) { require.Equal(3, len(stateDB.contractSnapshot)) require.Equal(3, len(stateDB.selfDestructedSnapshot)) require.Equal(3, len(stateDB.preimageSnapshot)) - // refund fix accessList and transient storage 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)) From 5c25a4877b5adf93c03f7337383e42b002e14a79 Mon Sep 17 00:00:00 2001 From: millken Date: Wed, 10 Apr 2024 11:08:43 +0800 Subject: [PATCH 11/13] fix comments --- action/protocol/execution/evm/evm_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index a421a0682b..8c58c60959 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -252,15 +252,15 @@ func TestConstantinople(t *testing.T) { "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", 29275560, }, - // after Tsunami + // after Tsunami - Upernavik { - action.EmptyAddress, - 29275561, + "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", + 29275562, }, - // after Tsunami - Upernavik + // after Tsunami { action.EmptyAddress, - 39275360, + 29275561, }, // after Upernavik { From 34b94d6155299a1fa4954768800d83545e3b9ed0 Mon Sep 17 00:00:00 2001 From: millken Date: Wed, 10 Apr 2024 11:10:16 +0800 Subject: [PATCH 12/13] fix comments --- action/protocol/execution/evm/evmstatedbadapter.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go index 3c7495648d..abb2bd1c6a 100644 --- a/action/protocol/execution/evm/evmstatedbadapter.go +++ b/action/protocol/execution/evm/evmstatedbadapter.go @@ -555,8 +555,6 @@ func (stateDB *StateDBAdapter) Prepare(rules params.Rules, sender, coinbase comm if rules.IsShanghai { // EIP-3651: warm coinbase stateDB.AddAddressToAccessList(coinbase) } - // Reset transient storage at the beginning of transaction execution - stateDB.transientStorage = newTransientStorage() } // AddressInAccessList returns true if the given address is in the access list From 03ac0dd8d22991a6f5f6696a2229a7105830af56 Mon Sep 17 00:00:00 2001 From: millken Date: Thu, 11 Apr 2024 09:48:21 +0800 Subject: [PATCH 13/13] fix comments --- action/protocol/execution/evm/evm_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index 8c58c60959..f995bfbab4 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -253,19 +253,18 @@ func TestConstantinople(t *testing.T) { 29275560, }, // after Tsunami - Upernavik - { - "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", - 29275562, - }, - // after Tsunami { action.EmptyAddress, 29275561, }, + { + "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y", + 39275560, + }, // after Upernavik { action.EmptyAddress, - 39275562, + 39275561, }, { "io1pcg2ja9krrhujpazswgz77ss46xgt88afqlk6y",