mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 03:30:37 +00:00
Implement EIP-1153 transient storage (#7405)
Port https://github.com/ethereum/go-ethereum/pull/26003 --------- Co-authored-by: Mark Tyneway <mark.tyneway@gmail.com>
This commit is contained in:
parent
d475bab15f
commit
1533674dad
@ -605,7 +605,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||
}
|
||||
}
|
||||
gasCap = hi
|
||||
b.pendingState.Prepare(libcommon.Hash{}, libcommon.Hash{}, len(b.pendingBlock.Transactions()))
|
||||
b.pendingState.SetTxContext(libcommon.Hash{}, libcommon.Hash{}, len(b.pendingBlock.Transactions()))
|
||||
|
||||
// Create a helper to check if a gas allowance results in an executable transaction
|
||||
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||
@ -715,7 +715,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx types.Transac
|
||||
return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.GetNonce(), nonce)
|
||||
}
|
||||
|
||||
b.pendingState.Prepare(tx.Hash(), libcommon.Hash{}, len(b.pendingBlock.Transactions()))
|
||||
b.pendingState.SetTxContext(tx.Hash(), libcommon.Hash{}, len(b.pendingBlock.Transactions()))
|
||||
//fmt.Printf("==== Start producing block %d, header: %d\n", b.pendingBlock.NumberU64(), b.pendingHeader.Number.Uint64())
|
||||
if _, _, err := core.ApplyTransaction(
|
||||
b.m.ChainConfig, core.GetHashFn(b.pendingHeader, b.getHeader), b.m.Engine,
|
||||
|
@ -520,7 +520,7 @@ func (b *blockProcessor) applyBlock(
|
||||
for i, tx := range block.Transactions() {
|
||||
if b.txNum >= b.startTxNum {
|
||||
ibs := state.New(b.reader)
|
||||
ibs.Prepare(tx.Hash(), block.Hash(), i)
|
||||
ibs.SetTxContext(tx.Hash(), block.Hash(), i)
|
||||
ct := exec3.NewCallTracer()
|
||||
b.vmConfig.Tracer = ct
|
||||
receipt, _, err := core.ApplyTransaction(b.chainConfig, getHashFn, b.engine, nil, gp, ibs, b.writer, header, tx, usedGas, b.vmConfig, parentHeader.ExcessDataGas)
|
||||
|
@ -207,7 +207,7 @@ func (api *APIImpl) CallMany(ctx context.Context, bundles []Bundle, simulateCont
|
||||
// and apply the message.
|
||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||
for idx, txn := range replayTransactions {
|
||||
st.Prepare(txn.Hash(), block.Hash(), idx)
|
||||
st.SetTxContext(txn.Hash(), block.Hash(), idx)
|
||||
msg, err := txn.AsMessage(*signer, block.BaseFee(), rules)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -65,7 +65,7 @@ func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, chainConfig *chai
|
||||
header := block.Header()
|
||||
excessDataGas := header.ParentExcessDataGas(getHeader)
|
||||
for i, txn := range block.Transactions() {
|
||||
ibs.Prepare(txn.Hash(), block.Hash(), i)
|
||||
ibs.SetTxContext(txn.Hash(), block.Hash(), i)
|
||||
receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, noopWriter, header, txn, usedGas, vm.Config{}, excessDataGas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -515,7 +515,7 @@ func (e *intraBlockExec) execTx(txNum uint64, txIndex int, txn types.Transaction
|
||||
e.stateReader.SetTxNum(txNum)
|
||||
txHash := txn.Hash()
|
||||
e.ibs.Reset()
|
||||
e.ibs.Prepare(txHash, e.blockHash, txIndex)
|
||||
e.ibs.SetTxContext(txHash, e.blockHash, txIndex)
|
||||
gp := new(core.GasPool).AddGas(txn.GetGas())
|
||||
msg, err := txn.AsMessage(*e.signer, e.header.BaseFee, e.rules)
|
||||
if err != nil {
|
||||
|
@ -85,7 +85,7 @@ func (api *OtterscanAPIImpl) genericTracer(dbtx kv.Tx, ctx context.Context, bloc
|
||||
excessDataGas := header.ParentExcessDataGas(getHeader)
|
||||
rules := chainConfig.Rules(block.NumberU64(), header.Time)
|
||||
for idx, tx := range block.Transactions() {
|
||||
ibs.Prepare(tx.Hash(), block.Hash(), idx)
|
||||
ibs.SetTxContext(tx.Hash(), block.Hash(), idx)
|
||||
|
||||
msg, _ := tx.AsMessage(*signer, header.BaseFee, rules)
|
||||
|
||||
|
@ -81,7 +81,7 @@ func (api *OtterscanAPIImpl) traceBlock(dbtx kv.Tx, ctx context.Context, blockNu
|
||||
rules := chainConfig.Rules(block.NumberU64(), header.Time)
|
||||
found := false
|
||||
for idx, tx := range block.Transactions() {
|
||||
ibs.Prepare(tx.Hash(), block.Hash(), idx)
|
||||
ibs.SetTxContext(tx.Hash(), block.Hash(), idx)
|
||||
|
||||
msg, _ := tx.AsMessage(*signer, header.BaseFee, rules)
|
||||
|
||||
|
@ -973,7 +973,7 @@ func (api *TraceAPIImpl) Call(ctx context.Context, args TraceCallParam, traceTyp
|
||||
|
||||
gp := new(core.GasPool).AddGas(msg.Gas())
|
||||
var execResult *core.ExecutionResult
|
||||
ibs.Prepare(libcommon.Hash{}, libcommon.Hash{}, 0)
|
||||
ibs.SetTxContext(libcommon.Hash{}, libcommon.Hash{}, 0)
|
||||
execResult, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, true /* gasBailout */)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1196,9 +1196,9 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx kv.Tx, msgs []type
|
||||
cloneReader = state.NewCachedReader(stateReader, cloneCache)
|
||||
}
|
||||
if args.txHash != nil {
|
||||
ibs.Prepare(*args.txHash, header.Hash(), txIndex)
|
||||
ibs.SetTxContext(*args.txHash, header.Hash(), txIndex)
|
||||
} else {
|
||||
ibs.Prepare(libcommon.Hash{}, header.Hash(), txIndex)
|
||||
ibs.SetTxContext(libcommon.Hash{}, header.Hash(), txIndex)
|
||||
}
|
||||
execResult, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, gasBailout /* gasBailout */)
|
||||
if err != nil {
|
||||
|
@ -836,7 +836,7 @@ func (api *TraceAPIImpl) filterV3(ctx context.Context, dbtx kv.TemporalTx, fromB
|
||||
evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vmConfig)
|
||||
|
||||
gp := new(core.GasPool).AddGas(msg.Gas())
|
||||
ibs.Prepare(txHash, lastBlockHash, txIndex)
|
||||
ibs.SetTxContext(txHash, lastBlockHash, txIndex)
|
||||
var execResult *core.ExecutionResult
|
||||
execResult, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, false /* gasBailout */)
|
||||
if err != nil {
|
||||
|
@ -128,7 +128,7 @@ func (api *PrivateDebugAPIImpl) traceBlock(ctx context.Context, blockNrOrHash rp
|
||||
stream.WriteNil()
|
||||
return ctx.Err()
|
||||
}
|
||||
ibs.Prepare(txn.Hash(), block.Hash(), idx)
|
||||
ibs.SetTxContext(txn.Hash(), block.Hash(), idx)
|
||||
msg, _ := txn.AsMessage(*signer, block.BaseFee(), rules)
|
||||
|
||||
if msg.FeeCap().IsZero() && engine != nil {
|
||||
@ -451,7 +451,7 @@ func (api *PrivateDebugAPIImpl) TraceCallMany(ctx context.Context, bundles []Bun
|
||||
// and apply the message.
|
||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||
for idx, txn := range replayTransactions {
|
||||
st.Prepare(txn.Hash(), block.Hash(), idx)
|
||||
st.SetTxContext(txn.Hash(), block.Hash(), idx)
|
||||
msg, err := txn.AsMessage(*signer, block.BaseFee(), rules)
|
||||
if err != nil {
|
||||
stream.WriteNil()
|
||||
@ -494,7 +494,7 @@ func (api *PrivateDebugAPIImpl) TraceCallMany(ctx context.Context, bundles []Bun
|
||||
}
|
||||
txCtx = core.NewEVMTxContext(msg)
|
||||
ibs := evm.IntraBlockState().(*state.IntraBlockState)
|
||||
ibs.Prepare(common.Hash{}, parent.Hash(), txn_index)
|
||||
ibs.SetTxContext(common.Hash{}, parent.Hash(), txn_index)
|
||||
err = transactions.TraceTx(ctx, msg, blockCtx, txCtx, evm.IntraBlockState(), config, chainConfig, stream, api.evmCallTimeout)
|
||||
|
||||
if err != nil {
|
||||
|
@ -415,7 +415,7 @@ func processBlock23(startTxNum uint64, trace bool, txNumStart uint64, rw *StateR
|
||||
for i, tx := range block.Transactions() {
|
||||
if txNum >= startTxNum {
|
||||
ibs := state.New(rw)
|
||||
ibs.Prepare(tx.Hash(), block.Hash(), i)
|
||||
ibs.SetTxContext(tx.Hash(), block.Hash(), i)
|
||||
ct := exec3.NewCallTracer()
|
||||
vmConfig.Tracer = ct
|
||||
receipt, _, err := core.ApplyTransaction(chainConfig, getHashFn, engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, excessDataGas)
|
||||
|
@ -256,7 +256,7 @@ func runHistory22(trace bool, blockNum, txNumStart uint64, hw *state.HistoryRead
|
||||
for i, tx := range block.Transactions() {
|
||||
hw.SetTxNum(txNum)
|
||||
ibs := state.New(hw)
|
||||
ibs.Prepare(tx.Hash(), block.Hash(), i)
|
||||
ibs.SetTxContext(tx.Hash(), block.Hash(), i)
|
||||
receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig, excessDataGas)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err)
|
||||
|
@ -714,7 +714,7 @@ func runBlock(engine consensus.Engine, ibs *state.IntraBlockState, txnWriter sta
|
||||
systemcontracts.UpgradeBuildInSystemContract(chainConfig, header.Number, ibs)
|
||||
rules := chainConfig.Rules(block.NumberU64(), block.Time())
|
||||
for i, tx := range block.Transactions() {
|
||||
ibs.Prepare(tx.Hash(), block.Hash(), i)
|
||||
ibs.SetTxContext(tx.Hash(), block.Hash(), i)
|
||||
receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, excessDataGas)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err)
|
||||
|
@ -182,7 +182,7 @@ func (rw *Worker) RunTxTaskNoLock(txTask *exec22.TxTask) {
|
||||
rw.taskGasPool.Reset(txTask.Tx.GetGas())
|
||||
rw.callTracer.Reset()
|
||||
vmConfig := vm.Config{Debug: true, Tracer: rw.callTracer, SkipAnalysis: txTask.SkipAnalysis}
|
||||
ibs.Prepare(txHash, txTask.BlockHash, txTask.TxIndex)
|
||||
ibs.SetTxContext(txHash, txTask.BlockHash, txTask.TxIndex)
|
||||
msg := txTask.TxAsMessage
|
||||
|
||||
blockContext := txTask.EvmBlockContext
|
||||
|
@ -330,7 +330,7 @@ func (rw *ReconWorker) runTxTask(txTask *exec22.TxTask) error {
|
||||
} else {
|
||||
gp := new(core.GasPool).AddGas(txTask.Tx.GetGas())
|
||||
vmConfig := vm.Config{NoReceipts: true, SkipAnalysis: txTask.SkipAnalysis}
|
||||
ibs.Prepare(txTask.Tx.Hash(), txTask.BlockHash, txTask.TxIndex)
|
||||
ibs.SetTxContext(txTask.Tx.Hash(), txTask.BlockHash, txTask.TxIndex)
|
||||
msg := txTask.TxAsMessage
|
||||
|
||||
rw.evm.ResetBetweenBlocks(txTask.EvmBlockContext, core.NewEVMTxContext(msg), ibs, vmConfig, txTask.Rules)
|
||||
|
@ -115,7 +115,7 @@ func ExecuteBlockEphemerally(
|
||||
noop := state.NewNoopWriter()
|
||||
//fmt.Printf("====txs processing start: %d====\n", block.NumberU64())
|
||||
for i, tx := range block.Transactions() {
|
||||
ibs.Prepare(tx.Hash(), block.Hash(), i)
|
||||
ibs.SetTxContext(tx.Hash(), block.Hash(), i)
|
||||
writeTrace := false
|
||||
if vmConfig.Debug && vmConfig.Tracer == nil {
|
||||
tracer, err := getTracer(i, tx.Hash())
|
||||
@ -226,7 +226,7 @@ func ExecuteBlockEphemerallyBor(
|
||||
noop := state.NewNoopWriter()
|
||||
//fmt.Printf("====txs processing start: %d====\n", block.NumberU64())
|
||||
for i, tx := range block.Transactions() {
|
||||
ibs.Prepare(tx.Hash(), block.Hash(), i)
|
||||
ibs.SetTxContext(tx.Hash(), block.Hash(), i)
|
||||
writeTrace := false
|
||||
if vmConfig.Debug && vmConfig.Tracer == nil {
|
||||
tracer, err := getTracer(i, tx.Hash())
|
||||
|
@ -116,7 +116,7 @@ func (b *BlockGen) AddTxWithChain(getHeader func(hash libcommon.Hash, number uin
|
||||
if b.gasPool == nil {
|
||||
b.SetCoinbase(libcommon.Address{})
|
||||
}
|
||||
b.ibs.Prepare(tx.Hash(), libcommon.Hash{}, len(b.txs))
|
||||
b.ibs.SetTxContext(tx.Hash(), libcommon.Hash{}, len(b.txs))
|
||||
receipt, _, err := ApplyTransaction(b.config, GetHashFn(b.header, getHeader), engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, b.parent.ExcessDataGas())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -129,7 +129,7 @@ func (b *BlockGen) AddFailedTxWithChain(getHeader func(hash libcommon.Hash, numb
|
||||
if b.gasPool == nil {
|
||||
b.SetCoinbase(libcommon.Address{})
|
||||
}
|
||||
b.ibs.Prepare(tx.Hash(), libcommon.Hash{}, len(b.txs))
|
||||
b.ibs.SetTxContext(tx.Hash(), libcommon.Hash{}, len(b.txs))
|
||||
receipt, _, err := ApplyTransaction(b.config, GetHashFn(b.header, getHeader), engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, b.parent.ExcessDataGas())
|
||||
_ = err // accept failed transactions
|
||||
b.txs = append(b.txs, tx)
|
||||
|
@ -75,13 +75,18 @@ type IntraBlockState struct {
|
||||
logs map[libcommon.Hash][]*types.Log
|
||||
logSize uint
|
||||
|
||||
// Per-transaction access list
|
||||
accessList *accessList
|
||||
|
||||
// Transient storage
|
||||
transientStorage transientStorage
|
||||
|
||||
// Journal of state modifications. This is the backbone of
|
||||
// Snapshot and RevertToSnapshot.
|
||||
journal *journal
|
||||
validRevisions []revision
|
||||
nextRevisionID int
|
||||
trace bool
|
||||
accessList *accessList
|
||||
balanceInc map[libcommon.Address]*BalanceIncrease // Map of balance increases (without first reading the account)
|
||||
}
|
||||
|
||||
@ -95,6 +100,7 @@ func New(stateReader StateReader) *IntraBlockState {
|
||||
logs: map[libcommon.Hash][]*types.Log{},
|
||||
journal: newJournal(),
|
||||
accessList: newAccessList(),
|
||||
transientStorage: newTransientStorage(),
|
||||
balanceInc: map[libcommon.Address]*BalanceIncrease{},
|
||||
}
|
||||
}
|
||||
@ -414,6 +420,35 @@ func (sdb *IntraBlockState) Selfdestruct(addr libcommon.Address) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SetTransientState sets transient storage for a given account. It
|
||||
// adds the change to the journal so that it can be rolled back
|
||||
// to its previous value if there is a revert.
|
||||
func (sdb *IntraBlockState) SetTransientState(addr libcommon.Address, key libcommon.Hash, value uint256.Int) {
|
||||
prev := sdb.GetTransientState(addr, key)
|
||||
if prev == value {
|
||||
return
|
||||
}
|
||||
|
||||
sdb.journal.append(transientStorageChange{
|
||||
account: &addr,
|
||||
key: key,
|
||||
prevalue: prev,
|
||||
})
|
||||
|
||||
sdb.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 (sdb *IntraBlockState) setTransientState(addr libcommon.Address, key libcommon.Hash, value uint256.Int) {
|
||||
sdb.transientStorage.Set(addr, key, value)
|
||||
}
|
||||
|
||||
// GetTransientState gets transient storage for a given account.
|
||||
func (sdb *IntraBlockState) GetTransientState(addr libcommon.Address, key libcommon.Hash) uint256.Int {
|
||||
return sdb.transientStorage.Get(addr, key)
|
||||
}
|
||||
|
||||
func (sdb *IntraBlockState) getStateObject(addr libcommon.Address) (stateObject *stateObject) {
|
||||
// Prefer 'live' objects.
|
||||
if obj := sdb.stateObjects[addr]; obj != nil {
|
||||
@ -701,13 +736,13 @@ func (sdb *IntraBlockState) Print(chainRules chain.Rules) {
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare sets the current transaction hash and index and block hash which is
|
||||
// used when the EVM emits new state logs.
|
||||
func (sdb *IntraBlockState) Prepare(thash, bhash libcommon.Hash, ti int) {
|
||||
// SetTxContext sets the current transaction hash and index and block hash which are
|
||||
// used when the EVM emits new state logs. It should be invoked before
|
||||
// transaction execution.
|
||||
func (sdb *IntraBlockState) SetTxContext(thash, bhash libcommon.Hash, ti int) {
|
||||
sdb.thash = thash
|
||||
sdb.bhash = bhash
|
||||
sdb.txIndex = ti
|
||||
sdb.accessList = newAccessList()
|
||||
}
|
||||
|
||||
// no not lock
|
||||
@ -717,30 +752,48 @@ func (sdb *IntraBlockState) clearJournalAndRefund() {
|
||||
sdb.refund = 0
|
||||
}
|
||||
|
||||
// PrepareAccessList handles the preparatory steps for executing a state transition with
|
||||
// regards to both EIP-2929 and EIP-2930:
|
||||
// Prepare handles the preparatory steps for executing a state transition.
|
||||
// This method must be invoked before state transition.
|
||||
//
|
||||
// - Add sender to access list (2929)
|
||||
// - Add destination to access list (2929)
|
||||
// - Add precompiles to access list (2929)
|
||||
// - Add the contents of the optional tx access list (2930)
|
||||
// Berlin fork:
|
||||
// - Add sender to access list (EIP-2929)
|
||||
// - Add destination to access list (EIP-2929)
|
||||
// - Add precompiles to access list (EIP-2929)
|
||||
// - Add the contents of the optional tx access list (EIP-2930)
|
||||
//
|
||||
// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number.
|
||||
func (sdb *IntraBlockState) PrepareAccessList(sender libcommon.Address, dst *libcommon.Address, precompiles []libcommon.Address, list types2.AccessList) {
|
||||
sdb.AddAddressToAccessList(sender)
|
||||
if dst != nil {
|
||||
sdb.AddAddressToAccessList(*dst)
|
||||
// If it's a create-tx, the destination will be added inside evm.create
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
sdb.AddAddressToAccessList(addr)
|
||||
}
|
||||
for _, el := range list {
|
||||
sdb.AddAddressToAccessList(el.Address)
|
||||
for _, key := range el.StorageKeys {
|
||||
sdb.AddSlotToAccessList(el.Address, key)
|
||||
// Shanghai fork:
|
||||
// - Add coinbase to access list (EIP-3651)
|
||||
//
|
||||
// Cancun fork:
|
||||
// - Reset transient storage (EIP-1153)
|
||||
func (sdb *IntraBlockState) Prepare(rules *chain.Rules, sender, coinbase libcommon.Address, dst *libcommon.Address,
|
||||
precompiles []libcommon.Address, list types2.AccessList,
|
||||
) {
|
||||
if rules.IsBerlin {
|
||||
// Clear out any leftover from previous executions
|
||||
al := newAccessList()
|
||||
sdb.accessList = al
|
||||
|
||||
al.AddAddress(sender)
|
||||
if dst != nil {
|
||||
al.AddAddress(*dst)
|
||||
// If it's a create-tx, the destination will be added inside evm.create
|
||||
}
|
||||
for _, addr := range precompiles {
|
||||
al.AddAddress(addr)
|
||||
}
|
||||
for _, el := range list {
|
||||
al.AddAddress(el.Address)
|
||||
for _, key := range el.StorageKeys {
|
||||
al.AddSlot(el.Address, key)
|
||||
}
|
||||
}
|
||||
if rules.IsShanghai { // EIP-3651: warm coinbase
|
||||
al.AddAddress(coinbase)
|
||||
}
|
||||
}
|
||||
// Reset transient storage at the beginning of transaction execution
|
||||
sdb.transientStorage = newTransientStorage()
|
||||
}
|
||||
|
||||
// AddAddressToAccessList adds the given address to the access list
|
||||
|
@ -158,6 +158,16 @@ func newTestAction(addr libcommon.Address, r *rand.Rand) testAction {
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
{
|
||||
name: "SetTransientState",
|
||||
fn: func(a testAction, s *IntraBlockState) {
|
||||
var key libcommon.Hash
|
||||
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
||||
val := uint256.NewInt(uint64(a.args[1]))
|
||||
s.SetTransientState(addr, key, *val)
|
||||
},
|
||||
args: make([]int64, 2),
|
||||
},
|
||||
}
|
||||
action := actions[r.Intn(len(actions))]
|
||||
var nameargs []string //nolint:prealloc
|
||||
@ -312,3 +322,27 @@ func (test *snapshotTest) checkEqual(state, checkstate *IntraBlockState) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestTransientStorage(t *testing.T) {
|
||||
state := New(nil)
|
||||
|
||||
key := libcommon.Hash{0x01}
|
||||
value := uint256.NewInt(2)
|
||||
addr := libcommon.Address{}
|
||||
|
||||
state.SetTransientState(addr, key, *value)
|
||||
if exp, got := 1, state.journal.length(); 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.journal.revert(state, 0)
|
||||
if got, exp := state.GetTransientState(addr, key), (*uint256.NewInt(0)); exp != got {
|
||||
t.Fatalf("transient storage mismatch: have %x, want %x", got, exp)
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +140,7 @@ type (
|
||||
touchChange struct {
|
||||
account *libcommon.Address
|
||||
}
|
||||
|
||||
// Changes to the access list
|
||||
accessListAddAccountChange struct {
|
||||
address *libcommon.Address
|
||||
@ -148,6 +149,12 @@ type (
|
||||
address *libcommon.Address
|
||||
slot *libcommon.Hash
|
||||
}
|
||||
|
||||
transientStorageChange struct {
|
||||
account *libcommon.Address
|
||||
key libcommon.Hash
|
||||
prevalue uint256.Int
|
||||
}
|
||||
)
|
||||
|
||||
func (ch createObjectChange) revert(s *IntraBlockState) {
|
||||
@ -249,6 +256,14 @@ func (ch fakeStorageChange) dirtied() *libcommon.Address {
|
||||
return ch.account
|
||||
}
|
||||
|
||||
func (ch transientStorageChange) revert(s *IntraBlockState) {
|
||||
s.setTransientState(*ch.account, ch.key, ch.prevalue)
|
||||
}
|
||||
|
||||
func (ch transientStorageChange) dirtied() *libcommon.Address {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch refundChange) revert(s *IntraBlockState) {
|
||||
s.refund = ch.prev
|
||||
}
|
||||
|
48
core/state/transient_storage.go
Normal file
48
core/state/transient_storage.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2022 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
||||
)
|
||||
|
||||
// transientStorage is a representation of EIP-1153 "Transient Storage".
|
||||
type transientStorage map[libcommon.Address]Storage
|
||||
|
||||
// newTransientStorage creates a new instance of a transientStorage.
|
||||
func newTransientStorage() transientStorage {
|
||||
return make(transientStorage)
|
||||
}
|
||||
|
||||
// Set sets the transient-storage `value` for `key` at the given `addr`.
|
||||
func (t transientStorage) Set(addr libcommon.Address, key libcommon.Hash, value uint256.Int) {
|
||||
if _, ok := t[addr]; !ok {
|
||||
t[addr] = make(Storage)
|
||||
}
|
||||
t[addr][key] = value
|
||||
}
|
||||
|
||||
// Get gets the transient storage for `key` at the given `addr`.
|
||||
func (t transientStorage) Get(addr libcommon.Address, key libcommon.Hash) uint256.Int {
|
||||
val, ok := t[addr]
|
||||
if !ok {
|
||||
return *uint256.NewInt(0)
|
||||
}
|
||||
return val[key]
|
||||
}
|
@ -299,11 +299,13 @@ func (st *StateTransition) preCheck(gasBailout bool) error {
|
||||
// However if any consensus issue encountered, return the error directly with
|
||||
// nil evm execution result.
|
||||
func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*ExecutionResult, error) {
|
||||
coinbase := st.evm.Context().Coinbase
|
||||
|
||||
var input1 *uint256.Int
|
||||
var input2 *uint256.Int
|
||||
if st.isBor {
|
||||
input1 = st.state.GetBalance(st.msg.From()).Clone()
|
||||
input2 = st.state.GetBalance(st.evm.Context().Coinbase).Clone()
|
||||
input2 = st.state.GetBalance(coinbase).Clone()
|
||||
}
|
||||
|
||||
// First check this message satisfies all consensus rules before
|
||||
@ -357,14 +359,10 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
|
||||
return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize)
|
||||
}
|
||||
|
||||
// Set up the initial access list.
|
||||
if rules.IsBerlin {
|
||||
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||
// EIP-3651 warm COINBASE
|
||||
if rules.IsShanghai {
|
||||
st.state.AddAddressToAccessList(st.evm.Context().Coinbase)
|
||||
}
|
||||
}
|
||||
// Execute the preparatory steps for state transition which includes:
|
||||
// - prepare accessList(post-berlin)
|
||||
// - reset transient storage(eip 1153)
|
||||
st.state.Prepare(rules, msg.From(), coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
@ -400,7 +398,7 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
|
||||
}
|
||||
amount := new(uint256.Int).SetUint64(st.gasUsed())
|
||||
amount.Mul(amount, effectiveTip) // gasUsed * effectiveTip = how much goes to the block producer (miner, validator)
|
||||
st.state.AddBalance(st.evm.Context().Coinbase, amount)
|
||||
st.state.AddBalance(coinbase, amount)
|
||||
if !msg.IsFree() && rules.IsLondon && rules.IsEip1559FeeCollector {
|
||||
burntContractAddress := *st.evm.ChainConfig().Eip1559FeeCollector
|
||||
burnAmount := new(uint256.Int).Mul(new(uint256.Int).SetUint64(st.gasUsed()), st.evm.Context().BaseFee)
|
||||
@ -415,7 +413,7 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*Executi
|
||||
st.state,
|
||||
|
||||
msg.From(),
|
||||
st.evm.Context().Coinbase,
|
||||
coinbase,
|
||||
|
||||
amount,
|
||||
input1,
|
||||
|
@ -22,18 +22,21 @@ import (
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
||||
|
||||
"github.com/ledgerwatch/erigon/params"
|
||||
)
|
||||
|
||||
var activators = map[int]func(*JumpTable){
|
||||
3855: enable3855,
|
||||
3860: enable3860,
|
||||
3855: enable3855,
|
||||
3529: enable3529,
|
||||
3198: enable3198,
|
||||
2929: enable2929,
|
||||
2200: enable2200,
|
||||
1884: enable1884,
|
||||
1344: enable1344,
|
||||
1153: enable1153,
|
||||
}
|
||||
|
||||
// EnableEIP enables the given EIP on the config.
|
||||
@ -168,6 +171,45 @@ func enable3198(jt *JumpTable) {
|
||||
}
|
||||
}
|
||||
|
||||
// enable1153 applies EIP-1153 "Transient Storage"
|
||||
// - Adds TLOAD that reads from transient storage
|
||||
// - Adds TSTORE that writes to transient storage
|
||||
func enable1153(jt *JumpTable) {
|
||||
jt[TLOAD] = &operation{
|
||||
execute: opTload,
|
||||
constantGas: params.WarmStorageReadCostEIP2929,
|
||||
numPop: 1,
|
||||
numPush: 1,
|
||||
}
|
||||
|
||||
jt[TSTORE] = &operation{
|
||||
execute: opTstore,
|
||||
constantGas: params.WarmStorageReadCostEIP2929,
|
||||
numPop: 2,
|
||||
numPush: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// opTload implements TLOAD opcode
|
||||
func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
loc := scope.Stack.Peek()
|
||||
hash := libcommon.Hash(loc.Bytes32())
|
||||
val := interpreter.evm.IntraBlockState().GetTransientState(scope.Contract.Address(), hash)
|
||||
loc.SetBytes(val.Bytes())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// opTstore implements TSTORE opcode
|
||||
func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
if interpreter.readOnly {
|
||||
return nil, ErrWriteProtection
|
||||
}
|
||||
loc := scope.Stack.Pop()
|
||||
val := scope.Stack.Pop()
|
||||
interpreter.evm.IntraBlockState().SetTransientState(scope.Contract.Address(), loc.Bytes32(), val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// opBaseFee implements BASEFEE opcode
|
||||
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
|
||||
baseFee := interpreter.evm.Context().BaseFee
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
"github.com/ledgerwatch/erigon-lib/chain"
|
||||
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
||||
types2 "github.com/ledgerwatch/erigon-lib/types"
|
||||
|
||||
@ -77,6 +79,9 @@ type IntraBlockState interface {
|
||||
GetState(address libcommon.Address, slot *libcommon.Hash, outValue *uint256.Int)
|
||||
SetState(libcommon.Address, *libcommon.Hash, uint256.Int)
|
||||
|
||||
GetTransientState(addr libcommon.Address, key libcommon.Hash) uint256.Int
|
||||
SetTransientState(addr libcommon.Address, key libcommon.Hash, value uint256.Int)
|
||||
|
||||
Selfdestruct(libcommon.Address) bool
|
||||
HasSelfdestructed(libcommon.Address) bool
|
||||
|
||||
@ -87,7 +92,9 @@ type IntraBlockState interface {
|
||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
Empty(libcommon.Address) bool
|
||||
|
||||
PrepareAccessList(sender libcommon.Address, dest *libcommon.Address, precompiles []libcommon.Address, txAccesses types2.AccessList)
|
||||
Prepare(rules *chain.Rules, sender, coinbase libcommon.Address, dest *libcommon.Address,
|
||||
precompiles []libcommon.Address, txAccesses types2.AccessList)
|
||||
|
||||
AddressInAccessList(addr libcommon.Address) bool
|
||||
SlotInAccessList(addr libcommon.Address, slot libcommon.Hash) (addressOk bool, slotOk bool)
|
||||
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
|
||||
|
@ -25,11 +25,13 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
||||
|
||||
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
|
||||
|
||||
"github.com/ledgerwatch/erigon/common"
|
||||
"github.com/ledgerwatch/erigon/common/u256"
|
||||
"github.com/ledgerwatch/erigon/core/state"
|
||||
"github.com/ledgerwatch/erigon/core/vm/evmtypes"
|
||||
"github.com/ledgerwatch/erigon/core/vm/stack"
|
||||
"github.com/ledgerwatch/erigon/crypto"
|
||||
"github.com/ledgerwatch/erigon/params"
|
||||
@ -51,6 +53,14 @@ type twoOperandParams struct {
|
||||
var commonParams []*twoOperandParams
|
||||
var twoOpMethods map[string]executionFunc
|
||||
|
||||
type contractRef struct {
|
||||
addr libcommon.Address
|
||||
}
|
||||
|
||||
func (c contractRef) Address() libcommon.Address {
|
||||
return c.addr
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
// Params is a list of common edgecases that should be used for some common tests
|
||||
@ -561,6 +571,45 @@ func BenchmarkOpMstore(bench *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpTstore(t *testing.T) {
|
||||
var (
|
||||
state = state.New(nil)
|
||||
env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, state, params.TestChainConfig, Config{})
|
||||
stack = stack.New()
|
||||
mem = NewMemory()
|
||||
evmInterpreter = NewEVMInterpreter(env, env.Config())
|
||||
caller = libcommon.Address{}
|
||||
to = libcommon.Address{1}
|
||||
contractRef = contractRef{caller}
|
||||
contract = NewContract(contractRef, AccountRef(to), u256.Num0, 0, false)
|
||||
scopeContext = ScopeContext{mem, stack, contract}
|
||||
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
|
||||
)
|
||||
|
||||
env.interpreter = evmInterpreter
|
||||
pc := uint64(0)
|
||||
// push the value to the stack
|
||||
stack.Push(new(uint256.Int).SetBytes(value))
|
||||
// push the location to the stack
|
||||
stack.Push(new(uint256.Int))
|
||||
opTstore(&pc, evmInterpreter, &scopeContext)
|
||||
// there should be no elements on the stack after TSTORE
|
||||
if stack.Len() != 0 {
|
||||
t.Fatal("stack wrong size")
|
||||
}
|
||||
// push the location to the stack
|
||||
stack.Push(new(uint256.Int))
|
||||
opTload(&pc, evmInterpreter, &scopeContext)
|
||||
// there should be one element on the stack after TLOAD
|
||||
if stack.Len() != 1 {
|
||||
t.Fatal("stack wrong size")
|
||||
}
|
||||
val := stack.Peek()
|
||||
if !bytes.Equal(val.Bytes(), value) {
|
||||
t.Fatal("incorrect element read from transient storage")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkOpKeccak256(bench *testing.B) {
|
||||
var (
|
||||
env = NewEVM(evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
|
@ -215,6 +215,12 @@ const (
|
||||
SELFDESTRUCT OpCode = 0xff
|
||||
)
|
||||
|
||||
// 0xb0 range.
|
||||
const (
|
||||
TLOAD OpCode = 0xb3
|
||||
TSTORE OpCode = 0xb4
|
||||
)
|
||||
|
||||
// Since the opcodes aren't all in order we can't use a regular slice.
|
||||
var opCodeToString = map[OpCode]string{
|
||||
// 0x0 range - arithmetic ops.
|
||||
@ -370,6 +376,10 @@ var opCodeToString = map[OpCode]string{
|
||||
LOG3: "LOG3",
|
||||
LOG4: "LOG4",
|
||||
|
||||
// 0xb0 range.
|
||||
TLOAD: "TLOAD",
|
||||
TSTORE: "TSTORE",
|
||||
|
||||
// 0xf0 range.
|
||||
CREATE: "CREATE",
|
||||
CALL: "CALL",
|
||||
@ -461,6 +471,8 @@ var stringToOp = map[string]OpCode{
|
||||
"GAS": GAS,
|
||||
"JUMPDEST": JUMPDEST,
|
||||
"PUSH0": PUSH0,
|
||||
"TLOAD": TLOAD,
|
||||
"TSTORE": TSTORE,
|
||||
"PUSH1": PUSH1,
|
||||
"PUSH2": PUSH2,
|
||||
"PUSH3": PUSH3,
|
||||
|
@ -132,10 +132,9 @@ func Execute(code, input []byte, cfg *Config, bn uint64) ([]byte, *state.IntraBl
|
||||
address = libcommon.BytesToAddress([]byte("contract"))
|
||||
vmenv = NewEnv(cfg)
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
rules = cfg.ChainConfig.Rules(vmenv.Context().BlockNumber, vmenv.Context().Time)
|
||||
)
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context().BlockNumber, vmenv.Context().Time); rules.IsBerlin {
|
||||
cfg.State.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||
cfg.State.CreateAccount(address, true)
|
||||
// set the receiver's (the executing contract) code for execution.
|
||||
cfg.State.SetCode(address, code)
|
||||
@ -177,10 +176,9 @@ func Create(input []byte, cfg *Config, blockNr uint64) ([]byte, libcommon.Addres
|
||||
var (
|
||||
vmenv = NewEnv(cfg)
|
||||
sender = vm.AccountRef(cfg.Origin)
|
||||
rules = cfg.ChainConfig.Rules(vmenv.Context().BlockNumber, vmenv.Context().Time)
|
||||
)
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context().BlockNumber, vmenv.Context().Time); rules.IsBerlin {
|
||||
cfg.State.PrepareAccessList(cfg.Origin, nil, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil)
|
||||
|
||||
// Call the code with the given configuration.
|
||||
code, address, leftOverGas, err := vmenv.Create(
|
||||
@ -204,9 +202,8 @@ func Call(address libcommon.Address, input []byte, cfg *Config) ([]byte, uint64,
|
||||
|
||||
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||
statedb := cfg.State
|
||||
if rules := cfg.ChainConfig.Rules(vmenv.Context().BlockNumber, vmenv.Context().Time); rules.IsBerlin {
|
||||
statedb.PrepareAccessList(cfg.Origin, &address, vm.ActivePrecompiles(rules), nil)
|
||||
}
|
||||
rules := cfg.ChainConfig.Rules(vmenv.Context().BlockNumber, vmenv.Context().Time)
|
||||
statedb.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||
|
||||
// Call the code with the given configuration.
|
||||
ret, leftOverGas, err := vmenv.Call(
|
||||
|
@ -383,7 +383,7 @@ func addTransactionsToMiningBlock(logPrefix string, current *MiningBlock, chainC
|
||||
parentHeader := getHeader(header.ParentHash, header.Number.Uint64()-1)
|
||||
|
||||
var miningCommitTx = func(txn types.Transaction, coinbase libcommon.Address, vmConfig *vm.Config, chainConfig chain.Config, ibs *state.IntraBlockState, current *MiningBlock) ([]*types.Log, error) {
|
||||
ibs.Prepare(txn.Hash(), libcommon.Hash{}, tcount)
|
||||
ibs.SetTxContext(txn.Hash(), libcommon.Hash{}, tcount)
|
||||
gasSnap := gasPool.Gas()
|
||||
snap := ibs.Snapshot()
|
||||
log.Debug("addTransactionsToMiningBlock", "txn hash", txn.Hash())
|
||||
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package eth
|
@ -71,7 +71,7 @@ func ComputeTxEnv(ctx context.Context, engine consensus.EngineReader, block *typ
|
||||
if historyV3 {
|
||||
rules := cfg.Rules(BlockContext.BlockNumber, BlockContext.Time)
|
||||
txn := block.Transactions()[txIndex]
|
||||
statedb.Prepare(txn.Hash(), block.Hash(), txIndex)
|
||||
statedb.SetTxContext(txn.Hash(), block.Hash(), txIndex)
|
||||
msg, _ := txn.AsMessage(*signer, block.BaseFee(), rules)
|
||||
if msg.FeeCap().IsZero() && engine != nil {
|
||||
syscall := func(contract libcommon.Address, data []byte) ([]byte, error) {
|
||||
@ -96,7 +96,7 @@ func ComputeTxEnv(ctx context.Context, engine consensus.EngineReader, block *typ
|
||||
case <-ctx.Done():
|
||||
return nil, evmtypes.BlockContext{}, evmtypes.TxContext{}, nil, nil, ctx.Err()
|
||||
}
|
||||
statedb.Prepare(txn.Hash(), block.Hash(), idx)
|
||||
statedb.SetTxContext(txn.Hash(), block.Hash(), idx)
|
||||
|
||||
// Assemble the transaction call message and return if the requested offset
|
||||
msg, _ := txn.AsMessage(*signer, block.BaseFee(), rules)
|
||||
|
Loading…
Reference in New Issue
Block a user