Skip to content

Commit 3592139

Browse files
NazariiDenhatynessnreynoldsholimanrjl493456442
authored
(feat)all: add EIP-1153 - transient storage (#721)
* all: implement EIP-1153 transient storage (ethereum#26003) Implements TSTORE and TLOAD as specified by the following EIP: https://eips.ethereum.org/EIPS/eip-1153 https://ethereum-magicians.org/t/eip-1153-transient-storage-opcodes/553 Co-authored-by: Sara Reynolds <snreynolds2506@gmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Gary Rong <garyrong0905@gmail.com> * core/vm: move TSTORE,TLOAD to correct opcode nums (ethereum#27613) * core/vm: move TSTORE,TLOAD to correct opcode nums * core/vm: cleanup * fix tests, rename * goimports * enable 1153 at Curie * bump version * comment fix * improve test * version * testchainconfig * fix another test that affects newly added * fix previous test to clenaup after --------- Co-authored-by: Mark Tyneway <mark.tyneway@gmail.com> Co-authored-by: Sara Reynolds <snreynolds2506@gmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Gary Rong <garyrong0905@gmail.com> Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
1 parent a860446 commit 3592139

24 files changed

+478
-84
lines changed

cmd/evm/internal/t8ntool/execution.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
162162
}
163163
vmConfig.Tracer = tracer
164164
vmConfig.Debug = (tracer != nil)
165-
statedb.Prepare(tx.Hash(), txIndex)
165+
statedb.SetTxContext(tx.Hash(), txIndex)
166166
txContext := core.NewEVMTxContext(msg)
167167
snapshot := statedb.Snapshot()
168168
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)

core/blockchain_test.go

