Skip to content

Commit 182e39e

Browse files
committed
Feature: transient storage (#4950)
* Transient storage functionality * Commented code block in blockchain_test.go * Refactor state transition rules and remove commented-out Prepare method * Remove commented-out code block in chain_makers_test.go * Remove unnecessary parameter from ChainConfig.Rules calls in runtime.go * Refactor ActivePrecompiles function to use rules parameter instead of evm.chainRules * Refactor state transition preparation to use statedb.Prepare method
1 parent 074cc8a commit 182e39e

File tree

7 files changed

+280
-55
lines changed

7 files changed

+280
-55
lines changed

core/blockchain_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package core
2+
3+
/*
4+
import (
5+
"math/big"
6+
"testing"
7+
8+
"github.com/ethereum/go-ethereum/common"
9+
"github.com/ethereum/go-ethereum/consensus/ethash"
10+
"github.com/ethereum/go-ethereum/core/types"
11+
"github.com/ethereum/go-ethereum/crypto"
12+
"github.com/harmony-one/harmony/core/rawdb"
13+
"github.com/harmony-one/harmony/core/vm"
14+
"github.com/harmony-one/harmony/internal/params"
15+
)
16+
17+
// TestTransientStorageReset ensures the transient storage is wiped correctly
18+
// between transactions.
19+
func TestTransientStorageReset(t *testing.T) {
20+
var (
21+
engine = ethash.NewFaker()
22+
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
23+
address = crypto.PubkeyToAddress(key.PublicKey)
24+
destAddress = crypto.CreateAddress(address, 0)
25+
funds = big.NewInt(1000000000000000)
26+
vmConfig = vm.Config{
27+
ExtraEips: []int{1153}, // Enable transient storage EIP
28+
}
29+
)
30+
code := append([]byte{
31+
// TLoad value with location 1
32+
byte(vm.PUSH1), 0x1,
33+
byte(vm.TLOAD),
34+
35+
// PUSH location
36+
byte(vm.PUSH1), 0x1,
37+
38+
// SStore location:value
39+
byte(vm.SSTORE),
40+
}, make([]byte, 32-6)...)
41+
initCode := []byte{
42+
// TSTORE 1:1
43+
byte(vm.PUSH1), 0x1,
44+
byte(vm.PUSH1), 0x1,
45+
byte(vm.TSTORE),
46+
47+
// Get the runtime-code on the stack
48+
byte(vm.PUSH32)}
49+
initCode = append(initCode, code...)
50+
initCode = append(initCode, []byte{
51+
byte(vm.PUSH1), 0x0, // offset
52+
byte(vm.MSTORE),
53+
byte(vm.PUSH1), 0x6, // size
54+
byte(vm.PUSH1), 0x0, // offset
55+
byte(vm.RETURN), // return 6 bytes of zero-code
56+
}...)
57+
gspec := &Genesis{
58+
Config: params.TestChainConfig,
59+
Alloc: GenesisAlloc{
60+
address: {Balance: funds},
61+
},
62+
}
63+
nonce := uint64(0)
64+
signer := types.HomesteadSigner{}
65+
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
66+
fee := big.NewInt(1)
67+
if b.header.BaseFee != nil {
68+
fee = b.header.BaseFee
69+
}
70+
b.SetCoinbase(common.Address{1})
71+
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
72+
Nonce: nonce,
73+
GasPrice: new(big.Int).Set(fee),
74+
Gas: 100000,
75+
Data: initCode,
76+
})
77+
nonce++
78+
b.AddTxWithVMConfig(tx, vmConfig)
79+
80+
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
81+
Nonce: nonce,
82+
GasPrice: new(big.Int).Set(fee),
83+
Gas: 100000,
84+
To: &destAddress,
85+
})
86+
b.AddTxWithVMConfig(tx, vmConfig)
87+
nonce++
88+
})
89+
90+
// Initialize the blockchain with 1153 enabled.
91+
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vmConfig, nil, nil)
92+
if err != nil {
93+
t.Fatalf("failed to create tester chain: %v", err)
94+
}
95+
// Import the blocks
96+
if _, err := chain.InsertChain(blocks); err != nil {
97+
t.Fatalf("failed to insert into chain: %v", err)
98+
}
99+
// Check the storage
100+
state, err := chain.StateAt(chain.CurrentHeader().Root())
101+
if err != nil {
102+
t.Fatalf("Failed to load state %v", err)
103+
}
104+
loc := common.BytesToHash([]byte{1})
105+
slot := state.GetState(destAddress, loc)
106+
if slot != (common.Hash{}) {
107+
t.Fatalf("Unexpected dirty storage slot")
108+
}
109+
}
110+
*/

core/chain_makers_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2015 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package core
18+
19+
/*
20+
import (
21+
"fmt"
22+
"math/big"
23+
24+
"github.com/ethereum/go-ethereum/consensus/ethash"
25+
"github.com/ethereum/go-ethereum/core/rawdb"
26+
"github.com/ethereum/go-ethereum/core/types"
27+
"github.com/ethereum/go-ethereum/core/vm"
28+
"github.com/ethereum/go-ethereum/crypto"
29+
"github.com/ethereum/go-ethereum/params"
30+
)
31+
32+
func ExampleGenerateChain() {
33+
var (
34+
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
35+
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
36+
key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
37+
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
38+
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
39+
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
40+
db = rawdb.NewMemoryDatabase()
41+
)
42+
43+
// Ensure that key1 has some funds in the genesis block.
44+
gspec := &Genesis{
45+
Config: &params.ChainConfig{HomesteadBlock: new(big.Int)},
46+
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
47+
}
48+
genesis := gspec.MustCommit(db)
49+
50+
// This call generates a chain of 5 blocks. The function runs for
51+
// each block and adds different features to gen based on the
52+
// block index.
53+
signer := types.HomesteadSigner{}
54+
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) {
55+
switch i {
56+
case 0:
57+
// In block 1, addr1 sends addr2 some ether.
58+
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
59+
gen.AddTx(tx)
60+
case 1:
61+
// In block 2, addr1 sends some more ether to addr2.
62+
// addr2 passes it on to addr3.
63+
tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
64+
tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
65+
gen.AddTx(tx1)
66+
gen.AddTx(tx2)
67+
case 2:
68+
// Block 3 is empty but was mined by addr3.
69+
gen.SetCoinbase(addr3)
70+
gen.SetExtra([]byte("yeehaw"))
71+
case 3:
72+
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
73+
b2 := gen.PrevBlock(1).Header()
74+
b2.Extra = []byte("foo")
75+
gen.AddUncle(b2)
76+
b3 := gen.PrevBlock(2).Header()
77+
b3.Extra = []byte("foo")
78+
gen.AddUncle(b3)
79+
}
80+
})
81+
82+
// Import the chain. This runs all block validation rules.
83+
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
84+
defer blockchain.Stop()
85+
86+
if i, err := blockchain.InsertChain(chain); err != nil {
87+
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
88+
return
89+
}
90+
91+
state, _ := blockchain.State()
92+
fmt.Printf("last block: #%d\n", blockchain.CurrentBlock().Number())
93+
fmt.Println("balance of addr1:", state.GetBalance(addr1))
94+
fmt.Println("balance of addr2:", state.GetBalance(addr2))
95+
fmt.Println("balance of addr3:", state.GetBalance(addr3))
96+
// Output:
97+
// last block: #5
98+
// balance of addr1: 989000
99+
// balance of addr2: 10000
100+
// balance of addr3: 19687500000000001000
101+
}
102+
*/

core/state/statedb.go

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ import (
3131
"github.com/ethereum/go-ethereum/trie"
3232
"github.com/harmony-one/harmony/core/rawdb"
3333
"github.com/harmony-one/harmony/core/state/snapshot"
34-
3534
types2 "github.com/harmony-one/harmony/core/types"
3635
common2 "github.com/harmony-one/harmony/internal/common"
36+
"github.com/harmony-one/harmony/internal/params"
3737
"github.com/harmony-one/harmony/internal/utils"
3838
"github.com/harmony-one/harmony/numeric"
3939
"github.com/harmony-one/harmony/staking"
@@ -1165,53 +1165,41 @@ func (db *DB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
11651165
return root, nil
11661166
}
11671167

1168-
// PrepareAccessList handles the preparatory steps for executing a state transition with
1169-
// regards to both EIP-2929 and EIP-2930:
1168+
// Prepare handles the preparatory steps for executing a state transition with.
1169+
// This method must be invoked before state transition.
11701170
//
1171+
// Berlin fork:
11711172
// - Add sender to access list (2929)
11721173
// - Add destination to access list (2929)
11731174
// - Add precompiles to access list (2929)
11741175
// - Add the contents of the optional tx access list (2930)
11751176
//
1176-
// This method should only be called if Berlin/2929+2930 is applicable at the current number.
1177-
func (s *DB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types2.AccessList) {
1178-
// Clear out any leftover from previous executions
1179-
s.accessList = newAccessList()
1180-
1181-
s.AddAddressToAccessList(sender)
1182-
if dst != nil {
1183-
s.AddAddressToAccessList(*dst)
1184-
// If it's a create-tx, the destination will be added inside evm.create
1185-
}
1186-
for _, addr := range precompiles {
1187-
s.AddAddressToAccessList(addr)
1188-
}
1189-
for _, el := range list {
1190-
s.AddAddressToAccessList(el.Address)
1191-
for _, key := range el.StorageKeys {
1192-
s.AddSlotToAccessList(el.Address, key)
1177+
// Potential EIPs:
1178+
// - Reset transient storage(1153)
1179+
func (s *DB) Prepare(rules params.Rules, sender common.Address, dst *common.Address, precompiles []common.Address, list types2.AccessList) {
1180+
if rules.IsBerlin {
1181+
// Clear out any leftover from previous executions
1182+
s.accessList = newAccessList()
1183+
1184+
s.AddAddressToAccessList(sender)
1185+
if dst != nil {
1186+
s.AddAddressToAccessList(*dst)
1187+
// If it's a create-tx, the destination will be added inside evm.create
1188+
}
1189+
for _, addr := range precompiles {
1190+
s.AddAddressToAccessList(addr)
1191+
}
1192+
for _, el := range list {
1193+
s.AddAddressToAccessList(el.Address)
1194+
for _, key := range el.StorageKeys {
1195+
s.AddSlotToAccessList(el.Address, key)
1196+
}
11931197
}
11941198
}
1199+
// Reset transient storage at the beginning of transaction execution
1200+
s.transientStorage = newTransientStorage()
11951201
}
11961202

1197-
/*
1198-
1199-
// Prepare handles the preparatory steps for executing a state transition with.
1200-
// This method must be invoked before state transition.
1201-
//
1202-
// - reset transient storage (1153)
1203-
//
1204-
// todo(sun): berlin fork
1205-
// - add sender to access list (2929)
1206-
// - add destination to access list (2929)
1207-
// - add precompiles to access list (2929)
1208-
// - add the contents of the optional tx access list (2930)
1209-
func (db *DB) Prepare() {
1210-
// reset transient storage prior to transaction execution
1211-
db.transientStorage = newTransientStorage()
1212-
}
1213-
*/
1214-
12151203
// AddAddressToAccessList adds the given address to the access list
12161204
func (db *DB) AddAddressToAccessList(addr common.Address) {
12171205
if db.accessList.AddAddress(addr) {

core/vm/evm.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,17 @@ type (
6969
// ActivePrecompiles returns the addresses of the precompiles enabled with the current
7070
// configuration
7171
func (evm *EVM) ActivePrecompiles() []common.Address {
72+
return ActivePrecompiles(evm.chainRules)
73+
}
74+
75+
// ActivePrecompiles returns the precompiles enabled with the current configuration.
76+
func ActivePrecompiles(rules params.Rules) []common.Address {
7277
switch {
73-
case evm.chainRules.IsYoloV2:
78+
case rules.IsYoloV2:
7479
return PrecompiledAddressesYoloV2
75-
case evm.chainRules.IsIstanbul:
80+
case rules.IsIstanbul:
7681
return PrecompiledAddressesIstanbul
77-
case evm.chainRules.IsByzantium:
82+
case rules.IsByzantium:
7883
return PrecompiledAddressesByzantium
7984
default:
8085
return PrecompiledAddressesHomestead

core/vm/interface.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121

2222
"github.com/ethereum/go-ethereum/common"
2323
"github.com/harmony-one/harmony/core/types"
24-
htypes "github.com/harmony-one/harmony/core/types"
24+
"github.com/harmony-one/harmony/internal/params"
2525
"github.com/harmony-one/harmony/numeric"
2626
staking "github.com/harmony-one/harmony/staking/types"
2727
)
@@ -58,9 +58,8 @@ type StateDB interface {
5858
GetCommittedState(common.Address, common.Hash) common.Hash
5959
GetState(common.Address, common.Hash) common.Hash
6060
SetState(common.Address, common.Hash, common.Hash)
61-
62-
//GetTransientState(addr common.Address, key common.Hash) common.Hash
63-
//SetTransientState(addr common.Address, key, value common.Hash)
61+
GetTransientState(addr common.Address, key common.Hash) common.Hash
62+
SetTransientState(addr common.Address, key, value common.Hash)
6463

6564
Suicide(common.Address) bool
6665
HasSuicided(common.Address) bool
@@ -72,7 +71,8 @@ type StateDB interface {
7271
// is defined according to EIP161 (balance = nonce = code = 0).
7372
Empty(common.Address) bool
7473

75-
PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses htypes.AccessList)
74+
// PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses htypes.AccessList)
75+
7676
AddressInAccessList(addr common.Address) bool
7777
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
7878
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
@@ -82,6 +82,8 @@ type StateDB interface {
8282
// even if the feature/fork is not active yet
8383
AddSlotToAccessList(addr common.Address, slot common.Hash)
8484

85+
Prepare(rules params.Rules, sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
86+
8587
RevertToSnapshot(int)
8688
Snapshot() int
8789

core/vm/opcodes.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ const (
222222
SELFDESTRUCT OpCode = 0xff
223223
)
224224

225+
const (
226+
TLOAD OpCode = 0xb3
227+
TSTORE OpCode = 0xb4
228+
)
229+
225230
// Since the opcodes aren't all in order we can't use a regular slice.
226231
var opCodeToString = map[OpCode]string{
227232
// 0x0 range - arithmetic ops.
@@ -376,6 +381,10 @@ var opCodeToString = map[OpCode]string{
376381
LOG3: "LOG3",
377382
LOG4: "LOG4",
378383

384+
// 0xb0 range.
385+
TLOAD: "TLOAD",
386+
TSTORE: "TSTORE",
387+
379388
// 0xf0 range.
380389
CREATE: "CREATE",
381390
CALL: "CALL",
@@ -466,6 +475,8 @@ var stringToOp = map[string]OpCode{
466475
"GAS": GAS,
467476
"JUMPDEST": JUMPDEST,
468477
"PUSH0": PUSH0,
478+
"TLOAD": TLOAD,
479+
"TSTORE": TSTORE,
469480
"PUSH1": PUSH1,
470481
"PUSH2": PUSH2,
471482
"PUSH3": PUSH3,

0 commit comments

Comments
 (0)