script.go 29.8 KB
Newer Older
1 2 3
package script

import (
4
	"bytes"
5
	"encoding/binary"
6
	"encoding/json"
7
	"errors"
8 9 10
	"fmt"
	"math/big"

11
	"github.com/ethereum-optimism/optimism/op-chain-ops/script/addresses"
12 13 14 15 16 17 18 19 20 21 22 23 24 25
	"github.com/holiman/uint256"

	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/rawdb"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/tracing"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/log"
	"github.com/ethereum/go-ethereum/params"
26 27
	"github.com/ethereum/go-ethereum/triedb"
	"github.com/ethereum/go-ethereum/triedb/hashdb"
28 29

	"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
30
	"github.com/ethereum-optimism/optimism/op-chain-ops/script/forking"
31
	"github.com/ethereum-optimism/optimism/op-chain-ops/srcmap"
32 33
)

34 35 36
// jumpHistory is the amount of successful jumps to track for debugging.
const jumpHistory = 5

37 38
// CallFrame encodes the scope context of the current call
type CallFrame struct {
39 40 41 42 43
	Depth int

	LastOp vm.OpCode
	LastPC uint64

44 45 46
	// To reconstruct a create2 later, e.g. on broadcast
	LastCreate2Salt [32]byte

47 48 49
	// Reverts often happen in generated code.
	// We want to fallback to logging the source-map position of
	// the non-generated code, i.e. the origin of the last successful jump.
50 51 52
	// And beyond that, a short history of the latest jumps is useful for debugging.
	// This is a list of program-counters at the time of the jump (i.e. before raching JUMPDEST).
	LastJumps []uint64
53 54

	Ctx *vm.ScopeContext
55 56 57 58 59

	// Prank overrides the msg.sender, and optionally the origin.
	// Forge script does not support nested pranks on the same call-depth.
	// Pranks can also be broadcasting.
	Prank *Prank
60 61 62 63 64 65 66 67 68

	// GasUsed keeps track of the amount of gas used by this call frame.
	// This is useful for broadcasts, which sometimes cannot correctly
	// estimate gas when sending transactions in parallel.
	GasUsed uint64

	// CallerNonce keeps track of the nonce of the caller who entered the callframe
	// (nonce of pranked caller, if pranked).
	CallerNonce uint64
69 70 71 72 73 74 75 76
}

// Host is an EVM executor that runs Forge scripts.
type Host struct {
	log      log.Logger
	af       *foundry.ArtifactsFS
	chainCfg *params.ChainConfig
	env      *vm.EVM
77 78 79 80 81 82

	state     *forking.ForkableState
	baseState *state.StateDB

	// only known contracts may utilize cheatcodes and logging
	allowedCheatcodes map[common.Address]struct{}
83 84 85 86

	cheatcodes *Precompile[*CheatCodesPrecompile]
	console    *Precompile[*ConsolePrecompile]

87 88
	precompiles map[common.Address]vm.PrecompiledContract

89
	callStack []*CallFrame
90 91 92 93 94 95 96 97

	// serializerStates are in-progress JSON payloads by name,
	// for the serializeX family of cheat codes, see:
	// https://book.getfoundry.sh/cheatcodes/serialize-json
	serializerStates map[string]json.RawMessage

	envVars map[string]string
	labels  map[common.Address]string
98 99 100 101 102 103

	// srcFS enables src-map loading;
	// this is a bit more expensive, but provides useful debug information.
	// src-maps are disabled if this is nil.
	srcFS   *foundry.SourceMapFS
	srcMaps map[common.Address]*srcmap.SourceMap
104 105

	onLabel []func(name string, addr common.Address)
106 107

	hooks *Hooks
108 109 110 111 112

	// isolateBroadcasts will flush the journal changes,
	// and prepare the ephemeral tx context again,
	// to make gas accounting of a broadcast sub-call more accurate.
	isolateBroadcasts bool
113 114 115 116

	// useCreate2Deployer uses the Create2Deployer for broadcasted
	// create2 calls.
	useCreate2Deployer bool
117 118 119 120 121 122 123 124
}

type HostOption func(h *Host)

type BroadcastHook func(broadcast Broadcast)

type Hooks struct {
	OnBroadcast BroadcastHook
125
	OnFork      ForkHook
126 127 128 129 130 131
}

func WithBroadcastHook(hook BroadcastHook) HostOption {
	return func(h *Host) {
		h.hooks.OnBroadcast = hook
	}
132 133
}

134 135 136 137 138 139
func WithForkHook(hook ForkHook) HostOption {
	return func(h *Host) {
		h.hooks.OnFork = hook
	}
}