+106-1
Original file line numberDiff line numberDiff line change
@@ -3188,6 +3188,9 @@ func TestTransactionCountLimit(t *testing.T) {
31883188
config := params.TestChainConfig
31893189
config.Scroll.MaxTxPerBlock = new(int)
31903190
*config.Scroll.MaxTxPerBlock = 1
3191+
defer func() {
3192+
config.Scroll.MaxTxPerBlock = nil
3193+
}()
31913194

31923195
var (
31933196
engine = ethash.NewFaker()
@@ -3359,6 +3362,10 @@ func TestL1MessageValidationFailure(t *testing.T) {
33593362
config.Scroll.L1Config.NumL1MessagesPerBlock = 1
33603363
maxPayload := 1024
33613364
config.Scroll.MaxTxPayloadBytesPerBlock = &maxPayload
3365+
defer func() {
3366+
config.Scroll.MaxTxPayloadBytesPerBlock = nil
3367+
config.Scroll.L1Config.NumL1MessagesPerBlock = 0
3368+
}()
33623369

33633370
genspec := &Genesis{
33643371
Config: config,
@@ -3437,7 +3444,9 @@ func TestBlockPayloadSizeLimit(t *testing.T) {
34373444
config := params.TestChainConfig
34383445
config.Scroll.MaxTxPayloadBytesPerBlock = new(int)
34393446
*config.Scroll.MaxTxPayloadBytesPerBlock = 150
3440-
config.Scroll.MaxTxPerBlock = nil
3447+
defer func() {
3448+
config.Scroll.MaxTxPayloadBytesPerBlock = nil
3449+
}()
34413450

34423451
var (
34433452
engine = ethash.NewFaker()
@@ -3607,3 +3616,99 @@ func TestEIP3651(t *testing.T) {
36073616
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
36083617
}
36093618
}
3619+
3620+
// TestTransientStorageReset ensures the transient storage is wiped correctly
3621+
// between transactions.
3622+
func TestTransientStorageReset(t *testing.T) {
3623+
var (
3624+
db = rawdb.NewMemoryDatabase()
3625+
engine = ethash.NewFaker()
3626+
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
3627+
address = crypto.PubkeyToAddress(key.PublicKey)
3628+
destAddress = crypto.CreateAddress(address, 0)
3629+
funds = big.NewInt(1000000000000000)
3630+
vmConfig = vm.Config{
3631+
ExtraEips: []int{1153}, // Enable transient storage EIP
3632+
}
3633+
)
3634+
code := append([]byte{
3635+
// TLoad value with location 1
3636+
byte(vm.PUSH1), 0x1,
3637+
byte(vm.TLOAD),
3638+
3639+
// PUSH location
3640+
byte(vm.PUSH1), 0x1,
3641+
3642+
// SStore location:value
3643+
byte(vm.SSTORE),
3644+
}, make([]byte, 32-6)...)
3645+
initCode := []byte{
3646+
// TSTORE 1:1
3647+
byte(vm.PUSH1), 0x1,
3648+
byte(vm.PUSH1), 0x1,
3649+
byte(vm.TSTORE),
3650+
3651+
// Get the runtime-code on the stack
3652+
byte(vm.PUSH32)}
3653+
initCode = append(initCode, code...)
3654+
initCode = append(initCode, []byte{
3655+
byte(vm.PUSH1), 0x0, // offset
3656+
byte(vm.MSTORE),
3657+
byte(vm.PUSH1), 0x6, // size
3658+
byte(vm.PUSH1), 0x0, // offset
3659+
byte(vm.RETURN), // return 6 bytes of zero-code
3660+
}...)
3661+
gspec := &Genesis{
3662+
Config: params.TestChainConfig,
3663+
Alloc: GenesisAlloc{
3664+
address: {Balance: funds},
3665+
},
3666+
}
3667+
genesis := gspec.MustCommit(db)
3668+
nonce := uint64(0)
3669+
signer := types.HomesteadSigner{}
3670+
blocks, _ := GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *BlockGen) {
3671+
fee := big.NewInt(1)
3672+
if b.header.BaseFee != nil {
3673+
fee = b.header.BaseFee
3674+
}
3675+
b.SetCoinbase(common.Address{1})
3676+
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
3677+
Nonce: nonce,
3678+
GasPrice: new(big.Int).Set(fee),
3679+
Gas: 100000,
3680+
Data: initCode,
3681+
})
3682+
nonce++
3683+
b.AddTxWithVMConfig(tx, vmConfig)
3684+
3685+
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
3686+
Nonce: nonce,
3687+
GasPrice: new(big.Int).Set(fee),
3688+
Gas: 100000,
3689+
To: &destAddress,
3690+
})
3691+
b.AddTxWithVMConfig(tx, vmConfig)
3692+
nonce++
3693+
})
3694+
3695+
// Initialize the blockchain with 1153 enabled.
3696+
chain, err := NewBlockChain(db, nil, gspec.Config, engine, vmConfig, nil, nil)
3697+
if err != nil {
3698+
t.Fatalf("failed to create tester chain: %v", err)
3699+
}
3700+
// Import the blocks
3701+
if _, err := chain.InsertChain(blocks); err != nil {
3702+
t.Fatalf("failed to insert into chain: %v", err)
3703+
}
3704+
// Check the storage
3705+
state, err := chain.StateAt(chain.CurrentHeader().Root)
3706+
if err != nil {
3707+
t.Fatalf("Failed to load state %v", err)
3708+
}
3709+
loc := common.BytesToHash([]byte{1})
3710+
slot := state.GetState(destAddress, loc)
3711+
if slot != (common.Hash{}) {
3712+
t.Fatalf("Unexpected dirty storage slot")
3713+
}
3714+
}

core/chain_makers.go

+29-11
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,26 @@ func (b *BlockGen) SetDifficulty(diff *big.Int) {
7979
b.header.Difficulty = diff
8080
}
8181

