Skip to content

Commit 88cf3e7

Browse files
authored
Merge pull request #647 from gzliudan/eip-1153
all: implement EIP-1153(transient storage)
2 parents 1c5e564 + ed242b4 commit 88cf3e7

File tree

18 files changed

+447
-68
lines changed

18 files changed

+447
-68
lines changed

consensus/tests/engine_v1_tests/helper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs
381381
var gasUsed = new(uint64)
382382
var receipts types.Receipts
383383
for i, tx := range txs {
384-
statedb.Prepare(tx.Hash(), i)
384+
statedb.SetTxContext(tx.Hash(), i)
385385
receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
386386
if err != nil {
387387
return nil, fmt.Errorf("%v when applying transaction", err)

consensus/tests/engine_v2_tests/helper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ func createBlockFromHeader(bc *core.BlockChain, customHeader *types.Header, txs
698698
var gasUsed = new(uint64)
699699
var receipts types.Receipts
700700
for i, tx := range txs {
701-
statedb.Prepare(tx.Hash(), i)
701+
statedb.SetTxContext(tx.Hash(), i)
702702
receipt, _, err, _ := core.ApplyTransaction(bc.Config(), nil, bc, &header.Coinbase, gp, statedb, nil, &header, tx, gasUsed, vm.Config{})
703703
if err != nil {
704704
return nil, fmt.Errorf("%v when applying transaction", err)

core/blockchain_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,3 +1512,100 @@ func TestEIP2718Transition(t *testing.T) {
15121512
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
15131513
}
15141514
}
1515+
1516+
// TestTransientStorageReset ensures the transient storage is wiped correctly
1517+
// between transactions.
1518+
func TestTransientStorageReset(t *testing.T) {
1519+
var (
1520+
engine = ethash.NewFaker()
1521+
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
1522+
address = crypto.PubkeyToAddress(key.PublicKey)
1523+
destAddress = crypto.CreateAddress(address, 0)
1524+
funds = big.NewInt(1000000000000000)
1525+
vmConfig = vm.Config{
1526+
ExtraEips: []int{1153}, // Enable transient storage EIP
1527+
}
1528+
)
1529+
code := append([]byte{
1530+
// TLoad value with location 1
1531+
byte(vm.PUSH1), 0x1,
1532+
byte(vm.TLOAD),
1533+
1534+
// PUSH location
1535+
byte(vm.PUSH1), 0x1,
1536+
1537+
// SStore location:value
1538+
byte(vm.SSTORE),
1539+
}, make([]byte, 32-6)...)
1540+
initCode := []byte{
1541+
// TSTORE 1:1
1542+
byte(vm.PUSH1), 0x1,
1543+
byte(vm.PUSH1), 0x1,
1544+
byte(vm.TSTORE),
1545+
1546+
// Get the runtime-code on the stack
1547+
byte(vm.PUSH32)}
1548+
initCode = append(initCode, code...)
1549+
initCode = append(initCode, []byte{
1550+
byte(vm.PUSH1), 0x0, // offset
1551+
byte(vm.MSTORE),
1552+
byte(vm.PUSH1), 0x6, // size
1553+
byte(vm.PUSH1), 0x0, // offset
1554+
byte(vm.RETURN), // return 6 bytes of zero-code
1555+
}...)
1556+
gspec := &Genesis{
1557+
Config: params.TestChainConfig,
1558+
Alloc: GenesisAlloc{
1559+
address: {Balance: funds},
1560+
},
1561+
}
1562+
nonce := uint64(0)
1563+
signer := types.HomesteadSigner{}
1564+
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
1565+
fee := big.NewInt(1)
1566+
if b.header.BaseFee != nil {
1567+
fee = b.header.BaseFee
1568+
}
1569+
b.SetCoinbase(common.Address{1})
1570+
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
1571+
Nonce: nonce,
1572+
GasPrice: new(big.Int).Set(fee),
1573+
Gas: 100000,
1574+
Data: initCode,
1575+
})
1576+
nonce++
1577+
b.AddTxWithVMConfig(tx, vmConfig)
1578+
1579+
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
1580+
Nonce: nonce,
1581+
GasPrice: new(big.Int).Set(fee),
1582+
Gas: 100000,
1583+
To: &destAddress,
1584+
})
1585+
b.AddTxWithVMConfig(tx, vmConfig)
1586+
nonce++
1587+
})
1588+
1589+
diskdb := rawdb.NewMemoryDatabase()
1590+
gspec.MustCommit(diskdb)
1591+
1592+
// Initialize the blockchain with 1153 enabled.
1593+
chain, err := NewBlockChain(diskdb, nil, gspec.Config, engine, vmConfig)
1594+
if err != nil {
1595+
t.Fatalf("failed to create tester chain: %v", err)
1596+
}
1597+
// Import the blocks
1598+
if _, err := chain.InsertChain(blocks); err != nil {
1599+
t.Fatalf("failed to insert into chain: %v", err)
1600+
}
1601+
// Check the storage
1602+
state, err := chain.StateAt(chain.CurrentHeader().Root)
1603+
if err != nil {
1604+
t.Fatalf("Failed to load state %v", err)
1605+
}
1606+
loc := common.BytesToHash([]byte{1})
1607+
slot := state.GetState(destAddress, loc)
1608+
if slot != (common.Hash{}) {
1609+
t.Fatalf("Unexpected dirty storage slot")
1610+
}
1611+
}