140 141 142 143 144 145 146 147 148 149 150
// WithIsolatedBroadcasts makes each broadcast clean the context,
// by flushing the dirty storage changes, and preparing the ephemeral state again.
// This then produces more accurate gas estimation for broadcast calls.
// This is not compatible with state-snapshots: upon cleaning,
// it is assumed that the state has to never revert back, similar to the state-dump guarantees.
func WithIsolatedBroadcasts() HostOption {
	return func(h *Host) {
		h.isolateBroadcasts = true
	}
}

151 152 153 154 155 156 157 158 159 160
// WithCreate2Deployer proxies each CREATE2 call through the CREATE2 deployer
// contract located at 0x4e59b44847b379578588920cA78FbF26c0B4956C. This is the Arachnid
// Create2Deployer contract Forge uses. See https://github.com/Arachnid/deterministic-deployment-proxy
// for the implementation.
func WithCreate2Deployer() HostOption {
	return func(h *Host) {
		h.useCreate2Deployer = true
	}
}

161 162
// NewHost creates a Host that can load contracts from the given Artifacts FS,
// and with an EVM initialized to the given executionContext.
163
// Optionally src-map loading may be enabled, by providing a non-nil srcFS to read sources from.
164 165 166 167 168 169 170
func NewHost(
	logger log.Logger,
	fs *foundry.ArtifactsFS,
	srcFS *foundry.SourceMapFS,
	executionContext Context,
	options ...HostOption,
) *Host {
171
	h := &Host{
172 173 174 175 176
		log:              logger,
		af:               fs,
		serializerStates: make(map[string]json.RawMessage),
		envVars:          make(map[string]string),
		labels:           make(map[common.Address]string),
177 178 179
		precompiles:      make(map[common.Address]vm.PrecompiledContract),
		srcFS:            srcFS,
		srcMaps:          make(map[common.Address]*srcmap.SourceMap),
180 181
		hooks: &Hooks{
			OnBroadcast: func(broadcast Broadcast) {},
182 183 184
			OnFork: func(opts *ForkConfig) (forking.ForkSource, error) {
				return nil, errors.New("no forking configured")
			},
185
		},
186
		allowedCheatcodes: make(map[common.Address]struct{}),
187 188 189 190
	}

	for _, opt := range options {
		opt(h)
191 192 193 194
	}

	// Init a default chain config, with all the mainnet L1 forks activated
	h.chainCfg = &params.ChainConfig{
195
		ChainID: executionContext.ChainID,
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
		// Ethereum forks in proof-of-work era.
		HomesteadBlock:      big.NewInt(0),
		EIP150Block:         big.NewInt(0),
		EIP155Block:         big.NewInt(0),
		EIP158Block:         big.NewInt(0),
		ByzantiumBlock:      big.NewInt(0),
		ConstantinopleBlock: big.NewInt(0),
		PetersburgBlock:     big.NewInt(0),
		IstanbulBlock:       big.NewInt(0),
		MuirGlacierBlock:    big.NewInt(0),
		BerlinBlock:         big.NewInt(0),
		LondonBlock:         big.NewInt(0),
		ArrowGlacierBlock:   big.NewInt(0),
		GrayGlacierBlock:    big.NewInt(0),
		MergeNetsplitBlock:  big.NewInt(0),
		// Ethereum forks in proof-of-stake era.
		TerminalTotalDifficulty:       big.NewInt(1),
		TerminalTotalDifficultyPassed: true,
		ShanghaiTime:                  new(uint64),
		CancunTime:                    new(uint64),
		PragueTime:                    nil,
		VerkleTime:                    nil,
		// OP-Stack forks are disabled, since we use this for L1.
		BedrockBlock: nil,
		RegolithTime: nil,
		CanyonTime:   nil,
		EcotoneTime:  nil,
		FjordTime:    nil,
		GraniteTime:  nil,
225
		HoloceneTime: nil,
226 227 228 229 230
		InteropTime:  nil,
		Optimism:     nil,
	}

	// Create an in-memory database, to host our temporary script state changes
231 232
	rawDB := rawdb.NewMemoryDatabase()
	stateDB := state.NewDatabase(triedb.NewDatabase(rawDB, &triedb.Config{
233 234 235 236
		Preimages: true, // To be able to iterate the state we need the Preimages
		IsVerkle:  false,
		HashDB:    hashdb.Defaults,
		PathDB:    nil,
237
	}), nil)
238
	var err error
239
	h.baseState, err = state.New(types.EmptyRootHash, stateDB)
240 241 242
	if err != nil {
		panic(fmt.Errorf("failed to create memory state db: %w", err))
	}
243
	h.state = forking.NewForkableState(h.baseState)
244 245 246 247 248 249 250 251 252 253 254

	// Initialize a block-context for the EVM to access environment variables.
	// The block context (after embedding inside of the EVM environment) may be mutated later.
	blockContext := vm.BlockContext{
		CanTransfer: core.CanTransfer,
		Transfer:    core.Transfer,
		GetHash: func(n uint64) (out common.Hash) {
			binary.BigEndian.PutUint64(out[:8], n)
			return crypto.Keccak256Hash(out[:])
		},
		L1CostFunc:  nil,
255 256 257 258
		Coinbase:    executionContext.FeeRecipient,
		GasLimit:    executionContext.GasLimit,
		BlockNumber: new(big.Int).SetUint64(executionContext.BlockNum),
		Time:        executionContext.Timestamp,
259 260 261
		Difficulty:  nil, // not used anymore post-merge
		BaseFee:     big.NewInt(0),
		BlobBaseFee: big.NewInt(0),
262
		Random:      &executionContext.PrevRandao,
263 264 265 266 267
	}

	// Initialize a transaction-context for the EVM to access environment variables.
	// The transaction context (after embedding inside of the EVM environment) may be mutated later.
	txContext := vm.TxContext{
268
		Origin:       executionContext.Origin,
269
		GasPrice:     big.NewInt(0),
270
		BlobHashes:   executionContext.BlobHashes,
271
		BlobFeeCap:   big.NewInt(0),
272
		AccessEvents: state.NewAccessEvents(h.baseState.PointCache()),
273 274 275 276
	}

	// Hook up the Host to capture the EVM environment changes
	trHooks := &tracing.Hooks{
277
		OnEnter:         h.onEnter,
278 279 280 281 282 283 284 285 286 287 288 289
		OnExit:          h.onExit,
		OnOpcode:        h.onOpcode,
		OnFault:         h.onFault,
		OnStorageChange: h.onStorageChange,
		OnLog:           h.onLog,
	}

	// Configure the EVM without basefee (because scripting), our trace hooks, and runtime precompile overrides.
	vmCfg := vm.Config{
		NoBaseFee:           true,
		Tracer:              trHooks,
		PrecompileOverrides: h.getPrecompile,
290
		CallerOverride:      h.handleCaller,
291 292 293 294 295 296 297
	}

	h.env = vm.NewEVM(blockContext, txContext, h.state, h.chainCfg, vmCfg)

	return h
}