82+
// addTx adds a transaction to the generated block. If no coinbase has
83+
// been set, the block's coinbase is set to the zero address.
84+
//
85+
// There are a few options can be passed as well in order to run some
86+
// customized rules.
87+
// - bc: enables the ability to query historical block hashes for BLOCKHASH
88+
// - vmConfig: extends the flexibility for customizing evm rules, e.g. enable extra EIPs
89+
func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transaction) {
90+
if b.gasPool == nil {
91+
b.SetCoinbase(common.Address{})
92+
}
93+
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
94+
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig)
95+
if err != nil {
96+
panic(err)
97+
}
98+
b.txs = append(b.txs, tx)
99+
b.receipts = append(b.receipts, receipt)
100+
}
101+
82102
// AddTx adds a transaction to the generated block. If no coinbase has
83103
// been set, the block's coinbase is set to the zero address.
84104
//
@@ -88,7 +108,7 @@ func (b *BlockGen) SetDifficulty(diff *big.Int) {
88108
// added. Notably, contract code relying on the BLOCKHASH instruction
89109
// will panic during execution.
90110
func (b *BlockGen) AddTx(tx *types.Transaction) {
91-
b.AddTxWithChain(nil, tx)
111+
b.addTx(nil, vm.Config{}, tx)
92112
}
93113

94114
// AddTxWithChain adds a transaction to the generated block. If no coinbase has
@@ -100,16 +120,14 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
100120
// added. If contract code relies on the BLOCKHASH instruction,
101121
// the block in chain will be returned.
102122
func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
103-
if b.gasPool == nil {
104-
b.SetCoinbase(common.Address{})
105-
}
106-
b.statedb.Prepare(tx.Hash(), len(b.txs))
107-
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
108-
if err != nil {
109-
panic(err)
110-
}
111-
b.txs = append(b.txs, tx)
112-
b.receipts = append(b.receipts, receipt)
123+
b.addTx(bc, vm.Config{}, tx)
124+
}
125+
126+
// AddTxWithVMConfig adds a transaction to the generated block. If no coinbase has
127+
// been set, the block's coinbase is set to the zero address.
128+
// The evm interpreter can be customized with the provided vm config.
129+
func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
130+
b.addTx(nil, config, tx)
113131
}
114132

115133
// GetBalance returns the balance of the given address at the generated block.

core/state/journal.go

+13
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ type (
138138
address *common.Address
139139
slot *common.Hash
140140
}
141+
142+
transientStorageChange struct {
143+
account *common.Address
144+
key, prevalue common.Hash
145+
}
141146
)
142147