core/chain_makers.go

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,31 @@ func (b *BlockGen) SetExtra(data []byte) {
7575
b.header.Extra = data
7676
}
7777

78+
// addTx adds a transaction to the generated block. If no coinbase has
79+
// been set, the block's coinbase is set to the zero address.
80+
//
81+
// There are a few options can be passed as well in order to run some
82+
// customized rules.
83+
// - bc: enables the ability to query historical block hashes for BLOCKHASH
84+
// - vmConfig: extends the flexibility for customizing evm rules, e.g. enable extra EIPs
85+
func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transaction) {
86+
if b.gasPool == nil {
87+
b.SetCoinbase(common.Address{})
88+
}
89+
feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb)
90+
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
91+
receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vmConfig)
92+
if err != nil {
93+
panic(err)
94+
}
95+
b.txs = append(b.txs, tx)
96+
b.receipts = append(b.receipts, receipt)
97+
if tokenFeeUsed {
98+
fee := common.GetGasFee(b.header.Number.Uint64(), gas)
99+
state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee)
100+
}
101+
}
102+
78103
// AddTx adds a transaction to the generated block. If no coinbase has
79104
// been set, the block's coinbase is set to the zero address.
80105
//
@@ -84,7 +109,7 @@ func (b *BlockGen) SetExtra(data []byte) {
84109
// added. Notably, contract code relying on the BLOCKHASH instruction
85110
// will panic during execution.
86111
func (b *BlockGen) AddTx(tx *types.Transaction) {
87-
b.AddTxWithChain(nil, tx)
112+
b.addTx(nil, vm.Config{}, tx)
88113
}
89114

90115
// AddTxWithChain adds a transaction to the generated block. If no coinbase has
@@ -96,21 +121,14 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
96121
// added. If contract code relies on the BLOCKHASH instruction,
97122
// the block in chain will be returned.
98123
func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
99-
if b.gasPool == nil {
100-
b.SetCoinbase(common.Address{})
101-
}
102-
feeCapacity := state.GetTRC21FeeCapacityFromState(b.statedb)
103-
b.statedb.Prepare(tx.Hash(), len(b.txs))
104-
receipt, gas, err, tokenFeeUsed := ApplyTransaction(b.config, feeCapacity, bc, &b.header.Coinbase, b.gasPool, b.statedb, nil, b.header, tx, &b.header.GasUsed, vm.Config{})
105-
if err != nil {
106-
panic(err)
107-
}
108-
b.txs = append(b.txs, tx)
109-
b.receipts = append(b.receipts, receipt)
110-
if tokenFeeUsed {
111-
fee := common.GetGasFee(b.header.Number.Uint64(), gas)
112-
state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee)
113-
}
124+
b.addTx(bc, vm.Config{}, tx)
125+
}
126+
127+
// AddTxWithVMConfig adds a transaction to the generated block. If no coinbase has
128+
// been set, the block's coinbase is set to the zero address.
129+
// The evm interpreter can be customized with the provided vm config.
130+
func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
131+
b.addTx(nil, config, tx)
114132
}
115133

116134
// AddUncheckedTx forcefully adds a transaction to the block without any

core/state/journal.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ type (
8888
address *common.Address
8989
slot *common.Hash
9090
}
91+
92+
transientStorageChange struct {
93+
account *common.Address
94+
key, prevalue common.Hash
95+
}
9196
)
9297

9398
func (ch createObjectChange) undo(s *StateDB) {
@@ -134,6 +139,10 @@ func (ch storageChange) undo(s *StateDB) {
134139
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
135140
}
136141

142+
func (ch transientStorageChange) undo(s *StateDB) {
143+
s.setTransientState(*ch.account, ch.key, ch.prevalue)
144+
}
145+
137146
func (ch refundChange) undo(s *StateDB) {
138147
s.refund = ch.prev
139148
}

core/state/statedb.go

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/XinFinOrg/XDPoSChain/core/types"
2828
"github.com/XinFinOrg/XDPoSChain/crypto"
2929
"github.com/XinFinOrg/XDPoSChain/log"
30+
"github.com/XinFinOrg/XDPoSChain/params"
3031
"github.com/XinFinOrg/XDPoSChain/rlp"
3132
"github.com/XinFinOrg/XDPoSChain/trie"
3233
)
@@ -69,6 +70,9 @@ type StateDB struct {
6970
// Per-transaction access list
7071
accessList *accessList
7172

73+
// Transient storage
74+
transientStorage transientStorage
75+
7276
// Journal of state modifications. This is the backbone of
7377
// Snapshot and RevertToSnapshot.
7478
journal journal
@@ -117,6 +121,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
117121
logs: make(map[common.Hash][]*types.Log),
118122
preimages: make(map[common.Hash][]byte),
119123
accessList: newAccessList(),
124+
transientStorage: newTransientStorage(),
120125
}, nil
121126
}
122127