298 299 300 301 302 303 304 305 306 307 308 309
// AllowCheatcodes allows the given address to utilize the cheatcodes and logging precompiles
func (h *Host) AllowCheatcodes(addr common.Address) {
	h.log.Debug("Allowing cheatcodes", "address", addr, "label", h.labels[addr])
	h.allowedCheatcodes[addr] = struct{}{}
}

// AllowedCheatcodes returns whether the given address is allowed to use cheatcodes
func (h *Host) AllowedCheatcodes(addr common.Address) bool {
	_, ok := h.allowedCheatcodes[addr]
	return ok
}

310 311 312 313 314 315 316 317 318 319
// EnableCheats enables the Forge/HVM cheat-codes precompile and the Hardhat-style console2 precompile.
func (h *Host) EnableCheats() error {
	vmPrecompile, err := NewPrecompile[*CheatCodesPrecompile](&CheatCodesPrecompile{h: h})
	if err != nil {
		return fmt.Errorf("failed to init VM cheatcodes precompile: %w", err)
	}
	h.cheatcodes = vmPrecompile
	// Solidity does EXTCODESIZE checks on functions without return-data.
	// We need to insert some placeholder code to prevent it from aborting calls.
	// Emulates Forge script: https://github.com/foundry-rs/foundry/blob/224fe9cbf76084c176dabf7d3b2edab5df1ab818/crates/evm/evm/src/executors/mod.rs#L108
320 321
	h.state.SetCode(addresses.VMAddr, []byte{0x00})
	h.precompiles[addresses.VMAddr] = h.cheatcodes
322 323 324 325 326 327 328 329 330

	consolePrecompile, err := NewPrecompile[*ConsolePrecompile](&ConsolePrecompile{
		logger: h.log,
		sender: h.MsgSender,
	})
	if err != nil {
		return fmt.Errorf("failed to init console precompile: %w", err)
	}
	h.console = consolePrecompile
331
	h.precompiles[addresses.ConsoleAddr] = h.console
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
	// The Console precompile does not need bytecode,
	// calls all go through a console lib, which avoids the EXTCODESIZE.
	return nil
}

