Skip to content

Commit 0dc97b3

Browse files
authored
Update VM cheatcodes to be permanent (#499)
* update warp to be permanent * temporary commit * make warp permanent * make warp and roll permanent * fix some bugs and update other vm-related cheatcodes to be permanent * make coinbase and fee permanent and fix some bugs * remove unused function * fix number of workers for fuzzer test * fix dependencies * see if switching to go 1.22 fixes gencodec
1 parent 31a41da commit 0dc97b3

File tree

14 files changed

+270
-252
lines changed

14 files changed

+270
-252
lines changed

chain/standard_cheat_code_contract.go

Lines changed: 99 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -72,58 +72,108 @@ func getStandardCheatCodeContract(tracer *cheatCodeTracer) (*CheatCodeContract,
7272
return nil, err
7373
}
7474

75-
// Warp: Sets VM timestamp
75+
// Warp: Sets VM timestamp. Note that this _permanently_ updates the block timestamp for the remainder of the
76+
// chain's lifecycle.
7677
contract.addMethod(
7778
"warp", abi.Arguments{{Type: typeUint256}}, abi.Arguments{},
7879
func(tracer *cheatCodeTracer, inputs []any) ([]any, *cheatCodeRawReturnData) {
79-
// Maintain our changes until the transaction exits.
80+
// Capture the original time
8081
originalTime := tracer.chain.pendingBlockContext.Time
8182

82-
// Retrieve new timestamp and make sure it is LEQ max value of an uint64
83+
// Retrieve the new timestamp and make sure it is LEQ max value of an uint64
8384
newTime := inputs[0].(*big.Int)
8485
if newTime.Cmp(MaxUint64) > 0 {
8586
return nil, cheatCodeRevertData([]byte("warp: timestamp exceeds max value of type(uint64).max"))
8687
}
8788

88-
// Set the time
89+
// Set the time for the pending block context and the pending block
90+
// The block context will reflect the time change in the current EVM context
91+
// And the pending block time will allow for the new time to reflect
92+
// permanently for the remainder of the chain's existence.
8993
tracer.chain.pendingBlockContext.Time = newTime.Uint64()
90-
tracer.CurrentCallFrame().onTopFrameExitRestoreHooks.Push(func() {
91-
// Reset the time
92-
tracer.chain.pendingBlockContext.Time = originalTime
94+
tracer.chain.pendingBlock.Header.Time = newTime.Uint64()
95+
96+
// If the transaction reverts, we will restore the original time
97+
tracer.CurrentCallFrame().onChainRevertRestoreHooks.Push(func() {
98+
// The warp's effect will naturally revert if the chain reverts. Thus, we only want to handle the
99+
// case if the transaction that called warp reverts (which is why we have the nil checks).
100+
if tracer.chain.pendingBlockContext != nil {
101+
tracer.chain.pendingBlockContext.Time = originalTime
102+
}
103+
if tracer.chain.pendingBlock != nil {
104+
tracer.chain.pendingBlock.Header.Time = originalTime
105+
}
93106
})
94107
return nil, nil
95108
},
96109
)
97110

98-
// Roll: Sets VM block number
111+
// Roll: Sets VM block number. Note that this _permanently_ updates the block number for the remainder of the
112+
// chain's lifecycle
99113
contract.addMethod(
100114
"roll", abi.Arguments{{Type: typeUint256}}, abi.Arguments{},
101115
func(tracer *cheatCodeTracer, inputs []any) ([]any, *cheatCodeRawReturnData) {
102-
// Maintain our changes until the transaction exits.
103-
original := new(big.Int).Set(tracer.chain.pendingBlockContext.BlockNumber)
104-
tracer.chain.pendingBlockContext.BlockNumber.Set(inputs[0].(*big.Int))
105-
tracer.CurrentCallFrame().onTopFrameExitRestoreHooks.Push(func() {
106-
tracer.chain.pendingBlockContext.BlockNumber.Set(original)
116+
// Capture the original block number
117+
originalBlockNumber := tracer.chain.pendingBlockContext.BlockNumber
118+
119+
// Retrieve the new block number
120+
newBlockNumber := inputs[0].(*big.Int)
121+
122+
// Set the block number for the pending block context and the pending block
123+
// The block context will reflect the block number change in the current EVM context
124+
// And the pending block number will allow for the number to reflect
125+
// permanently for the remainder of the chain.
126+
tracer.chain.pendingBlockContext.BlockNumber.Set(newBlockNumber)
127+
tracer.chain.pendingBlock.Header.Number.Set(newBlockNumber)
128+
129+
// If the transaction reverts, we will restore the original block number
130+
tracer.CurrentCallFrame().onChainRevertRestoreHooks.Push(func() {
131+
// The roll's effect will naturally revert if the chain reverts. Thus, we only want to handle the
132+
// case if the transaction that called roll reverts (which is why we have the nil checks).
133+
if tracer.chain.pendingBlockContext != nil {
134+
tracer.chain.pendingBlockContext.BlockNumber.Set(originalBlockNumber)
135+
}
136+
if tracer.chain.pendingBlock != nil {
137+
tracer.chain.pendingBlock.Header.Number.Set(originalBlockNumber)
138+
}
107139
})
140+
108141
return nil, nil
109142
},
110143
)
111144

112-
// Fee: Update base fee
145+
// Fee: Update the base bee. Note that this _permanently_ updates the base fee for the remainder of the
146+
// chain's lifecycle
113147
contract.addMethod(
114148
"fee", abi.Arguments{{Type: typeUint256}}, abi.Arguments{},
115149
func(tracer *cheatCodeTracer, inputs []any) ([]any, *cheatCodeRawReturnData) {
116-
// Maintain our changes until the transaction exits.
150+
// Capture the original value
117151
original := new(big.Int).Set(tracer.chain.pendingBlockContext.BaseFee)
152+
153+
// Update the pending block context and pending block's base fee
154+
// The block context will reflect the base fee change in the current EVM context
155+
// And the pending block will allow for the base fee to reflect
156+
// permanently for the remainder of the chain.
118157
tracer.chain.pendingBlockContext.BaseFee.Set(inputs[0].(*big.Int))
119-
tracer.CurrentCallFrame().onTopFrameExitRestoreHooks.Push(func() {
120-
tracer.chain.pendingBlockContext.BaseFee.Set(original)
158+
tracer.chain.pendingBlock.Header.BaseFee.Set(inputs[0].(*big.Int))
159+
160+
// If the transaction reverts, we will restore the original base fee
161+
tracer.CurrentCallFrame().onChainRevertRestoreHooks.Push(func() {
162+
// The fee's effect will naturally revert if the chain reverts. Thus, we only want to handle the
163+
// case if the transaction that called fee reverts (which is why we have the nil checks).
164+
if tracer.chain.pendingBlockContext != nil {
165+
tracer.chain.pendingBlockContext.BaseFee.Set(original)
166+
}
167+
if tracer.chain.pendingBlock != nil {
168+
tracer.chain.pendingBlock.Header.BaseFee.Set(original)
169+
}
121170
})
122171
return nil, nil
123172
},
124173
)
125174

126175
// Difficulty: Updates difficulty
176+
// TODO: Make changes to difficulty permanent and make it revert for post-Paris EVM versions
127177
contract.addMethod(
128178
"difficulty", abi.Arguments{{Type: typeUint256}}, abi.Arguments{},
129179
func(tracer *cheatCodeTracer, inputs []any) ([]any, *cheatCodeRawReturnData) {
@@ -141,6 +191,38 @@ func getStandardCheatCodeContract(tracer *cheatCodeTracer) (*CheatCodeContract,
141191
},
142192
)
143193

194+
// TODO: Add prevrandao cheatcode
195+
196+
// Coinbase: Updates the block coinbase. Note that this _permanently_ updates the coinbase for the remainder of the
197+
// chain's lifecycle
198+
contract.addMethod(
199+
"coinbase", abi.Arguments{{Type: typeAddress}}, abi.Arguments{},
200+
func(tracer *cheatCodeTracer, inputs []any) ([]any, *cheatCodeRawReturnData) {
201+
// Capture the original coinbase
202+
original := tracer.chain.pendingBlockContext.Coinbase
203+
204+
// Update the pending block context and the pending block's coinbase
205+
// The block context will reflect the coinbase change in the current EVM context
206+
// And the pending block will allow for the coinbase change to reflect
207+
// permanently for the remainder of the chain.
208+
tracer.chain.pendingBlockContext.Coinbase = inputs[0].(common.Address)
209+
tracer.chain.pendingBlock.Header.Coinbase = inputs[0].(common.Address)
210+
211+
// If the transaction reverts, we will restore the original base fee
212+
tracer.CurrentCallFrame().onChainRevertRestoreHooks.Push(func() {
213+
// The coinbase's effect will naturally revert if the chain reverts. Thus, we only want to handle the
214+
// case if the transaction that called coinbase reverts (which is why we have the nil checks).
215+
if tracer.chain.pendingBlockContext != nil {
216+
tracer.chain.pendingBlockContext.Coinbase = original
217+
}
218+
if tracer.chain.pendingBlock != nil {
219+
tracer.chain.pendingBlock.Header.Coinbase = original
220+
}
221+
})
222+
return nil, nil
223+
},
224+
)
225+
144226
// ChainId: Sets VM chain ID
145227
contract.addMethod(
146228
"chainId", abi.Arguments{{Type: typeUint256}}, abi.Arguments{},
@@ -224,20 +306,6 @@ func getStandardCheatCodeContract(tracer *cheatCodeTracer) (*CheatCodeContract,
224306
},
225307
)
226308

227-
// Coinbase: Sets the block coinbase.
228-
contract.addMethod(
229-
"coinbase", abi.Arguments{{Type: typeAddress}}, abi.Arguments{},
230-
func(tracer *cheatCodeTracer, inputs []any) ([]any, *cheatCodeRawReturnData) {
231-
// Maintain our changes until the transaction exits.
232-
original := tracer.chain.pendingBlockContext.Coinbase
233-
tracer.chain.pendingBlockContext.Coinbase = inputs[0].(common.Address)
234-
tracer.CurrentCallFrame().onTopFrameExitRestoreHooks.Push(func() {
235-
tracer.chain.pendingBlockContext.Coinbase = original
236-
})
237-
return nil, nil
238-
},
239-
)
240-
241309
// Prank: Sets the msg.sender within the next EVM call scope created by the caller.
242310
contract.addMethod(
243311
"prank", abi.Arguments{{Type: typeAddress}}, abi.Arguments{},

0 commit comments

Comments
 (0)