143148
func (ch createObjectChange) revert(s *StateDB) {
@@ -213,6 +218,14 @@ func (ch storageChange) dirtied() *common.Address {
213218
return ch.account
214219
}
215220

221+
func (ch transientStorageChange) revert(s *StateDB) {
222+
s.setTransientState(*ch.account, ch.key, ch.prevalue)
223+
}
224+
225+
func (ch transientStorageChange) dirtied() *common.Address {
226+
return nil
227+
}
228+
216229
func (ch refundChange) revert(s *StateDB) {
217230
s.refund = ch.prev
218231
}

core/state/statedb.go

+72-25
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ type StateDB struct {
9797
// Per-transaction access list
9898
accessList *accessList
9999

100+
// Transient storage
101+
transientStorage transientStorage
102+
100103
// Journal of state modifications. This is the backbone of
101104
// Snapshot and RevertToSnapshot.
102105
journal *journal
@@ -140,6 +143,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
140143
preimages: make(map[common.Hash][]byte),
141144
journal: newJournal(),
142145
accessList: newAccessList(),
146+
transientStorage: newTransientStorage(),
143147
hasher: crypto.NewKeccakState(),
144148
}
145149
if sdb.snaps != nil {
@@ -472,6 +476,35 @@ func (s *StateDB) Suicide(addr common.Address) bool {
472476
return true
473477
}
474478

479+
// SetTransientState sets transient storage for a given account. It
480+
// adds the change to the journal so that it can be rolled back
481+
// to its previous value if there is a revert.
482+
func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) {
483+
prev := s.GetTransientState(addr, key)
484+
if prev == value {
485+
return
486+
}
487+
488+
s.journal.append(transientStorageChange{
489+
account: &addr,
490+
key: key,
491+
prevalue: prev,
492+
})
493+
494+
s.setTransientState(addr, key, value)
495+
}
496+
497+
// setTransientState is a lower level setter for transient storage. It
498+
// is called during a revert to prevent modifications to the journal.
499+
func (s *StateDB) setTransientState(addr common.Address, key, value common.Hash) {
500+
s.transientStorage.Set(addr, key, value)
501+
}
502+
503+
// GetTransientState gets transient storage for a given account.
504+
func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash {
505+
return s.transientStorage.Get(addr, key)
506+
}
507+
475508
//
476509
// Setting, updating & deleting state object methods.
477510
//
@@ -635,8 +668,8 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
635668
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
636669
// a contract does the following:
637670
//
638-
// 1. sends funds to sha(account ++ (nonce + 1))
639-
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
671+
// 1. sends funds to sha(account ++ (nonce + 1))
672+
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
640673
//
641674
// Carrying over the balance ensures that Ether doesn't disappear.
642675
func (s *StateDB) CreateAccount(addr common.Address) {
@@ -741,6 +774,8 @@ func (s *StateDB) Copy() *StateDB {
741774
// to not blow up if we ever decide copy it in the middle of a transaction
742775
state.accessList = s.accessList.Copy()
743776

777+
state.transientStorage = s.transientStorage.Copy()
778+
744779
// If there's a prefetcher running, make an inactive copy of it that can
745780
// only access data but does not actively preload (since the user will not
746781
// know that they need to explicitly terminate an active copy).
@@ -913,9 +948,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
913948
return s.trie.Hash()
914949
}
915950

916-
// Prepare sets the current transaction hash and index which are
917-
// used when the EVM emits new state logs.
918-
func (s *StateDB) Prepare(thash common.Hash, ti int) {
951+
// SetTxContext sets the current transaction hash and index which are
952+
// used when the EVM emits new state logs. It should be invoked before
953+
// transaction execution.
954+
func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
919955
s.thash = thash
920956
s.txIndex = ti
921957
s.accessList = newAccessList()
@@ -1018,34 +1054,45 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
10181054
return root, err
10191055
}
10201056

1021-
// PrepareAccessList handles the preparatory steps for executing a state transition with
1022-
// regards to EIP-2929, EIP-2930 and EIP-3651:
1057+
// Prepare handles the preparatory steps for executing a state transition with.
1058+
// This method must be invoked before state transition.
10231059
//
1060+
// Berlin fork:
10241061
// - Add sender to access list (2929)
10251062
// - Add destination to access list (2929)
10261063
// - Add precompiles to access list (2929)
10271064
// - Add the contents of the optional tx access list (2930)
1028-
// - Add coinbase to access list (3651)
10291065
//
1030-
// This method should only be called if Berlin/2929+2930 is applicable at the current number.
1031-
func (s *StateDB) PrepareAccessList(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
1032-
s.AddAddressToAccessList(sender)
1033-
if dst != nil {
1034-
s.AddAddressToAccessList(*dst)
1035-
// If it's a create-tx, the destination will be added inside evm.create
1036-
}
1037-
for _, addr := range precompiles {
1038-
s.AddAddressToAccessList(addr)
1039-
}
1040-
for _, el := range list {
1041-
s.AddAddressToAccessList(el.Address)
1042-
for _, key := range el.StorageKeys {
1043-
s.AddSlotToAccessList(el.Address, key)
1066+
// Potential EIPs:
1067+
// - Reset access list (Berlin)
1068+
// - Add coinbase to access list (EIP-3651)
1069+
// - Reset transient storage (EIP-1153)
1070+
func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
1071+
if rules.IsBerlin {
1072+
// Clear out any leftover from previous executions
1073+
al := newAccessList()
1074+
s.accessList = al
1075+
1076+
al.AddAddress(sender)
1077+
if dst != nil {
1078+
al.AddAddress(*dst)
1079+
// If it's a create-tx, the destination will be added inside evm.create
1080+
}
1081+
for _, addr := range precompiles {
1082+
al.AddAddress(addr)
1083+
}
1084+
for _, el := range list {
1085+
al.AddAddress(el.Address)
1086+
for _, key := range el.StorageKeys {
1087+
al.AddSlot(el.Address, key)
1088+
}
1089+
}
1090+
if rules.IsShanghai { // EIP-3651: warm coinbase
1091+
al.AddAddress(coinbase)
10441092
}
10451093
}
1046-
if rules.IsShanghai { // EIP-3651: warm coinbase
1047-
s.AddAddressToAccessList(coinbase)
1048-
}
1094+
// Reset transient storage at the beginning of transaction execution
1095+
s.transientStorage = newTransientStorage()
10491096
}
10501097

10511098
// AddAddressToAccessList adds the given address to the access list

0 commit comments

Comments
 (0)