// prelude is a helper function to prepare the Host for a new call/create on the EVM environment.
func (h *Host) prelude(from common.Address, to *common.Address) {
	rules := h.chainCfg.Rules(h.env.Context.BlockNumber, true, h.env.Context.Time)
	activePrecompiles := vm.ActivePrecompiles(rules)
	h.env.StateDB.Prepare(rules, from, h.env.Context.Coinbase, to, activePrecompiles, nil)
}

// Call calls a contract in the EVM. The state changes persist.
func (h *Host) Call(from common.Address, to common.Address, input []byte, gas uint64, value *uint256.Int) (returnData []byte, leftOverGas uint64, err error) {
	h.prelude(from, &to)
	return h.env.Call(vm.AccountRef(from), to, input, gas, value)
}

// LoadContract loads the bytecode of a contract, and deploys it with regular CREATE.
func (h *Host) LoadContract(artifactName, contractName string) (common.Address, error) {
	artifact, err := h.af.ReadArtifact(artifactName, contractName)
	if err != nil {
		return common.Address{}, fmt.Errorf("failed to load %s / %s: %w", artifactName, contractName, err)
	}
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
	addr, err := h.Create(h.TxOrigin(), artifact.Bytecode.Object)
	if err != nil {
		return common.Address{}, err
	}
	h.RememberArtifact(addr, artifact, contractName)
	return addr, nil
}

// RememberArtifact registers an address as originating from a particular artifact.
// This register a source-map, if the Host is configured with a source-map FS.
func (h *Host) RememberArtifact(addr common.Address, artifact *foundry.Artifact, contract string) {
	if h.srcFS == nil {
		return
	}
	code := h.state.GetCode(addr)
	if !bytes.Equal(code, artifact.DeployedBytecode.Object) {
		h.log.Warn("src map warning: state bytecode does not match artifact deployed bytecode", "addr", addr)
	}

	srcMap, err := h.srcFS.SourceMap(artifact, contract)
376
	if err != nil {
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
		h.log.Warn("failed to load srcmap", "addr", addr, "err", err)
		return
	}
	h.srcMaps[addr] = srcMap
}

// Create a contract with unlimited gas, and 0 ETH value.
// This create function helps deploy contracts quickly for scripting etc.
func (h *Host) Create(from common.Address, initCode []byte) (common.Address, error) {
	h.prelude(from, nil)
	ret, addr, _, err := h.env.Create(vm.AccountRef(from),
		initCode, DefaultFoundryGasLimit, uint256.NewInt(0))
	if err != nil {
		retStr := fmt.Sprintf("%x", ret)
		if len(retStr) > 20 {
			retStr = retStr[:20] + "..."
		}
		return common.Address{}, fmt.Errorf("failed to create contract, return: %s, err: %w", retStr, err)
395 396 397 398
	}
	return addr, nil
}

399 400 401 402 403 404 405 406 407 408
// Wipe an account: removing the code, and setting address and balance to 0. This makes the account "empty".
// Note that storage is not removed.
func (h *Host) Wipe(addr common.Address) {
	if h.state.GetCodeSize(addr) > 0 {
		h.state.SetCode(addr, nil)
	}
	h.state.SetNonce(addr, 0)
	h.state.SetBalance(addr, uint256.NewInt(0), tracing.BalanceChangeUnspecified)
}

409 410 411 412 413 414 415 416 417 418
// SetNonce sets an account's nonce in state.
func (h *Host) SetNonce(addr common.Address, nonce uint64) {
	h.state.SetNonce(addr, nonce)
}

// GetNonce returs an account's nonce from state.
func (h *Host) GetNonce(addr common.Address) uint64 {
	return h.state.GetNonce(addr)
}

419 420 421 422 423
// ImportState imports a set of foundry.ForgeAllocs into the
// host's state database. It does not erase existing state
// when importing.
func (h *Host) ImportState(allocs *foundry.ForgeAllocs) {
	for addr, alloc := range allocs.Accounts {
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
		h.ImportAccount(addr, alloc)
	}
}

func (h *Host) ImportAccount(addr common.Address, account types.Account) {
	var balance *uint256.Int
	if account.Balance == nil {
		balance = uint256.NewInt(0)
	} else {
		balance = uint256.MustFromBig(account.Balance)
	}
	h.state.SetBalance(addr, balance, tracing.BalanceChangeUnspecified)
	h.state.SetNonce(addr, account.Nonce)
	h.state.SetCode(addr, account.Code)
	for key, value := range account.Storage {
		h.state.SetState(addr, key, value)
440 441 442
	}
}