@@ -406,6 +411,35 @@ func (s *StateDB) Suicide(addr common.Address) bool {
406411
return true
407412
}
408413

414+
// SetTransientState sets transient storage for a given account. It
415+
// adds the change to the journal so that it can be rolled back
416+
// to its previous value if there is a revert.
417+
func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) {
418+
prev := s.GetTransientState(addr, key)
419+
if prev == value {
420+
return
421+
}
422+
423+
s.journal = append(s.journal, transientStorageChange{
424+
account: &addr,
425+
key: key,
426+
prevalue: prev,
427+
})
428+
429+
s.setTransientState(addr, key, value)
430+
}
431+
432+
// setTransientState is a lower level setter for transient storage. It
433+
// is called during a revert to prevent modifications to the journal.
434+
func (s *StateDB) setTransientState(addr common.Address, key, value common.Hash) {
435+
s.transientStorage.Set(addr, key, value)
436+
}
437+
438+
// GetTransientState gets transient storage for a given account.
439+
func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash {
440+
return s.transientStorage.Get(addr, key)
441+
}
442+
409443
//
410444
// Setting, updating & deleting state object methods.
411445
//
@@ -577,6 +611,9 @@ func (s *StateDB) Copy() *StateDB {
577611
// However, it doesn't cost us much to copy an empty list, so we do it anyway
578612
// to not blow up if we ever decide copy it in the middle of a transaction
579613
state.accessList = s.accessList.Copy()
614+
615+
state.transientStorage = s.transientStorage.Copy()
616+
580617
return state
581618
}
582619

@@ -638,9 +675,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
638675
return s.trie.Hash()
639676
}
640677

641-
// Prepare sets the current transaction hash and index and block hash which is
642-
// used when the EVM emits new state logs.
643-
func (s *StateDB) Prepare(thash common.Hash, ti int) {
678+
// SetTxContext sets the current transaction hash and index which are
679+
// used when the EVM emits new state logs. It should be invoked before
680+
// transaction execution.
681+
func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
644682
s.thash = thash
645683
s.txIndex = ti
646684
}
@@ -717,33 +755,39 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
717755
return root, err
718756
}
719757

720-
// PrepareAccessList handles the preparatory steps for executing a state transition with
721-
// regards to both EIP-2929 and EIP-2930:
758+
// Prepare handles the preparatory steps for executing a state transition with.
759+
// This method must be invoked before state transition.
722760
//
761+
// Berlin fork:
723762
// - Add sender to access list (2929)
724763
// - Add destination to access list (2929)
725764
// - Add precompiles to access list (2929)
726765
// - Add the contents of the optional tx access list (2930)
727766
//
728-
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
729-
func (s *StateDB) PrepareAccessList(sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
730-
// Clear out any leftover from previous executions
731-
s.accessList = newAccessList()
732-
733-
s.AddAddressToAccessList(sender)
734-
if dst != nil {
735-
s.AddAddressToAccessList(*dst)
736-
// If it's a create-tx, the destination will be added inside evm.create
737-
}
738-
for _, addr := range precompiles {
739-
s.AddAddressToAccessList(addr)
740-
}
741-
for _, el := range list {
742-
s.AddAddressToAccessList(el.Address)
743-
for _, key := range el.StorageKeys {
744-
s.AddSlotToAccessList(el.Address, key)
767+
// Potential EIPs:
768+
// - Reset transient storage(1153)
769+
func (s *StateDB) Prepare(rules params.Rules, sender common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) {
770+
if rules.IsEIP1559 {
771+
// Clear out any leftover from previous executions
772+
s.accessList = newAccessList()
773+
774+
s.AddAddressToAccessList(sender)
775+
if dst != nil {
776+
s.AddAddressToAccessList(*dst)
777+
// If it's a create-tx, the destination will be added inside evm.create
778+
}
779+
for _, addr := range precompiles {
780+
s.AddAddressToAccessList(addr)
781+
}
782+
for _, el := range list {
783+
s.AddAddressToAccessList(el.Address)
784+
for _, key := range el.StorageKeys {
785+
s.AddSlotToAccessList(el.Address, key)
786+
}
745787
}
746788
}
789+
// Reset transient storage at the beginning of transaction execution
790+
s.transientStorage = newTransientStorage()
747791
}
748792

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

0 commit comments

Comments
 (0)