443 444 445 446
func (h *Host) SetStorage(addr common.Address, key common.Hash, value common.Hash) {
	h.state.SetState(addr, key, value)
}

447 448
// getPrecompile overrides any accounts during runtime, to insert special precompiles, if activated.
func (h *Host) getPrecompile(rules params.Rules, original vm.PrecompiledContract, addr common.Address) vm.PrecompiledContract {
449
	if p, ok := h.precompiles[addr]; ok {
450
		return &AccessControlledPrecompile{h: h, inner: p}
451 452 453 454 455 456 457 458 459 460 461 462
	}
	return original
}

// SetPrecompile inserts a precompile at the given address.
// If the precompile is nil, it removes the precompile override from that address, and wipes the account.
func (h *Host) SetPrecompile(addr common.Address, precompile vm.PrecompiledContract) {
	if precompile == nil {
		h.log.Debug("removing precompile", "addr", addr)
		delete(h.precompiles, addr)
		h.Wipe(addr)
		return
463
	}
464 465 466 467 468 469 470 471 472 473
	h.log.Debug("adding precompile", "addr", addr)
	h.precompiles[addr] = precompile
	// insert non-empty placeholder bytecode, so EXTCODESIZE checks pass
	h.state.SetCode(addr, []byte{0})
}

// HasPrecompileOverride inspects if there exists an active precompile-override at the given address.
func (h *Host) HasPrecompileOverride(addr common.Address) bool {
	_, ok := h.precompiles[addr]
	return ok
474
}
475 476 477 478 479

// GetCode returns the code of an account from the state.
func (h *Host) GetCode(addr common.Address) []byte {
	return h.state.GetCode(addr)
}
480

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
// onEnter is a trace-hook, which we use to apply changes to the state-DB, to simulate isolated broadcast calls,
// for better gas estimation of the exact broadcast call execution.
func (h *Host) onEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
	if len(h.callStack) == 0 {
		return
	}
	parentCallFrame := h.callStack[len(h.callStack)-1]
	if parentCallFrame.Prank == nil {
		return
	}
	// sanity check our callframe is set up correctly
	if parentCallFrame.LastOp != vm.OpCode(typ) {
		panic(fmt.Errorf("parent call-frame has invalid last Op: %d", typ))
	}
	if !parentCallFrame.Prank.Broadcast {
		return
	}
498
	if to == addresses.VMAddr || to == addresses.ConsoleAddr { // no broadcasts to the cheatcode or console address
499 500 501
		return
	}

502 503
	// Bump nonce value, such that a broadcast Call or CREATE2 appears like a tx
	if parentCallFrame.LastOp == vm.CALL || parentCallFrame.LastOp == vm.CREATE2 {
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
		sender := parentCallFrame.Ctx.Address()
		if parentCallFrame.Prank.Sender != nil {
			sender = *parentCallFrame.Prank.Sender
		}
		h.state.SetNonce(sender, h.state.GetNonce(sender)+1)
	}

	if h.isolateBroadcasts {
		var dest *common.Address
		switch parentCallFrame.LastOp {
		case vm.CREATE, vm.CREATE2:
			dest = nil // no destination address to warm up
		case vm.CALL:
			dest = &to
		default:
			return
		}
		h.state.Finalise(true)
		// the prank msg.sender, if any, has already been applied to 'from' before onEnter
		h.prelude(from, dest)
	}
}

527 528 529 530 531
// onExit is a trace-hook, which we use to maintain an accurate view of functions, and log any revert warnings.
func (h *Host) onExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
	// Note: onExit runs also when going deeper, exiting the context into a nested context.
	addr := h.SelfAddress()
	if reverted {
532
		h.LogCallStack()
533
		if msg, revertInspectErr := abi.UnpackRevert(output); revertInspectErr == nil {
534
			h.log.Warn("Revert", "addr", addr, "err", err, "revertMsg", msg, "depth", depth)
535
		} else {
536
			h.log.Warn("Revert", "addr", addr, "err", err, "revertData", hexutil.Bytes(output), "depth", depth)
537 538
		}
	}
539 540

	h.callStack[len(h.callStack)-1].GasUsed += gasUsed
541
	h.unwindCallstack(depth)
542 543 544 545
}

// onFault is a trace-hook, catches things more generic than regular EVM reverts.
func (h *Host) onFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
546
	h.log.Warn("Fault", "addr", scope.Address(), "err", err, "depth", depth)
547 548 549 550 551 552
}

// unwindCallstack is a helper to remove call-stack entries.
func (h *Host) unwindCallstack(depth int) {
	// pop the callstack until the depth matches
	for len(h.callStack) > 0 && h.callStack[len(h.callStack)-1].Depth > depth {
553 554 555 556
		// unset the prank, if the parent call-frame had set up a prank that does not repeat
		if len(h.callStack) > 1 {
			parentCallFrame := h.callStack[len(h.callStack)-2]
			if parentCallFrame.Prank != nil {
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
				if parentCallFrame.Prank.Broadcast {
					if parentCallFrame.LastOp == vm.DELEGATECALL {
						h.log.Warn("Cannot broadcast a delegate-call. Ignoring broadcast hook.")
					} else if parentCallFrame.LastOp == vm.STATICCALL {
						h.log.Trace("Broadcast is active, ignoring static-call.")
					} else {
						currentCallFrame := h.callStack[len(h.callStack)-1]
						bcast := NewBroadcast(parentCallFrame, currentCallFrame)
						h.log.Debug(
							"calling broadcast hook",
							"from", bcast.From,
							"to", bcast.To,
							"input", bcast.Input,
							"value", bcast.Value,
							"type", bcast.Type,
						)
						h.hooks.OnBroadcast(bcast)
					}
575 576
				}

577 578 579 580 581 582 583 584 585 586 587
				// While going back to the parent, restore the tx.origin.
				// It will later be re-applied on sub-calls if the prank persists (if Repeat == true).
				if parentCallFrame.Prank.Origin != nil {
					h.env.TxContext.Origin = parentCallFrame.Prank.PrevOrigin
				}
				if !parentCallFrame.Prank.Repeat {
					parentCallFrame.Prank = nil
				}
			}
		}
		// Now pop the call-frame
588
		h.callStack[len(h.callStack)-1] = nil // don't hold on to the underlying call-frame resources
589 590 591 592 593 594 595 596
		h.callStack = h.callStack[:len(h.callStack)-1]
	}
}

// onOpcode is a trace-hook, used to maintain a view of the call-stack, and do any per op-code overrides.
func (h *Host) onOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
	h.unwindCallstack(depth)
	scopeCtx := scope.(*vm.ScopeContext)
597 598 599 600 601 602 603
	if scopeCtx.Contract.IsDeployment {
		// If we are not yet allowed access to cheatcodes, but if the caller is,
		// and if this is a contract-creation, then we are automatically granted cheatcode access.
		if !h.AllowedCheatcodes(scopeCtx.Address()) && h.AllowedCheatcodes(scopeCtx.Caller()) {
			h.AllowCheatcodes(scopeCtx.Address())
		}
	}
604 605 606
	// Check if we are entering a new depth, add it to the call-stack if so.
	// We do this here, instead of onEnter, to capture an initialized scope.
	if len(h.callStack) == 0 || h.callStack[len(h.callStack)-1].Depth < depth {
607
		h.callStack = append(h.callStack, &CallFrame{
608 609 610 611 612
			Depth:       depth,
			LastOp:      vm.OpCode(op),
			LastPC:      pc,
			Ctx:         scopeCtx,
			CallerNonce: h.GetNonce(scopeCtx.Caller()),
613 614 615
		})
	}
	// Sanity check that top of the call-stack matches the scope context now
616
	if h.callStack[len(h.callStack)-1].Ctx != scopeCtx {
617 618
		panic("scope context changed without call-frame pop/push")
	}
619
	cf := h.callStack[len(h.callStack)-1]
620
	if vm.OpCode(op) == vm.JUMPDEST { // remember the last PC before successful jump
621 622 623 624 625
		cf.LastJumps = append(cf.LastJumps, cf.LastPC)
		if len(cf.LastJumps) > jumpHistory {
			copy(cf.LastJumps[:], cf.LastJumps[len(cf.LastJumps)-jumpHistory:])
			cf.LastJumps = cf.LastJumps[:jumpHistory]
		}
626 627 628
	}
	cf.LastOp = vm.OpCode(op)
	cf.LastPC = pc
629 630 631
	if cf.LastOp == vm.CREATE2 {
		cf.LastCreate2Salt = scopeCtx.Stack.Back(3).Bytes32()
	}
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
}

// onStorageChange is a trace-hook to capture state changes
func (h *Host) onStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash) {
	h.log.Debug("storage change", "addr", addr, "slot", slot, "prev_value", prev, "new_value", new)
	// future storage recording
}

// onLog is a trace-hook to capture log events
func (h *Host) onLog(ev *types.Log) {
	logger := h.log
	for i, topic := range ev.Topics {
		logger = logger.With(fmt.Sprintf("topic%d", i), topic)
	}
	logger.Debug("log event", "data", hexutil.Bytes(ev.Data))
	// future log recording
}

// CurrentCall returns the top of the callstack. Or zeroed if there was no call frame yet.
// If zeroed, the call-frame has a nil scope context.
652
func (h *Host) CurrentCall() *CallFrame {
653
	if len(h.callStack) == 0 {
654
		return &CallFrame{}
655
	}
656
	return h.callStack[len(h.callStack)-1]
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
}

// MsgSender returns the msg.sender of the current active EVM call-frame,
// or a zero address if there is no active call-frame.
func (h *Host) MsgSender() common.Address {
	cf := h.CurrentCall()
	if cf.Ctx == nil {
		return common.Address{}
	}
	return cf.Ctx.Caller()
}

// SelfAddress returns the current executing address of the current active EVM call-frame,
// or a zero address if there is no active call-frame.
func (h *Host) SelfAddress() common.Address {
	cf := h.CurrentCall()
	if cf.Ctx == nil {
		return common.Address{}
	}
	return cf.Ctx.Address()
}
678

679 680 681
func (h *Host) GetEnvVar(key string) (value string, ok bool) {
	value, ok = h.envVars[key]
	return
682 683
}

684 685 686 687 688 689 690 691 692 693 694
func (h *Host) SetEnvVar(key string, value string) {
	h.envVars[key] = value
}

// StateDump turns the current EVM state into a foundry-allocs dump
// (wrapping a geth Account allocs type). This is used to export the state.
// Note that upon dumping, the state-DB is committed and flushed.
// This affects any remaining self-destructs, as all accounts are flushed to persistent state.
// After flushing the EVM state also cannot revert to a previous snapshot state:
// the state should not be dumped within contract-execution that needs to revert.
func (h *Host) StateDump() (*foundry.ForgeAllocs, error) {
695 696 697 698
	if id, ok := h.state.ActiveFork(); ok {
		return nil, fmt.Errorf("cannot state-dump while fork %s is active", id)
	}
	baseState := h.baseState
699 700
	// We have to commit the existing state to the trie,
	// for all the state-changes to be captured by the trie iterator.
701
	root, err := baseState.Commit(h.env.Context.BlockNumber.Uint64(), true)
702 703
	if err != nil {
		return nil, fmt.Errorf("failed to commit state: %w", err)
704
	}
705
	// We need a state object around the state DB
706
	st, err := state.New(root, baseState.Database())
707 708
	if err != nil {
		return nil, fmt.Errorf("failed to create state object for state-dumping: %w", err)
709
	}
710
	// After Commit we cannot reuse the old State, so we update the host to use the new one
711 712
	h.baseState = st
	h.state.SubstituteBaseState(st)
713

714 715 716 717
	// We use the new state object for state-dumping & future state-access, wrapped around
	// the just committed trie that has all changes in it.
	// I.e. the trie is committed and ready to provide all data,
	// and the state is new and iterable, prepared specifically for FromState(state).
718 719 720 721
	var allocs foundry.ForgeAllocs
	allocs.FromState(st)

	// Sanity check we have no lingering scripts.
722 723
	for i := uint64(0); i <= allocs.Accounts[addresses.ScriptDeployer].Nonce; i++ {
		scriptAddr := crypto.CreateAddress(addresses.ScriptDeployer, i)
724 725
		h.log.Info("removing script from state-dump", "addr", scriptAddr, "label", h.labels[scriptAddr])
		delete(allocs.Accounts, scriptAddr)
726
	}
727

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
	// Clean out empty storage slots in the dump - this is necessary for compatibility
	// with the superchain registry.
	for _, account := range allocs.Accounts {
		toDelete := make([]common.Hash, 0)

		for slot, value := range account.Storage {
			if value == (common.Hash{}) {
				toDelete = append(toDelete, slot)
			}
		}

		for _, slot := range toDelete {
			delete(account.Storage, slot)
		}
	}

744
	// Remove the script deployer from the output
745 746
	delete(allocs.Accounts, addresses.ScriptDeployer)
	delete(allocs.Accounts, addresses.ForgeDeployer)
747 748 749

	// The cheatcodes VM has a placeholder bytecode,
	// because solidity checks if the code exists prior to regular EVM-calls to it.
750
	delete(allocs.Accounts, addresses.VMAddr)
751 752 753 754

	// Precompile overrides come with temporary state account placeholders. Ignore those.
	for addr := range h.precompiles {
		delete(allocs.Accounts, addr)
755
	}
756 757

	return &allocs, nil
758 759
}

760 761
func (h *Host) SetTxOrigin(addr common.Address) {
	h.env.TxContext.Origin = addr
762 763
}

764 765 766
func (h *Host) TxOrigin() common.Address {
	return h.env.TxContext.Origin
}
767

768 769 770 771 772 773 774 775
// ScriptBackendFn is a convenience method for scripts to attach to the Host.
// It return a function pre-configured with the given destination-address,
// to call the destination script.
func (h *Host) ScriptBackendFn(to common.Address) CallBackendFn {
	return func(data []byte) ([]byte, error) {
		ret, _, err := h.Call(h.env.TxContext.Origin, to, data, DefaultFoundryGasLimit, uint256.NewInt(0))
		return ret, err
	}
776 777
}

778 779 780 781 782
// EnforceMaxCodeSize configures the EVM to enforce (if true), or not enforce (if false),
// the maximum contract bytecode size.
func (h *Host) EnforceMaxCodeSize(v bool) {
	h.env.Config.NoMaxCodeSize = !v
}
783

784 785 786 787 788
// LogCallStack is a convenience method for debugging,
// to log details of each call-frame (from bottom to top) to the logger.
func (h *Host) LogCallStack() {
	for _, cf := range h.callStack {
		callsite := ""
789 790 791 792 793
		srcMap, ok := h.srcMaps[cf.Ctx.Address()]
		if !ok && cf.Ctx.Contract.CodeAddr != nil { // if delegate-call, we might know the implementation code.
			srcMap, ok = h.srcMaps[*cf.Ctx.Contract.CodeAddr]
		}
		if ok {
794
			callsite = srcMap.FormattedInfo(cf.LastPC)
795 796
			if callsite == "unknown:0:0" && len(cf.LastJumps) > 0 {
				callsite = srcMap.FormattedInfo(cf.LastJumps[len(cf.LastJumps)-1])
797 798 799 800 801 802 803
			}
		}
		input := cf.Ctx.CallInput()
		byte4 := ""
		if len(input) >= 4 {
			byte4 = fmt.Sprintf("0x%x", input[:4])
		}
804
		h.log.Debug("callframe input", "depth", cf.Depth, "input", hexutil.Bytes(input), "pc", cf.LastPC, "op", cf.LastOp)
805 806
		h.log.Warn("callframe", "depth", cf.Depth, "byte4", byte4,
			"addr", cf.Ctx.Address(), "callsite", callsite, "label", h.labels[cf.Ctx.Address()])
807 808 809 810 811
		if srcMap != nil {
			for _, jmpPC := range cf.LastJumps {
				h.log.Debug("recent jump", "depth", cf.Depth, "callsite", srcMap.FormattedInfo(jmpPC), "pc", jmpPC)
			}
		}
812
	}
813 814
}

815 816 817 818
// Label an address with a name, like the foundry vm.label cheatcode.
func (h *Host) Label(addr common.Address, label string) {
	h.log.Debug("labeling", "addr", addr, "label", label)
	h.labels[addr] = label
819 820 821 822 823 824 825 826

	for _, fn := range h.onLabel {
		fn(label, addr)
	}
}

// NewScriptAddress creates a new address for the ScriptDeployer account, and bumps the nonce.
func (h *Host) NewScriptAddress() common.Address {
827
	deployer := addresses.ScriptDeployer
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
	deployNonce := h.state.GetNonce(deployer)
	// compute address of script contract to be deployed
	addr := crypto.CreateAddress(deployer, deployNonce)
	h.state.SetNonce(deployer, deployNonce+1)
	return addr
}

func (h *Host) ChainID() *big.Int {
	return new(big.Int).Set(h.chainCfg.ChainID)
}

func (h *Host) Artifacts() *foundry.ArtifactsFS {
	return h.af
}

// RememberOnLabel links the contract source-code of srcFile upon a given label
func (h *Host) RememberOnLabel(label, srcFile, contract string) error {
	artifact, err := h.af.ReadArtifact(srcFile, contract)
	if err != nil {
		return fmt.Errorf("failed to read artifact %s (contract %s) for label %q", srcFile, contract, label)
	}
	h.onLabel = append(h.onLabel, func(v string, addr common.Address) {
		if label == v {
			h.RememberArtifact(addr, artifact, contract)
		}
	})
	return nil
855
}
856 857 858 859 860 861 862 863 864 865 866 867

func (h *Host) CreateSelectFork(opts ...ForkOption) (*big.Int, error) {
	src, err := h.onFork(opts...)
	if err != nil {
		return nil, fmt.Errorf("failed to setup fork source: %w", err)
	}
	id, err := h.state.CreateSelectFork(src)
	if err != nil {
		return nil, fmt.Errorf("failed to create-select fork: %w", err)
	}
	return id.U256().ToBig(), nil
}